从流中读取application/json格式的参数之后,request中信息不完整

    在公司写验签的时候,遇到了一个问题:

    用post请求,application/json形式的时候,request要想获取参数,只能用流的形式,但是这样造成的问题是,我把参数从流中拿出来消耗之后,request中就没有了参数的信息,这样就达不到验签(验证请求是否正确,不会操作请求的任何信息,请求照样向后传递)的效果了。所以从网上找到了一个解决方法,并用过滤器来实现这个验签,放弃用拦截器实现的想法,就成了。在这里给大家分享出来,希望可以有帮助。

    文章写的很好,相信大家可以理解。


文章如下:

获取请求对象中的数据流,通过解析流信息来获取提交的内容;代码示例如下:

[java]  view plain  copy
  1.     /** 
  2.      * 获取请求Body 
  3.      * 
  4.      * @param request 
  5.      * @return 
  6.      */  
  7.     public static String getBodyString(ServletRequest request) {  
  8.         StringBuilder sb = new StringBuilder();  
  9.         InputStream inputStream = null;  
  10.         BufferedReader reader = null;  
  11.         try {  
  12.             inputStream = request.getInputStream();  
  13.             reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8")));  
  14.             String line = "";  
  15.             while ((line = reader.readLine()) != null) {  
  16.                 sb.append(line);  
  17.             }  
  18.         } catch (IOException e) {  
  19.             e.printStackTrace();  
  20.         } finally {  
  21.             if (inputStream != null) {  
  22.                 try {  
  23.                     inputStream.close();  
  24.                 } catch (IOException e) {  
  25.                     e.printStackTrace();  
  26.                 }  
  27.             }  
  28.             if (reader != null) {  
  29.                 try {  
  30.                     reader.close();  
  31.                 } catch (IOException e) {  
  32.                     e.printStackTrace();  
  33.                 }  
  34.             }  
  35.         }  
  36.         return sb.toString();  
  37.     }  


通过这种简单的解析方式,将http请求中的body解析成 字符串的形式;然后再对字符串的格式进行业务解析;

这种处理方式的优点是:能解析 出content-Type 为  application/x-www-form-urlencoded ,application/json , text/xml 这三种提交方式的 数据 

但是这种方式有一个很大的缺点:那就是当从请求中获取流以后,流被filter中的这个 inputStreamToString(InputStream in) 这个方法处理后就被“消耗”了,这会导致,chain.doFilter(request, res)这个链在传递 request对象的时候,里面的请求流为空,导致责任链模式下,其他下游的链无法获取请求的body,从而导致程序无法正常运行,这也使得我们的这个filter虽然可以获取请求信息,但是它会导致整个应用程序不可用,那么它也就失去了意义;

    针对缺陷:流一旦被读取,就无法向下传递整个问题,有如下解决方案;

    解决思路如下:将取出来的字符串,再次转换成流,然后把它放入到新request 对象中,在chain.doFiler方法中 传递新的request对象;要实现这种思路,需要自定义一个类

继承HttpServletRequestWrapper,然后重写public BufferedReader getReader()方法,public  ServletInputStream getInputStream()方法;(这两个方法的重写实现逻辑如下:getInputStream()方法中将body体中的字符串转换为字节流(它实质上返回的是一个ServletInputStream 对象);然后通过getReader()调用---->getInputStream()方法;),继承实现重写逻辑以后,在自定义分filter(VersionCheckFilter)中,使用自定义的HttpServletRequestWrapper(BodyReaderHttpServletRequestWrapper)将原始的HttpServletRequest对象进行再次封装;再通过BodyReaderHttpServletRequestWrapper对象去做dofilter(req,res)的req对象;

   涉及的三个类的代码如下:

   自定义的HttpServletRequestWrapper

