我正在使用jboss'Resteasy作为我们的JAX-RS提供商。我们需要读取用于身份验证目的的servlet请求主体的要求,问题是一旦在请求中读取了输入流,就无法再读取它,因此@formparam不起作用,除非我可以以某种方式"将内容放回"。我尝试了以下两个选项:
-
使用Resteasy的预处理访问者,我能够阅读身体,但是没有办法重置InputStream或添加包装器类型。该文档对此没有任何提及。根据JBOSS的"问题跟踪器",目前不可能。
-
使用Servlet Filter 包装器类型Apporach(请参见此处),我能够在
@javax.ws.rs.core.Context HttpServletRequest request
中获取请求主体,但是所有@formparam仍然返回null。
这是预处理互动器的片段:
@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());
}
如果任何人仍然对答案感兴趣,这就是我解决的方式:
创建一个扩展httpservletrequestwrapper的自定义类型。确保您覆盖
- getInputstream()
- getReader()
- getParameter()
- getParameTermap()
- getParameternames()
- getParameTervalues()
这是因为当Resteasy尝试使用@Form,@formparam和@queryparam等绑定时,它将调用Resteasy类上的GetParameter()方法,然后将其委派给基础请求,就我而言,就Apache的土狼而言,servlet请求。因此,仅用的GetInputStream()和getReader()是不够的,您必须确保GetParameter()也使用新的输入流。
如果要存储身体以备后用,则必须通过解析查询字符串和编码形式的主体来构造参数映射。实施非常直接,但它具有自身的风险。我建议阅读土狼的实现相同方法。
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);
}
}