关于Filter中获取请求体body后再次读取的问题

Filter获取请求体body再次读取

工作需要,要将请求和响应做一些处理,写一个filter拦截请求,拦截request中body内容后,字符流关闭,controller取到的请求体内容为空。

从Request中获取输入流,InputStream只能被读取一次。

解决方案

给request添加一个包装类BodyWrapper,继承HttpServletRequestWrapper,

先从request中取输入流,读取流中的数据,然后重写getInputStream()和getReader()方法。

chain.doFilter(requestWrapper, response);
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.util.Enumeration;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import com.xera.fsafesso.HttpHelper;
public class BodyWrapper extends HttpServletRequestWrapper {undefined
    private final byte[] body;
    public BodyWrapper(HttpServletRequest request) throws IOException {undefined
        super(request);
        body = HttpHelper.getBodyString(request).getBytes(Charset.forName("UTF-8"));
    }
    @Override
    public BufferedReader getReader() throws IOException {undefined
        return new BufferedReader(new InputStreamReader(getInputStream()));
    }
    @Override
    public ServletInputStream getInputStream() throws IOException {undefined
        final ByteArrayInputStream bais = new ByteArrayInputStream(body);
        return new ServletInputStream(){undefined
            @Override
            public int read() throws IOException {undefined
                return bais.read();
            }
            @Override
            public boolean isFinished() {undefined
                return false;
            }
            @Override
            public boolean isReady() {undefined
                return false;
            }
            @Override
            public void setReadListener(ReadListener arg0) {undefined
            }
        };
    }
    @Override
    public String getHeader(String name) {undefined
        return super.getHeader(name);
    }
    @Override
    public Enumeration<String> getHeaderNames() {undefined
        return super.getHeaderNames();
    }
    @Override
    public Enumeration<String> getHeaders(String name) {undefined
        return super.getHeaders(name);
    }
}
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import javax.servlet.ServletRequest;
public class HttpHelper {undefined
     /**
     * 获取请求Body
     * @param request
     * @return
     */
    public static String getBodyString(ServletRequest request) {undefined
        StringBuilder sb = new StringBuilder();
        InputStream inputStream = null;
        BufferedReader reader = null;
        try {undefined
            inputStream = request.getInputStream();
            reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8")));
            String line = "";
            while ((line = reader.readLine()) != null) {undefined
                sb.append(line);
            }
        } catch (IOException e) {undefined
            e.printStackTrace();
        } finally {undefined
            if (inputStream != null) {undefined
                try {undefined
                    inputStream.close();
                } catch (IOException e) {undefined
                    e.printStackTrace();
                }
            }
            if (reader != null) {undefined
                try {undefined
                    reader.close();
                } catch (IOException e) {undefined
                    e.printStackTrace();
                }
            }
        }
        return sb.toString();
    }
}

Filter中写法如下:

HttpServletRequest httpServletRequest = (HttpServletRequest) request;
            Map<String, String> requestMap = this.getTypesafeRequestMap(httpServletRequest);
            requestWrapper = new BodyWrapper(httpServletRequest);
            String body = HttpHelper.getBodyString(requestWrapper);
            log.info("loggingFilter---请求路径 {},请求参数 {},请求体内容 {}",httpServletRequest.getRequestURL(),requestMap,body);
      chain.doFilter(requestWrapper, response);

在使用注解的方式(即@WebFilter)声明过滤器时,

需要再main函数类上添加@ServletComponentScan(basePackages = "此处写明类地址,格式为包名+类名(如com.*)

Http请求解决body流一旦被读取了就无法二次读取情况

相信大家在工作当中,经常会遇到需要处理http请求及响应body的场景,这里最大的问题应该就是body中流以但被读取就无法二次读取了。

解决request请求流只能读取一次的问题

我们编写一个过滤器,这样就可以重写body了

package com.interceptor;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class MyFilter implements Filter {
    private static String privateKey ;
    public String getPrivateKey() {
		return privateKey;
	}
	public void setPrivateKey(String key) {
		privateKey = key;
	}

	/**
     *  排除过滤路径
     */
    List<String> ignore = Arrays.asList("/xxxx");

    /**
     *   前缀排除   如 /static/goods 排除
     */
    List<String> ignorePrefix = Arrays.asList( "/css/", "/pop/", "/js/", "/static/", "/images/", "/favicon.ico");

    /**
     *  排除过滤路径
     */
    List<String> ignoreSuffix = Arrays.asList("/test");

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    	//过滤器初始化
    }
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        // 防止流读取一次后就没有了, 所以需要将流继续写出去
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        String uri = request.getServletPath();
        response.setContentType("application/json;charset=UTF-8");
        ServletRequest requestWrapper = null;
        if(canIgnore(uri)) {
        	requestWrapper = new MyFilterBodyReaderHttpServletRequestWrapper(request);
        	filterChain.doFilter(requestWrapper, response);
        	return;
        }
    	try {
    		requestWrapper = new MyFilterBodyReaderHttpServletRequestWrapper(request, response, privateKey);
		} catch (Exception e) {
			e.printStackTrace();
			return;
		}
    	filterChain.doFilter(requestWrapper, servletResponse);
    }

    @Override
    public void destroy() {
    	//过滤器销毁
    }

    private boolean canIgnore(String uri) {  

    	logger.info("过滤器  request  uri : {} ",uri);
        boolean isExcludedPage = false;
        for (String page : ignore) {
            if (uri.equals(page)) {
            	logger.info("请求路径不需要拦截,忽略该uri : {} ",uri);
                isExcludedPage = true;
                break;
            }
        }

        for (String prefix : ignorePrefix) {
            if (uri.startsWith(prefix)) {
            	logger.info("请求路径前缀[{}],不拦截该uri : {} ", prefix, uri);
                isExcludedPage = true;
                break;
            }
        }

        for (String prefix : ignoreSuffix) {
        	if (uri.endsWith(prefix)) {
        		logger.info("请求路径后缀[{}],不拦截该uri : {} ", prefix, uri);
        		isExcludedPage = true;
        		break;
        	}
        }
        return isExcludedPage;
    }
}
 