[java]  view plain  copy
  1. import java.io.BufferedReader;  
  2. import java.io.ByteArrayInputStream;  
  3. import java.io.IOException;  
  4. import java.io.InputStreamReader;  
  5. import java.nio.charset.Charset;  
  6. import java.util.Enumeration;  
  7.   
  8. import javax.servlet.ServletInputStream;  
  9. import javax.servlet.http.HttpServletRequest;  
  10. import javax.servlet.http.HttpServletRequestWrapper;  
  11.   
  12.   
  13.   
  14. import com.yt.util.HttpHelper;  
  15.   
  16. public class BodyReaderHttpServletRequestWrapper extends  
  17.         HttpServletRequestWrapper {  
  18.       
  19.     private final byte[] body;  
  20.   
  21.     public BodyReaderHttpServletRequestWrapper(HttpServletRequest request) throws IOException {  
  22.         super(request);  
  23.         System.out.println("-------------------------------------------------");    
  24.         Enumeration e = request.getHeaderNames()   ;    
  25.          while(e.hasMoreElements()){    
  26.              String name = (String) e.nextElement();    
  27.              String value = request.getHeader(name);    
  28.              System.out.println(name+" = "+value);    
  29.                  
  30.          }    
  31.         body = HttpHelper.getBodyString(request).getBytes(Charset.forName("UTF-8"));  
  32.     }  
  33.   
  34.     @Override  
  35.     public BufferedReader getReader() throws IOException {  
  36.         return new BufferedReader(new InputStreamReader(getInputStream()));  
  37.     }  
  38.   
  39.     @Override  
  40.     public ServletInputStream getInputStream() throws IOException {  
  41.   
  42.         final ByteArrayInputStream bais = new ByteArrayInputStream(body);  
  43.   
  44.         return new ServletInputStream() {  
  45.             @Override  
  46.             public int read() throws IOException {  
  47.                 return bais.read();  
  48.             }  
  49.         };  
  50.     }  
  51.   
  52.     @Override  
  53.     public String getHeader(String name) {  
  54.         return super.getHeader(name);  
  55.     }  
  56.   
  57.     @Override  
  58.     public Enumeration<String> getHeaderNames() {  
  59.         return super.getHeaderNames();  
  60.     }  
  61.   
  62.     @Override  
  63.     public Enumeration<String> getHeaders(String name) {  
  64.         return super.getHeaders(name);  
  65.     }  
  66.       
  67. }  

 2、辅助类

HttpHelper

[java]  view plain  copy
  1. import java.io.BufferedReader;  
  2. import java.io.IOException;  
  3. import java.io.InputStream;  
  4. import java.io.InputStreamReader;  
  5. import java.nio.charset.Charset;  
  6.   
  7. import javax.servlet.ServletRequest;  
  8.   
  9. public class HttpHelper {  
  10.     /** 
  11.      * 获取请求Body 
  12.      * 
  13.      * @param request 
  14.      * @return 
  15.      */  
  16.     public static String getBodyString(ServletRequest request) {  
  17.         StringBuilder sb = new StringBuilder();  
  18.         InputStream inputStream = null;  
  19.         BufferedReader reader = null;  
  20.         try {  
  21.             inputStream = request.getInputStream();  
  22.             reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8")));  
  23.             String line = "";  
  24.             while ((line = reader.readLine()) != null) {  
  25.                 sb.append(line);  
  26.             }  
  27.         } catch (IOException e) {  
  28.             e.printStackTrace();  
  29.         } finally {  
  30.             if (inputStream != null) {  
  31.                 try {  
  32.                     inputStream.close();  
  33.                 } catch (IOException e) {  
  34.                     e.printStackTrace();  
  35.                 }  
  36.             }  
  37.             if (reader != null) {  
  38.                 try {  
  39.                     reader.close();  
  40.                 } catch (IOException e) {  
  41.                     e.printStackTrace();  
  42.                 }  
  43.             }  
  44.         }  
  45.         return sb.toString();  
  46.     }  
  47. }  

3、自定义的filter

