基于Spring Boot应用ServletRequestWrapper和ServletResponseWrapper

记录:277

场景:在过滤器Filter的doFilter中,拦截一个http请求。对请求对象ServletRequest,按照业务需求进行处理,比如参数验证、SQL注入拦截、日志审计等。对应答对象ServletResponse,按照业务需求进行处理,比如统一返回数据格式,加密返回数据,压缩返回数据等。

版本:Spring Boot 2.6.3

一、案例场景

1.发起一个http请求(POST请求),入参是JSON数据。

2.在Filter的doFilter中,使用ServletRequest接口的包装类,获取请求的入参并处理。

3.发布Restful的Controller处理业务请求,并返回JSON字符串数据。

4.在Filter的doFilter中,使用ServletResponse接口的包装类,处理应答数据,增加一个应答数据。

二、使用类

1.请求对象关系

javax.servlet.ServletRequest,接口。

javax.servlet.http.HttpServletRequest,接口。

javax.servlet.ServletRequestWrapper,包装类。

javax.servlet.http.HttpServletRequestWrapper,包装类。

2.应答对象关系

javax.servlet.ServletResponse,接口。

javax.servlet.http.HttpServletResponse,接口。

javax.servlet.ServletResponseWrapper,包装类。

javax.servlet.http.HttpServletResponseWrapper,包装类。

三、代码

1.CustomRequestWrapper

CustomRequestWrapper,继承HttpServletRequestWrapper,重写相关方法。

public class CustomRequestWrapper extends HttpServletRequestWrapper {
  private final String body;
  public CustomRequestWrapper(HttpServletRequest request) throws IOException {
   super(request);
   StringBuilder sb = new StringBuilder();
   BufferedReader bufferedReader = null;
   try {
     InputStream inputStream = request.getInputStream();
     if (inputStream != null) {
       bufferedReader = new BufferedReader(new InputStreamReader(
               inputStream, StandardCharsets.UTF_8));
       char[] charBuffer = new char[512];
       int bytesRead = -1;
       while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
           sb.append(charBuffer, 0, bytesRead);
       }
     } else {
         sb.append("");
     }
   } catch (IOException e) {
      e.printStackTrace();
      throw e;
   } finally {
     if (bufferedReader != null) {
      try {
          bufferedReader.close();
      } catch (IOException e) {
          e.printStackTrace();
          throw e;
      }
     }
   }
   body = sb.toString();
  }
  @Override
  public ServletInputStream getInputStream() throws IOException {
   final ByteArrayInputStream bais = new ByteArrayInputStream(
       body.getBytes("UTF-8"));
   return new ServletInputStream() {
     @Override
     public boolean isFinished() {
         return false;
     }
	 
     @Override
     public boolean isReady() {
         return false;
     }
	 
     @Override
     public void setReadListener(ReadListener readListener) {
	 
     }
     @Override
     public int read() {
         return bais.read();
     }
   };
  }
  @Override
  public BufferedReader getReader() throws IOException {
    return new BufferedReader(new InputStreamReader(
      this.getInputStream(), StandardCharsets.UTF_8));
  }
  public String getBody() {
      return this.body;
  }
  @Override
  public String getParameter(String name) {
    return super.getParameter(name);
  }
  @Override
  public Map<String, String[]> getParameterMap() {
    return super.getParameterMap();
  }
  @Override
  public Enumeration<String> getParameterNames() {
    return super.getParameterNames();
  }
  @Override
  public String[] getParameterValues(String name) {
    return super.getParameterValues(name);
 }
}

2.CustomResponseWrapper

CustomResponseWrapper,继承HttpServletResponseWrapper,重写相关方法。

public class CustomResponseWrapper extends HttpServletResponseWrapper {
  private ByteArrayOutputStream byteArrayOutputStream = null;
  private ServletOutputStream servletOutputStream = null;
  private PrintWriter printWriter = null;
  public CustomResponseWrapper(HttpServletResponse response) {
   super(response);
   // 数据流输出到此对象
   byteArrayOutputStream = new ByteArrayOutputStream();
  }
  /**
   * 重载
   * javax.servlet.ServletResponseWrapper的
   * getOutputStream方法
   */
  @Override
  public ServletOutputStream getOutputStream() throws IOException {
   if (servletOutputStream == null) {
       servletOutputStream = new WrapperOutputStream(
               byteArrayOutputStream);
   }
   return servletOutputStream;
  }
  /**
   * 重载
   * javax.servlet.ServletResponseWrapper的
   * getWriter方法
   */
  @Override
  public PrintWriter getWriter() throws IOException {
    if (printWriter == null) {
        printWriter = new PrintWriter(new OutputStreamWriter(
                byteArrayOutputStream,
                this.getCharacterEncoding()));
    }
    return printWriter;
  }
  @Override
  public void flushBuffer() throws IOException {
    if (servletOutputStream != null) {
        servletOutputStream.flush();
    }
    if (printWriter != null) {
        printWriter.flush();
    }
  }
  public byte[] getResponseByteData() throws IOException {
    flushBuffer();
    return byteArrayOutputStream.toByteArray();
  }
  public String getResponseStringData() throws IOException {
    flushBuffer();
    return byteArrayOutputStream.toString("utf-8");
  }
  public Map getResponseMapResult() {
    String contentType = getContentType();
    if (StringUtils.isNotBlank(contentType)
         && contentType.toLowerCase().contains("application/json")) {
     try {
         String jsonData = getResponseStringData();
         Map mapJson = JSON.parseObject(jsonData, Map.class);
         return mapJson;
     } catch (Exception e) {
         e.printStackTrace();
     }
    }
    return null;
  }
  private class WrapperOutputStream extends ServletOutputStream {
    private ByteArrayOutputStream bos = null;
    public WrapperOutputStream(ByteArrayOutputStream bos) {
        super();
        this.bos = bos;
    }
    @Override
    public boolean isReady() {
        return false;
    }
    @Override
    public void setWriteListener(WriteListener writeListener) {
    }
    @Override
    public void write(int b) throws IOException {
        bos.write(b);
    }
  }
}

