我正在使用JBoss的Resteasy作为我们的JAX-RS提供商。我们需要读取servlet请求体以进行身份验证,问题是一旦在请求中读取了InputStream,就无法再次读取它,因此@FormParam将无法工作,除非我能以某种方式“放回内容”。我尝试了以下两个选项:
使用Resteasy的PreProcessInterceptor,我能够读取正文,但无法重置InputStream或添加包装类型。 documentation没有提及任何相关内容。根据JBoss'issue tracker,目前不可能。
使用Servlet过滤器+ Wrapper类型apporach(see example here),我能够在@javax.ws.rs.core.Context HttpServletRequest request
中获取请求正文,但所有@FormParam仍然返回null。
这是PreProcessorInterceptor的片段:
@Provider
@ServerInterceptor
public class SomePreprocessor implements PreProcessInterceptor {
public ServerResponse preProcess(HttpRequest request, ResourceMethod method)
throws Failure, WebApplicationException {
try{
StringWriter writer = new StringWriter();
IOUtils.copy(request.getInputStream(), writer, "UTF-8");
System.out.println("Request Body: "+writer.toString());
// What can I do to reset the request body?
}
catch(Exception ex){
ex.printStackTrace();
}
return null;
}
}
以下是其余方法的片段:
@POST
@Path("/something")
@Produces("application/xml")
public Response doSomething(
@FormParam("name") String name,
@javax.ws.rs.core.Context HttpServletRequest request) {
// name is always null
System.out.println(name);
// prints nothing in approach 1, returns the body in approach 2
java.io.StringWriter writer = new java.io.StringWriter();
org.apache.commons.io.IOUtils.copy(request.getReader(), writer);
System.out.println(writer.toString());
}
答案 0 :(得分:3)
如果有人仍然对答案感兴趣,请按照以下方式解决问题:
创建一个扩展HttpServletRequestWrapper的自定义类型。请务必覆盖
这是因为当Resteasy尝试使用@ Event,@ ActionParam和@QueryParam等进行绑定时,它会调用Resteasy类上的getParameter()方法,然后将其委托给底层请求,在我的例子中,Apache&# 39; s Coyote Servlet请求。因此仅仅重写getInputStream()和getReader()是不够的,你必须确保getParameter()也使用新的输入流。
如果要存储主体供以后使用,则必须通过解析查询字符串和url编码的表单主体来自己构建param映射。它很容易实施,但它有自己的风险。我建议阅读Coyote对相同方法的实现。
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URLDecoder;
import java.util.Collections;
import java.util.Enumeration;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.ws.rs.core.MediaType;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
/**
* Wrapper class that supports repeated read of the request body and parameters.
*/
public class CustomHttpServletRequest extends HttpServletRequestWrapper {
private static final Logger logger = Logger.getLogger(CustomHttpServletRequest.class);
// A typical url encoded form is "key1=value&key2=some%20value"
public final static Pattern urlStrPattern = Pattern.compile("([^=&]+)=([^&]*)[&]?");
// Cached request body
protected ByteArrayOutputStream cachedBytes;
protected String encoding;
protected String requestBody;
// Cached form parameters
protected Map<String, String[]> paramMap = new LinkedHashMap<String, String[]>();
// Cached header names, including extra headers we injected.
protected Enumeration<?> headerNames = null;
/**
*
* @param request
*/
public CustomHttpServletRequest(HttpServletRequest request) {
super(request);
// Read the body and construct parameters
try{
encoding = (request.getCharacterEncoding()==null)?"UTF-8":request.getCharacterEncoding();
// Parameters in query strings must be added to paramMap
String queryString = request.getQueryString();
logger.debug("Extracted HTTP query string: "+queryString);
if(queryString != null && !queryString.isEmpty()){
addParameters(queryString, encoding);
}
// Parse the request body if this is a form submission. Clients must set content-type to "x-www-form-urlencoded".
requestBody = IOUtils.toString(this.getInputStream(), encoding);
if (StringUtils.isEmpty(requestBody)) {requestBody = null;}
logger.debug("Extracted HTTP request body: "+requestBody);
if(request.getContentType() != null && request.getContentType().toLowerCase().contains(MediaType.APPLICATION_FORM_URLENCODED)){
addParameters(requestBody, encoding);
}
}
catch(IOException ex){
throw new RuntimeException(ex);
}
}
/**
*
* @param requestBody
* @param encoding
* @throws IOException
*/
private void addParameters(String requestBody, String encoding) throws IOException {
if(requestBody == null){
return;
}
Matcher matcher = urlStrPattern.matcher(requestBody);
while(matcher.find()){
String decodedName = URLDecoder.decode(matcher.group(1), encoding);
// If there's no value, matcher.group(2) returns an empty string instead of null
String decodedValue = URLDecoder.decode(matcher.group(2), encoding);
addParameter(decodedName, decodedValue);
logger.debug("Parsed form parameter, name = "+decodedName+", value = "+decodedValue);
}
}
/**
*
* @param name
* @param value
*/
private void addParameter(String name, String value) {
String[] pv = paramMap.get(name);
if (pv == null) {
pv = new String[]{value};
paramMap.put(name, pv);
}
else {
String[] newValue = new String[pv.length+1];
System.arraycopy(pv, 0, newValue, 0, pv.length);
newValue[pv.length] = value;
paramMap.put(name, newValue);
}
}
/*
* (non-Javadoc)
* @see javax.servlet.ServletRequestWrapper#getInputStream()
*/
@Override
public ServletInputStream getInputStream() throws IOException {
if (cachedBytes == null){
cachedBytes = new ByteArrayOutputStream();
IOUtils.copy(super.getInputStream(), cachedBytes);
}
// Return a inner class that references cachedBytes
return new CachedServletInputStream();
}
/*
* (non-Javadoc)
* @see javax.servlet.ServletRequestWrapper#getReader()
*/
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(getInputStream()));
}
/**
*
* @return
*/
public String getRequestBody() {
return requestBody;
}
/*
* (non-Javadoc)
* @see javax.servlet.ServletRequestWrapper#getParameter(java.lang.String)
*/
@Override
public String getParameter(String name) {
if(paramMap.containsKey(name)){
String[] value = (String[]) paramMap.get(name);
if(value == null){
return null;
}
else{
return value[0];
}
}
return null;
}
/*
* (non-Javadoc)
* @see javax.servlet.ServletRequestWrapper#getParameterMap()
*/
@Override
public Map<String, String[]> getParameterMap() {
return Collections.unmodifiableMap(paramMap);
}
/*
* (non-Javadoc)
* @see javax.servlet.ServletRequestWrapper#getParameterNames()
*/
@Override
public Enumeration<?> getParameterNames() {
return Collections.enumeration(paramMap.keySet());
}
/*
* (non-Javadoc)
* @see javax.servlet.ServletRequestWrapper#getParameterValues(java.lang.String)
*/
@Override
public String[] getParameterValues(String name) {
if(paramMap.containsKey(name)){
return paramMap.get(name);
}
return null;
}
/**
* Inner class that reads from stored byte array
*/
public class CachedServletInputStream extends ServletInputStream {
private ByteArrayInputStream input;
public CachedServletInputStream() {
input = new ByteArrayInputStream(cachedBytes.toByteArray());
}
@Override
public int read() throws IOException {
return input.read();
}
@Override
public int read(byte[] b) throws IOException {
return input.read(b);
}
@Override
public int read(byte[] b, int off, int len) {
return input.read(b, off, len);
}
}
}
添加一个过滤器来包装原始请求:
public class CustomFilter implements Filter {
private static final Logger logger = Logger.getLogger(CustomFilter.class);
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
if(request!=null && request instanceof HttpServletRequest){
HttpServletRequest httpRequest = (HttpServletRequest) request;
logger.debug("Wrapping HTTP request");
request = new CustomHttpServletRequest(httpRequest);
}
chain.doFilter(request, response);
}
}