[java]  view plain  copy
  1. import java.io.IOException;  
  2. import java.io.InputStream;  
  3. import java.io.PrintWriter;  
  4. import java.net.URL;  
  5. import java.net.URLDecoder;  
  6. import java.util.Map;  
  7.   
  8. import javax.servlet.Filter;  
  9. import javax.servlet.FilterChain;  
  10. import javax.servlet.FilterConfig;  
  11. import javax.servlet.ServletException;  
  12. import javax.servlet.ServletRequest;  
  13. import javax.servlet.ServletResponse;  
  14. import javax.servlet.http.HttpServletRequest;  
  15. import javax.servlet.http.HttpServletResponse;  
  16. import javax.xml.crypto.URIDereferencer;  
  17.   
  18. import com.yt.util.HttpHelper;  
  19. import com.yt.util.JsonHelper;  
  20.   
  21. public class VersionCheckFilter implements Filter {  
  22.   
  23.     @Override  
  24.     public void destroy() {  
  25.   
  26.     }  
  27.   
  28.     @Override  
  29.     public void doFilter(ServletRequest req, ServletResponse res,  
  30.             FilterChain chain) throws IOException, ServletException {  
  31.         HttpServletRequest hreq = (HttpServletRequest) req;  
  32.         String uri = hreq.getRequestURI();  
  33.         if(uri.contains("uploadImageServlet")){  
  34.             //图像上传的请求,不做处理  
  35.             chain.doFilter(req, res);  
  36.         }else{  
  37.             String reqMethod = hreq.getMethod();  
  38.             if("POST".equals(reqMethod)){  
  39.                   
  40.                 PrintWriter out = null;   
  41.                 HttpServletResponse response = (HttpServletResponse) res;  
  42.                 response.setCharacterEncoding("UTF-8");    
  43.                 response.setContentType("application/json; charset=utf-8");    
  44.                   
  45.               /* String requestStr = this.inputStreamToString(hreq.getInputStream()); 
  46.  
  47.                 String[] arrs = requestStr.split("&");  
  48.                 for (String strs : arrs) { 
  49.                     String[] strs2 = strs.split("="); 
  50.                     for (int i = 0; i < strs2.length; i++) { 
  51.                         if (strs2[0].equals("param")) { 
  52.                             if (strs2[1] != null &&!strs2[1].equals("")) { 
  53.                                 System.out.println("test=" + strs2[1]); 
  54.                             } 
  55.                         } 
  56.                     } 
  57.                 }*/  
  58.                   
  59.                ServletRequest requestWrapper = new BodyReaderHttpServletRequestWrapper(hreq);  
  60.                String body = HttpHelper.getBodyString(requestWrapper);  
  61.                   
  62.                 //如果是POST请求则需要获取 param 参数  
  63.                String param = URLDecoder.decode(body,"utf-8");  
  64.                 //String param = hreq.getParameter("param");  
  65.                 //json串 转换为Map  
  66.                 if(param!=null&¶m.contains("=")){  
  67.                     param = param.split("=")[1];  
  68.                 }  
  69.                 Map paramMap = JsonHelper.getGson().fromJson(param, Map.class);  
  70.                 Object obj_clientversion = paramMap.get("clientVersion");  
  71.                   
  72.                 String clientVersion = null;  
  73.                 if(obj_clientversion != null){  
  74.                     clientVersion = obj_clientversion.toString();  
  75.                     System.out.println(clientVersion);  
  76.                     /*try {   
  77.                         out = response.getWriter(); 
  78.                         Map remap = new HashMap<String, Object>(); 
  79.                         remap.put("code", 9); 
  80.                         remap.put("message", Constant.SYSTEM_ERR_DESC); 
  81.                         out.append(JsonHelper.getGson().toJson(remap));   
  82.                     } catch (IOException e) {   
  83.                         e.printStackTrace();   
  84.                     } finally {   
  85.                         if (out != null) {   
  86.                             out.close();   
  87.                         }   
  88.                     }*/  
  89.                     chain.doFilter(requestWrapper, res);      
  90.             }else{  
  91.                 chain.doFilter(requestWrapper, res);      
  92.             }  
  93.             }else{  
  94.                 //get请求直接放行  
  95.                 chain.doFilter(req, res);  
  96.             }  
  97.         }  
  98.     }  
  99.   
  100.     @Override  
  101.     public void init(FilterConfig arg0) throws ServletException {  
  102.           
  103.     }  
  104.   
  105.     /*public String inputStreamToString(InputStream in) throws IOException { 
  106.         StringBuffer out = new StringBuffer(); 
  107.         byte[] b = new byte[4096]; 
  108.         for (int n; (n = in.read(b)) != -1;) { 
  109.             out.append(new String(b, 0, n)); 
  110.         } 
  111.         return out.toString(); 
  112.     }*/  
  113. }  



这个转载,我删掉了一些东西,只取了核心的东西,完整文章请看下面链接:

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Java 修改 API 接口支持 Content-Type 为 application/json 的请求头,可以采用以下两种方式: 1. 使用 @Consumes 注解 在 API 方法上使用 @Consumes 注解,指定请求的 Content-Type 为 application/json。示例代码如下: ```java @POST @Path("/api") @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) public Response processJsonRequest(JsonObject request) { // 处理请求 } ``` 2. 使用 @Consumes 注解和 MessageBodyReader 自定义 MessageBodyReader,使其支持解析 application/json 类型的请求体。示例代码如下: ```java @Provider @Consumes(MediaType.APPLICATION_JSON) public class JsonBodyReader implements MessageBodyReader<JsonObject> { @Override public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) { return JsonObject.class.isAssignableFrom(type); } @Override public JsonObject readFrom(Class<JsonObject> type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, String> httpHeaders, InputStream entityStream) throws IOException, WebApplicationException { // 从输入流读取 JSON 数据,并转换为 JsonObject 对象 return Json.createReader(entityStream).readObject(); } } ``` 然后在 API 方法上使用 @Consumes 注解,指定请求的 Content-Type 为 application/json。示例代码如下: ```java @POST @Path("/api") @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) public Response processJsonRequest(JsonObject request) { // 处理请求 } ``` 以上两种方式,都可以让 API 接口支持 Content-Type 为 application/json 的请求头。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值