SpringBoot过滤器如何获取POST请求的JSON参数|SpringBoot过滤器如何获取POST请求的JSON参数

目录

  • SpringBoot过滤器获取POST请求的JSON参数
    • 想到了使用过滤器来实现这个功能
    • 所以我们可以通过获取到输入流来获取body
    • 从源码我们可以看到
    • 我们创建一个类并继承这个包装类
    • 有一点需要注意的

SpringBoot过滤器获取POST请求的JSON参数 项目中需要将每个请求的路径和请求参数以及响应结果,都记录在日志中,这样在出现问题时可以快速定位是哪里出现了问题。

想到了使用过滤器来实现这个功能
当请求来到过滤器时,会有一个Request参数,通过该参数就能获取到请求路径和请求参数,以及相关内容
parameterMap = httpRequest.getParameterMap();String requestMethod = httpRequest.getMethod();String remoteAddr = httpRequest.getRemoteAddr();int remotePort = httpRequest.getRemotePort();

上面的getParameterMap(),只能够获取到GET请求的参数,如果是POST方法传的JSON那就没法获取到,那如何获取呢,POST的请求是在请求体body中,而POST请求中的body参数是已流形式存在的

所以我们可以通过获取到输入流来获取body
ServletInputStream inputStream = httpRequest.getInputStream();InputStreamReader reader = new InputStreamReader(inputStream,StandardCharsets.UTF_8);BufferedReader bfReader = new BufferedReader(reader);StringBuilder sb = new StringBuilder();String line;while ((line = bfReader.readLine()) != null){sb.append(line);}System.out.println(sb.toString());

通过上面的方法,我们确实能在过滤器中获取到POST的JSON参数了,但是按照上面的方法实现的过滤器,我们会发现,当请求经过过滤器来到Controller的时候,请求参数不见了
SpringBoot过滤器如何获取POST请求的JSON参数|SpringBoot过滤器如何获取POST请求的JSON参数
文章图片

可以看到,过滤器确实拿到JSON参数,但是接着报了一个request body missing的异常,也就是请求来到Controller时,参数没有了,这是为啥呢?我们先去源码看看,Controller平时是怎么拿到请求参数的吧
SpringBoot过滤器如何获取POST请求的JSON参数|SpringBoot过滤器如何获取POST请求的JSON参数
文章图片

根据DeBug,可以看到SpringBoot处理请求的最主要的两个方法是上图红框的doService和doDisparch方法,上面就是通过反射去获取参数名去匹配等
SpringBoot过滤器如何获取POST请求的JSON参数|SpringBoot过滤器如何获取POST请求的JSON参数
文章图片

来到invokeForRequest方法,这里面的getMethodArgumentValues,就是SpringBoot获取请求参数的入口,进入入口后
SpringBoot过滤器如何获取POST请求的JSON参数|SpringBoot过滤器如何获取POST请求的JSON参数
文章图片

再经过上面的红框,就能看到SpringBoot获取POST请求JSON的参数的真面目了
SpringBoot过滤器如何获取POST请求的JSON参数|SpringBoot过滤器如何获取POST请求的JSON参数
文章图片


从源码我们可以看到
SpringBoot也是通过获取request的输入流来获取参数,这样上面的疑问就能解开了,为什么经过过滤器来到Controller请求参数就没了,这是因为 InputStream read方法内部有一个,postion,标志当前流读取到的位置,每读取一次,位置就会移动一次,如果读到最后,InputStream.read方法会返回-1,标志已经读取完了,如果想再次读取,可以调用inputstream.reset方法,position就会移动到上次调用mark的位置,mark默认是0,所以就能从头再读了。但是呢 是否能reset又是由markSupported决定的,为true能reset,为false就不能reset,从源码可以看到,markSupported是为false的,而且一调用reset就是直接异常
SpringBoot过滤器如何获取POST请求的JSON参数|SpringBoot过滤器如何获取POST请求的JSON参数
文章图片

所以这也就代表,InputStream只能被读取一次,后面就读取不到了。因此我们在过滤器的时候,已经将InputStream读取过了一次,当来到Controller,SpringBoot读取InputStream的时候自然是什么都读取不到了
SpringBoot过滤器如何获取POST请求的JSON参数|SpringBoot过滤器如何获取POST请求的JSON参数
文章图片

既然InputStream只能读取一次,那我们可以把InputStream给保存下来,然后完整的传下去SpringBoot就可以读取到了,这里就需要用到HttpServletRequest的包装类HttpServletRequestWrapper了,该类可以自定义一些方法

我们创建一个类并继承这个包装类
public class RequestWrapper extends HttpServletRequestWrapper {private final byte[] body;public RequestWrapper(HttpServletRequest request) throws IOException {super(request);//保存一份InputStream,将其转换为字节数组body = StreamUtils.copyToByteArray(request.getInputStream());} //转换成Stringpublic String getBodyString(){return new String(body,StandardCharsets.UTF_8);}@Overridepublic BufferedReader getReader() throws IOException {return new BufferedReader(new InputStreamReader(getInputStream()));} //把保存好的InputStream,传下去@Overridepublic ServletInputStream getInputStream() throws IOException {final ByteArrayInputStream bais = new ByteArrayInputStream(body);return new ServletInputStream() {@Overridepublic int read() throws IOException {return bais.read();}@Overridepublic boolean isFinished() {return false;}@Overridepublic boolean isReady() {return false;}@Overridepublic void setReadListener(ReadListener readListener) {}};}}

通过保存一份流,就可实现在过滤器中能拿到JSON参数,同时Controller也不会丢失参数
SpringBoot过滤器如何获取POST请求的JSON参数|SpringBoot过滤器如何获取POST请求的JSON参数
文章图片


有一点需要注意的
在过滤器放行的时候,放行的是包装类和而不是原来的Request
SpringBoot过滤器如何获取POST请求的JSON参数|SpringBoot过滤器如何获取POST请求的JSON参数
文章图片

【SpringBoot过滤器如何获取POST请求的JSON参数|SpringBoot过滤器如何获取POST请求的JSON参数】以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

    推荐阅读