解决spring设置filter过滤器结合rest风格获取post请求body参数输入流问题


           由于最近在使用spring+jersey开发要设置基于servlet的filter。当在filter中通过request.getReader或者getInputStream读取body中的json参数处理时,由于rest风格的jersey框架底层亦是基于同样原理读取post请求body中参数。因为request自身的原则:getReader或者getInputStream只能调用其中一个且只有一次可以正常获取body中内容,导致在filter中通过getReader第一次读取body中参数成功,当放行时,jersey中控制器执行时候,会出现异常:

java.lang.IllegalStateException: getReader() has already been called for this request。


############################

###           运行系统:windows8

###          JDK版本 : JDK1.7

###          框架:spring3x + jersey2.x

###          开发IDE:MyEclipse

############################

1.0 在web.xml中配置spring过滤器filter,集成自定义filter


		DelegateFilter
		org.springframework.web.filter.DelegatingFilterProxy
		
			targetBeanName
			myFilter 
		
		
			targetFilterLifecycle
			true
		
	
	
		DelegateFilter
		/*
	

2.0在spring配置文件application.xml中定义自定义filterbean。

PS:若是在filter中想使用springIOC容器中其他bean.如其他service类。那么可以在该bean中配置。
例如在filter中需要注入公用commonSerivce
public class myFilter implements Filter{
    private CommonService commonService;

//需要setter
   .......
}
要想使用commonService而不抛出空指针异常,需要在bean中配置依赖:

  
 
  

3.0 自定义filter
原始代码与运行结果如下:
 
  

public class FilterPost implements Filter{
	
	private Log log = LogFactory.getLog(FilterPost.class);

	String param = "";
	
	public void destroy() {
		// TODO Auto-generated method stub
		
	}

	public void doFilter(ServletRequest req, ServletResponse res,
			FilterChain chain) throws IOException, ServletException {
		   HttpServletRequest request = null;
	        if(req instanceof HttpServletRequest) { 
	           request = (HttpServletRequest)req;
	        }
	        
	        if("POST".equalsIgnoreCase(request.getMethod())){
	        	param = this.getBodyString(request.getReader());
	        	log.info("filter读取body中的参数>>>>>>>>>"+param);
	        	chain.doFilter(request, res);
	}
		
	}

	public void init(FilterConfig config) throws ServletException {
		
	}
        //获取request请求body中参数
	public static String getBodyString(BufferedReader br) {
		  String inputLine;
		       String str = "";
		     try {
		       while ((inputLine = br.readLine()) != null) {
		        str += inputLine;
		       }
		       br.close();
		     } catch (IOException e) {
		       System.out.println("IOException: " + e);
		     }
		     return str;
		 }

4.0基于jersey的controller中post请求处理

	@POST
	@Path("/postJson")
	@Produces(MediaType.APPLICATION_JSON)
	@Consumes(MediaType.APPLICATION_JSON)
	public Map postByJson(Map jsonParam) {

		JSONObject jo = new JSONObject(jsonParam);
		System.out.println(jsonParam);
		Map param = new HashMap();
		param.put("name", jo.getString("name").length()==0? "" : jo.getString("name"));
		param.put("age", jo.getString("age").length()==0  ? "" :jo.getString("age"));
		param.put("status", "200");
		param.put("Msg", "ok,success");
		return param;
	}


5. 抛出异常
java.lang.IllegalStateException: getReader() has already been called for this request
	at org.apache.catalina.connector.Request.getInputStream(Request.java:1085)

解决方法:

结合JODD开源框架,且在Filter中将ServletRequest替换为ServletRequestWrapper 来解决该问题。


A:添加jodd支持:

在pom.xml中添加对jodd的依赖:



org.jodd
jodd-core
3.4.8


B:结合jodd创建自定义ServletRequestWrapper

public class BodyReaderHttpServletRequestWrapper extends HttpServletRequestWrapper{

	 private final byte[] body; //用于保存读取body中数据 
     
	    public BodyReaderHttpServletRequestWrapper(HttpServletRequest request)   
	throws IOException {  
	        super(request);  
	        body = StreamUtil.readBytes(request.getReader(), "UTF-8");  
	    }  
	  
	    @Override  
	    public BufferedReader getReader() throws IOException {  
	        return new BufferedReader(new InputStreamReader(getInputStream()));  
	    }  
	  
	    @Override  
	    public ServletInputStream getInputStream() throws IOException {  
	        final ByteArrayInputStream bais = new ByteArrayInputStream(body);  
	        return new ServletInputStream() {  
	  
	            @Override  
	            public int read() throws IOException {  
	                return bais.read();  
	            }

				@Override
				public boolean isFinished() {
					// TODO Auto-generated method stub
					return false;
				}

				@Override
				public boolean isReady() {
					// TODO Auto-generated method stub
					return false;
				}

				@Override
				public void setReadListener(ReadListener arg0) {
					// TODO Auto-generated method stub
					
				}  
	        };  
	    }  
}

C:用wrapper替换request修改filter

public void doFilter(ServletRequest req, ServletResponse res,
			FilterChain chain) throws IOException, ServletException {
		  String method = "GET";
		   ServletRequest requestWrapper = null;  
	        if(req instanceof HttpServletRequest) {  
	        	method = ((HttpServletRequest) req).getMethod();
	            requestWrapper = new BodyReaderHttpServletRequestWrapper((HttpServletRequest) req);  //替换
	        }  
	        
	        if("POST".equalsIgnoreCase(method)){
	        	param = this.getBodyString(requestWrapper.getReader());
	        	log.info("filter读取body中的参数>>>>>>>>>"+param);
	        	chain.doFilter(requestWrapper, res);
	}
		


D:请求测试

解决spring设置filter过滤器结合rest风格获取post请求body参数输入流问题_第1张图片

请求成功................


参考文章:http://liwx2000.iteye.com/blog/1542431



你可能感兴趣的:(JAVA,Java_framework)