3.ExampleFilter

ExampleFilter,实现Filter接口,重写相关方法。在doFilter中实现具体逻辑。

@Slf4j
public class ExampleFilter implements Filter {
  @Override
  public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
       FilterChain filterChain) throws IOException, ServletException {
   HttpServletRequest httpRequest = (HttpServletRequest) servletRequest;
   HttpServletResponse httpResponse = (HttpServletResponse) servletResponse;
   // 1.创建请求包装类: CustomRequestWrapper
   CustomRequestWrapper requestWrapper = new CustomRequestWrapper(httpRequest);
   if ("POST".equals(httpRequest.getMethod().toUpperCase())) {
     // 2. 从requestWrapper获取请求参数,可按需拦截,并做业务处理
     String body = requestWrapper.getBody();
     log.info("ExampleFilter—>doFilter,从request的InputStream,获取数据: " + body.toString());
   }
   // 3.创建响应包装类: CustomResponseWrapper
   CustomResponseWrapper responseWrapper = new CustomResponseWrapper(httpResponse);
   try {
     log.info("ExampleFilter—>doFilter,在FilterChain,传入requestWrapper和responseWrapper.");
     log.info("ExampleFilter—>doFilter,触发requestWrapper的getInputStream调用.");
     log.info("ExampleFilter—>doFilter,触发responseWrapper的getOutputStream调用.");
     // 4. 过滤器链执行(请求包装类,应答不装类)
     // 4.1 触发调用requestWrapper的getInputStream,将读取数据回写到ServletInputStream
     // 4.2 触发调用responseWrapper的getOutputStream,将数据写到responseWrapper的byteArrayOutputStream
     filterChain.doFilter(requestWrapper, responseWrapper);
     // 5. 从responseWrapper获取应答结果,可按需拦截,并做业务处理
     Map mapTemp = responseWrapper.getResponseMapResult();
     log.info("ExampleFilter—>doFilter,从responseWrapper的OutputStream,获取数据: " + JSON.toJSONString(mapTemp));
     if (mapTemp != null && mapTemp.size() > 0) {
         mapTemp.put("checkFlagDate", DateUtil.format(new Date(),"yyyy-MM-dd"));
     }
     log.info("ExampleFilter—>doFilter,加工后写入response的OutputStream数据: " + JSON.toJSONString(mapTemp));
     // 6. 应答数据回写到response的OutputStream中
     httpResponse.getOutputStream().write(
             JSON.toJSONString(mapTemp).getBytes(StandardCharsets.UTF_8));
     httpResponse.getOutputStream().flush();
   } catch (Exception e) {
      e.printStackTrace();
      throw e;
   }
  }
}

4.FilterConfiguration

FilterConfiguration,注册过滤器。

@Configuration
public class FilterConfiguration {
  @Bean("exampleFilter")
  public ExampleFilter sqlInjectFilter() {
      return new ExampleFilter();
  }
  @Bean
  public FilterRegistrationBean<ExampleFilter> sqlFilterRegistrationBean() {
      FilterRegistrationBean<ExampleFilter> filterReg = new FilterRegistrationBean<>();
      filterReg.setFilter(sqlInjectFilter());
      filterReg.addUrlPatterns("/*");
      filterReg.setOrder(1);
      return filterReg;
  }
}

5.FilterController

FilterController,提供resiful方法。

@Slf4j
@RestController
@RequestMapping("/filter")
public class FilterController {
 @PostMapping("/f1")
 public Object f1(@RequestBody Object obj) {
   log.info("FilterController->f1,接收参数,obj = " + obj.toString());
   Map objMap = (Map) obj;
   return ResultObj.builder().code("200").message("成功").build();
 }
}

6.ResultObj

ResultObj,全局返回对象。

@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class ResultObj {
    private String code;
    private String message;
}

7.测试

测试URL: http://127.0.0.1:8080/server/filter/f1

入参:

{"userName": "HangZhou","tradeName": "Vue进阶教程"}

返回:

{"code": "200","checkFlagDate": "2022-06-20","message": "成功"}

输出日志:

以上,感谢

2022年6月21日

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值