package com.interceptor;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.util.Map;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import com.alibaba.fastjson.JSON;

/**
* @Description: TODO 过滤器处理requestbody获取一次就失效
*/
public class MyFilterBodyReaderHttpServletRequestWrapper extends HttpServletRequestWrapper {
    private final byte[] body;
    /**
     * TODO 重写requestbody
     * @param request
     * @throws IOException
     */
    public MyFilterBodyReaderHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
    	super(request);
    	String sessionStream = getBodyString(request);
    	body = sessionStream.getBytes(Charset.forName("UTF-8"));
    }

    /**
     * TODO 拦截解密,校验,重写requestbody
     */
    @SuppressWarnings({ "rawtypes", "unchecked" })
	public MyFilterBodyReaderHttpServletRequestWrapper(HttpServletRequest request,HttpServletResponse response, String clientKey, Boolean ignoreCheckSign) throws Exception {
        super(request);
        String sessionStream = getBodyString(request);
        Map paramMap = (Map) JSON.parse(sessionStream);
      /**
        *自己项目中与合作方的加解密内容
        *如:String data= (String) paramMap.get("data");
        *    String json=xxxxxutil.decrypt(参数);
      */
        body = json.getBytes(Charset.forName("UTF-8"));
    }

    /**
    * TODO 获取请求Body
    * @param request
    * @return
    * @throws
    */
    public String getBodyString(final ServletRequest request) {
        StringBuilder sb = new StringBuilder();
        InputStream inputStream = null;
        BufferedReader reader = null;
        try {
            inputStream = cloneInputStream(request.getInputStream());
            reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8")));
            String line = "";
            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }
        }
        catch (IOException e) {
            e.printStackTrace();
        }
        finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                }
                catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (reader != null) {
                try {
                    reader.close();
                }
                catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return sb.toString();
    }

    /**
    * TODO 无参获取请求Body
    * @return
    * @throws
    */
    public String getBodyString() {
    	if (body == null) {
            return null;
        }
        String str = new String(body);
        return str;
    }

    /**
    * TODO 复制输入流
    * @param inputStream
    * @return
    * @throws
    */
    public InputStream cloneInputStream(ServletInputStream inputStream) {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int len;
        try {
            while ((len = inputStream.read(buffer)) > -1) {
                byteArrayOutputStream.write(buffer, 0, len);
            }
            byteArrayOutputStream.flush();
        }
        catch (IOException e) {
            e.printStackTrace();
        }
        InputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
        return byteArrayInputStream;
    }
    @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() {
                return false;
            }

            @Override
            public boolean isReady() {
                return false;
            }

            @Override
            public void setReadListener(ReadListener readListener) {
            }
        };
    }
} 

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • Spring Cloud Gateway 获取请求体(Request Body)的多种方法

    一.直接在全局拦截器中获取,伪代码如下 private String resolveBodyFromRequest(ServerHttpRequest serverHttpRequest){ Flux<DataBuffer> body = serverHttpRequest.getBody(); AtomicReference<String> bodyRef = new AtomicReference<>(); body.subscribe(buffer -> {

  • SpringCloud Gateway自定义filter获取body中的数据为空的问题

    最近在使用SpringCloud Gateway进行网关的开发,我使用的版本是:SpringBoot的2.3.4.RELEASE+SpringCloud的Hoxton.SR8,在自定义过滤器时需要获取ServerHttpRequest中body的数据,发现一直无法获取到数据,经过各种百度.谷歌,再加上自己的实践,终于找到解决方案: 1.首先创建一个全局过滤器把body中的数据缓存起来 package com.cloudpath.gateway.portal.filter; import lomb

  • Filter中获取传递参数方式(解决post请求参数问题)

    目录 Filter中获取传递参数 1. GET 传递 2. Post 传递 XyRequestWrapper 类 XySecurityFilter Filter中获取传递参数 1. GET 传递 参数可以直接通过request.getParameter获取. 2. Post 传递 产生不能过直接从request.getInputStream() 读取,必须要进行重新写.(request.getInputStream()只能够读取一次) 方式: 通过重写 HttpServletRequestWra

  • 完美解决request请求流只能读取一次的问题

    解决request请求流只能读取一次的问题 实际开发碰到的问题 解决request请求流中的数据二次或多次使用问题 实际开发碰到的问题 springboot项目中,为了防止sql注入,采用Filter拦截器对所有请求流中的json数据进行校验,请求数据没问题则继续向下执行,在后边的代码中应用到请求参数值时,发现request中的json数据为空: 除上边描述的情况,尝试过两次从request中获取json数据,第二次同样是获取不到的. 解决request请求流中的数据二次或多次使用问题 继承Ht

  • 关于Filter中获取请求体body后再次读取的问题

    Filter获取请求体body再次读取 工作需要,要将请求和响应做一些处理,写一个filter拦截请求,拦截request中body内容后,字符流关闭,controller取到的请求体内容为空. 从Request中获取输入流,InputStream只能被读取一次. 解决方案 给request添加一个包装类BodyWrapper,继承HttpServletRequestWrapper, 先从request中取输入流,读取流中的数据,然后重写getInputStream()和getReader()方

  • 在JavaScript中获取请求的URL参数

    当然我们可以在后台中获取参数的值,然后在前台js代码中获取变量的值,具体做法请参考我的这篇文章:JavaScript获取后台C#变量以及调用后台方法. 其实我们也可以直接在js中获取请求的参数的值,通过使用window.location.search可以获取到当前URL的?号开始的字符串,如前面的链接获取到的search为?id=001.再对获取的字符串进行处理,就可以获取到参数的值了. 复制代码 代码如下: function getUrlParam(name) { var reg = new

  • 在JavaScript中获取请求的URL参数[正则]

    第一种方法:,代码比较专业 推荐 复制代码 代码如下: <script> function GetLocationParam(param){ var request = { QueryString : function(val) { var uri = window.location.search; var re = new RegExp("" +val+ "=([^&?]*)", "ig"); return ((uri.ma

  • 详解Nginx的核心配置模块中对于请求体的接受流程

    本篇文章主要会介绍nginx中请求的接收流程,包括请求头的解析和请求体的读取流程. 首先介绍一下rfc2616中定义的http请求基本格式: Request = Request-Line *(( general-header | request-header | entity-header ) CRLF) CRLF [ message-body ] </span> 第一行是请求行(request line),用来说明请求方法,要访问的资源以及所使用的HTTP版本: Request-Line  

  • SpringBoot之自定义Filter获取请求参数与响应结果案例详解

    一个系统上线,肯定会或多或少的存在异常情况.为了更快更好的排雷,记录请求参数和响应结果是非常必要的.所以,Nginx 和 Tomcat 之类的 web 服务器,都提供了访问日志,可以帮助我们记录一些请求信息. 本文是在我们的应用中,定义一个Filter来实现记录请求参数和响应结果的功能. 有一定经验的都知道,如果我们在Filter中读取了HttpServletRequest或者HttpServletResponse的流,就没有办法再次读取了,这样就会造成请求异常.所以,我们需要借助 Spring

  • Spring Boot中获取request的三种方式及请求过程

    目录 一.请求过程 二.获取request的三种方式 2.1.可以封装为静态方法 2.2.controller的方法里面 2.3.直接注入 三.request常用API 3.1.request路径相关 3.2.Header相关 3.3.获取请求体 3.4.获取参数 3.5.中文乱码 3.6.转发 3.7.共享数据 四.response常用API 五.常用工具类 5.1.封装的 5.2.Hutool工具类 本篇博客主要记录request相关知识,也是开发当中经常遇到的,感兴趣的跟小编一起学习吧!

  • 详解Nginx的配置函数对于请求体的读取

    nginx核心本身不会主动读取请求体,这个工作是交给请求处理阶段的模块来做,但是nginx核心提供了ngx_http_read_client_request_body()接口来读取请求体,另外还提供了一个丢弃请求体的接口-ngx_http_discard_request_body(),在请求执行的各个阶段中,任何一个阶段的模块如果对请求体感兴趣或者希望丢掉客户端发过来的请求体,可以分别调用这两个接口来完成.这两个接口是nginx核心提供的处理请求体的标准接口,如果希望配置文件中一些请求体相关的指

  • 基于express中路由规则及获取请求参数的方法

    express中常见的路由规则 主要使用的路由规则是get和post两种,即 var express = require('express'); var app = express(); app.get(); // get和post两种请求方式 app.post(); app.get()和app.post()的第一个参数为请求路径,第二个参数为处理请求的回调函数:回调函数有两个参数,分别为req和res,代表请求信息和响应信息. 获取请求路径和请求体中的各种参数 路径请求及对应获取请求路径的形式

随机推荐