• SpringBoot过滤器获取Request的body数据


    背景 

           由于在SpringBoot过滤器或者拦截器中,request中getReader()和getInputStream()只能调用一次,到controller里数据就为空了,因此会导致Controller中@RequestBody的参数无法注入而导致 400 错误

      

    解决方案

    写HttpServletRequestWrapper把request保存下来,然后通过过滤器把保存下来的request再填充进去

    1. 写一个CxmHttpServletRequestWrapper包装类,继承HttpServletRequestWrapper

    1. @Component
    2. public class CxmHttpServletRequestWrapper extends HttpServletRequestWrapper {
    3. private byte[] requestBody = new byte[0];
    4. private boolean bufferFilled = false;
    5. private ConcurrentHashMap<String, String> cxmHeaders = new ConcurrentHashMap<>();
    6. public CxmHttpServletRequestWrapper(HttpServletRequest request) {
    7. super(request);
    8. }
    9. @Override
    10. public ServletInputStream getInputStream() throws IOException {
    11. return new CachedServletInputStream(getRequestBody());
    12. }
    13. @Override
    14. public String getHeader(String name) {
    15. String headerValue = cxmHeaders.get(name);
    16. if (headerValue != null) {
    17. return headerValue;
    18. } else {
    19. return super.getHeader(name);
    20. }
    21. }
    22. public void setHeader(String name, String value){
    23. this.cxmHeaders.put(name, value);
    24. }
    25. public byte[] getRequestBody() throws IOException {
    26. if (bufferFilled) {
    27. return Arrays.copyOf(requestBody, requestBody.length);
    28. }
    29. InputStream inputStream = super.getInputStream();
    30. requestBody = IOUtils.toByteArray(inputStream);
    31. bufferFilled = true;
    32. return requestBody;
    33. }
    34. }

    写一个CachedServletInputStream继承ServletInputStream用来处理数据

    1. public class CachedServletInputStream extends ServletInputStream {
    2. private ByteArrayInputStream buffer;
    3. public CachedServletInputStream(byte[] contents) {
    4. this.buffer = new ByteArrayInputStream(contents);
    5. }
    6. @Override
    7. public int read() throws IOException {
    8. return buffer.read();
    9. }
    10. @Override
    11. public boolean isFinished() {
    12. return buffer.available() == 0;
    13. }
    14. @Override
    15. public boolean isReady() {
    16. return true;
    17. }
    18. @Override
    19. public void setReadListener(ReadListener listener) {
    20. throw new RuntimeException("Not implemented");
    21. }
    22. }

    2. 在过滤器中取值的时候转换成为我们自己的request包装类,并且把request传递下去

    1. @Override
    2. public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
    3. throws IOException, ServletException {
    4. HttpServletRequest requestWrapper = null;
    5. if (request instanceof HttpServletRequest) {
    6. requestWrapper = new CxmHttpServletRequestWrapper((HttpServletRequest) request);
    7. }
    8. chain.doFilter(requestWrapper, response);
    9. }

    3. 过滤器配置

    1. @Slf4j
    2. @Configuration
    3. public class WebMvcConfig implements WebMvcConfigurer {
    4. /**
    5. *
    6. * @Title: Filters
    7. * @Description: 配置自定义的filter
    8. * @return
    9. * @throws
    10. *
    11. */
    12. @Bean
    13. public FilterRegistrationBean<CxmRequestValidFilter> Filters() {
    14. FilterRegistrationBean<CxmRequestValidFilter> register = new FilterRegistrationBean<CxmRequestValidFilter>();
    15. register.setFilter(new CxmRequestValidFilter());
    16. register.addUrlPatterns("/*");
    17. // 初始化filter的参数
    18. register.addInitParameter("profile", profile);
    19. register.setName("cxmRequestValidFilter");
    20. return register;
    21. }
    22. }

  • 相关阅读:
    angular的observable
    K8S存储
    大数据中的分布式文件系统MapReduce的选择题
    windows安装zabbix-agent
    AI短视频矩阵运营软件|抖音视频矩阵控制工具
    CVPR 2023 | EfficientViT:让ViT在多个部署场景实现实时推理
    (三)Shell编程之数据类型
    取出分组后前 N 名对应记录
    Go项目使用自定义的公共单元
    【解决方案】Collecting package metadata (current_repodata.json): failed
  • 原文地址:https://blog.csdn.net/Crystalqy/article/details/134437288