当前位置:   article > 正文

利用HttpServletRequestWrapper来支持可重复读取HttpServletRequest中的请求输入流且不影响Controller层的参数获取_java request 流改成可重复读流的性能影响

java request 流改成可重复读流的性能影响

HttpServletRequest中的请求输入流不可重复读取的原因就不叙述了,一堆搜索结果随便看,解决方案有2种,1是使用Spring MVC自带的类ContentCachingRequestWrapper,2是自定义请求包装器,方案2请看如下步骤正文:

1. 定义请求包装器,继承于 HttpServletRequestWrapper

  1. import lombok.extern.slf4j.Slf4j;
  2. import org.apache.commons.lang3.ArrayUtils;
  3. import org.apache.commons.lang3.StringUtils;
  4. import org.springframework.http.MediaType;
  5. import org.springframework.util.LinkedMultiValueMap;
  6. import org.springframework.util.MultiValueMap;
  7. import javax.servlet.ReadListener;
  8. import javax.servlet.ServletInputStream;
  9. import javax.servlet.http.HttpServletRequest;
  10. import javax.servlet.http.HttpServletRequestWrapper;
  11. import java.io.*;
  12. import java.net.URLDecoder;
  13. import java.nio.charset.Charset;
  14. import java.nio.charset.StandardCharsets;
  15. import java.util.*;
  16. /**
  17. * 自定义请求包装器,用于存储请求body数据,以解决请求输入流只能读取一次的问题
  18. */
  19. @Slf4j
  20. public class MyRequestWrapper extends HttpServletRequestWrapper {
  21. public static final Charset defaultCharset = StandardCharsets.UTF_8;
  22. private final Charset charset;
  23. private final byte[] bodyBytes;
  24. private final MultiValueMap<String, String> formParameters;
  25. public MyRequestWrapper(HttpServletRequest request) throws IOException {
  26. super(request);
  27. String charEncode = request.getCharacterEncoding();
  28. String contentType = request.getContentType();
  29. if (StringUtils.isNotBlank(charEncode)) {
  30. charset = Charset.forName(charEncode);
  31. } else if (StringUtils.isNotBlank(contentType)
  32. && contentType.contains("charset=")) {
  33. charset = Charset.forName(contentType.substring(contentType.indexOf("=") + 1));
  34. } else {
  35. charset = defaultCharset;
  36. }
  37. bodyBytes = handleInputStream(request.getInputStream());
  38. if (StringUtils.isNotBlank(contentType)
  39. && contentType.contains(MediaType.APPLICATION_FORM_URLENCODED_VALUE)) {
  40. formParameters = handleFormParameters();
  41. } else {
  42. formParameters = new LinkedMultiValueMap(0);
  43. }
  44. }
  45. private byte[] handleInputStream(InputStream inputStream) throws IOException {
  46. /*BufferedInputStream bis = new BufferedInputStream(inputStream);
  47. byte[] buffer = new byte[1024];
  48. int volume;
  49. byte[] target = new byte[0];
  50. while ((volume = bis.read(buffer)) != -1) {
  51. target = ArrayUtils.addAll(target, buffer);
  52. }
  53. bis.close();*/
  54. StringBuffer sb = new StringBuffer();
  55. BufferedReader br = new BufferedReader(new InputStreamReader(inputStream, charset));
  56. String line;
  57. while ((line = br.readLine()) != null) {
  58. sb.append(line);
  59. }
  60. br.close();
  61. byte[] target = sb.toString().getBytes(charset);
  62. return target;
  63. }
  64. public String getBody() {
  65. return new String(bodyBytes, this.charset);
  66. }
  67. /**
  68. * 参照HttpPutFormContentFilter
  69. */
  70. private MultiValueMap<String, String> handleFormParameters() throws UnsupportedEncodingException {
  71. String[] pairs = tokenizeToStringArray(getBody(), "&", true, true);
  72. MultiValueMap<String, String> result = new LinkedMultiValueMap(pairs.length);
  73. String[] var8 = pairs;
  74. int var9 = pairs.length;
  75. for(int var10 = 0; var10 < var9; ++var10) {
  76. String pair = var8[var10];
  77. int idx = pair.indexOf(61);
  78. if (idx == -1) {
  79. result.add(URLDecoder.decode(pair, charset.name()), null);
  80. } else {
  81. String name = URLDecoder.decode(pair.substring(0, idx), charset.name());
  82. String value = URLDecoder.decode(pair.substring(idx + 1), charset.name());
  83. result.add(name, value);
  84. }
  85. }
  86. return result;
  87. }
  88. private String[] tokenizeToStringArray(String str, String delimiters, boolean trimTokens, boolean ignoreEmptyTokens) {
  89. if (str == null) {
  90. return new String[0];
  91. } else {
  92. StringTokenizer st = new StringTokenizer(str, delimiters);
  93. ArrayList<String> tokens = new ArrayList<>();
  94. while(true) {
  95. String token;
  96. do {
  97. if (!st.hasMoreTokens()) {
  98. return tokens.toArray(new String[0]);
  99. }
  100. token = st.nextToken();
  101. if (trimTokens) {
  102. token = token.trim();
  103. }
  104. } while(ignoreEmptyTokens && token.length() <= 0);
  105. tokens.add(token);
  106. }
  107. }
  108. }
  109. @Override
  110. public ServletInputStream getInputStream() throws IOException {
  111. ByteArrayInputStream inputStream = new ByteArrayInputStream(bodyBytes);
  112. return new ServletInputStream() {
  113. @Override
  114. public boolean isFinished() {
  115. return false;
  116. }
  117. @Override
  118. public boolean isReady() {
  119. return false;
  120. }
  121. @Override
  122. public void setReadListener(ReadListener readListener) {
  123. }
  124. @Override
  125. public int read() throws IOException {
  126. return inputStream.read();
  127. }
  128. };
  129. }
  130. @Override
  131. public BufferedReader getReader() throws IOException {
  132. return new BufferedReader(new InputStreamReader(getInputStream()));
  133. }
  134. @Override
  135. public String getParameter(String name) {
  136. String queryStringValue = super.getParameter(name);
  137. String formValue = this.formParameters.getFirst(name);
  138. return queryStringValue != null ? queryStringValue : formValue;
  139. }
  140. @Override
  141. public Map<String, String[]> getParameterMap() {
  142. Map<String, String[]> result = new LinkedHashMap();
  143. Enumeration names = this.getParameterNames();
  144. while(names.hasMoreElements()) {
  145. String name = (String)names.nextElement();
  146. result.put(name, this.getParameterValues(name));
  147. }
  148. return result;
  149. }
  150. @Override
  151. public Enumeration<String> getParameterNames() {
  152. Set<String> names = new LinkedHashSet();
  153. names.addAll(Collections.list(super.getParameterNames()));
  154. names.addAll(this.formParameters.keySet());
  155. return Collections.enumeration(names);
  156. }
  157. @Override
  158. public String[] getParameterValues(String name) {
  159. String[] parameterValues = super.getParameterValues(name);
  160. List<String> formParam = this.formParameters.get(name);
  161. if (formParam == null) {
  162. return parameterValues;
  163. } else if (parameterValues != null && this.getQueryString() != null) {
  164. List<String> result = new ArrayList(parameterValues.length + formParam.size());
  165. result.addAll(Arrays.asList(parameterValues));
  166. result.addAll(formParam);
  167. return result.toArray(new String[0]);
  168. } else {
  169. return formParam.toArray(new String[0]);
  170. }
  171. }
  172. }

2. 定义过滤器,用于将 HttpServletRequest 对象替换成自定义请求包装器 MyRequestWrapper 对象

  1. import lombok.extern.slf4j.Slf4j;
  2. import javax.servlet.*;
  3. import javax.servlet.http.HttpServletRequest;
  4. import java.io.IOException;
  5. /**
  6. * 在过滤器中将 HttpServletRequest 对象替换成自定义请求包装器 MyRequestWrapper 对象
  7. */
  8. @Slf4j
  9. public class MyRequestWrapperFilter implements Filter {
  10. @Override
  11. public void init(FilterConfig filterConfig) throws ServletException {
  12. }
  13. @Override
  14. public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
  15. log.debug("执行HttpServletRequestWrapper包装器替换");
  16. MyRequestWrapper requestWrapper = new MyRequestWrapper((HttpServletRequest) servletRequest);
  17. filterChain.doFilter(requestWrapper, servletResponse);
  18. }
  19. @Override
  20. public void destroy() {
  21. }
  22. }

3. 定义拦截器,提前获取请求body数据

  1. import com.alibaba.fastjson.JSON;
  2. import com.me.config.filter.MyRequestWrapper;
  3. import lombok.extern.slf4j.Slf4j;
  4. import org.springframework.web.servlet.HandlerInterceptor;
  5. import org.springframework.web.util.WebUtils;
  6. import javax.servlet.http.HttpServletRequest;
  7. import javax.servlet.http.HttpServletResponse;
  8. import java.util.Map;
  9. @Slf4j
  10. public class AccessInterceptor implements HandlerInterceptor {
  11. @Override
  12. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
  13. Map<String, String[]> parameterMap;
  14. String body;
  15. /*if (request instanceof MyRequestWrapper) {
  16. MyRequestWrapper requestWrapper = (MyRequestWrapper) request;
  17. body = requestWrapper.getBody();
  18. parameterMap = requestWrapper.getParameterMap();
  19. } else {
  20. body = "";
  21. parameterMap = request.getParameterMap();
  22. }*/
  23. MyRequestWrapper requestWrapper = WebUtils.getNativeRequest(request, MyRequestWrapper.class);
  24. if (requestWrapper != null) {
  25. body = requestWrapper.getBody();
  26. parameterMap = requestWrapper.getParameterMap();
  27. } else {
  28. body = StringUtils.EMPTY;
  29. parameterMap = request.getParameterMap();
  30. }
  31. log.info("clientURL----->{}", request.getRemoteAddr());
  32. log.info("requestURL----->{} {}", request.getMethod(), request.getRequestURL().toString());
  33. log.info("parameterMap----->{}", JSON.toJSONString(parameterMap));
  34. log.info("requestBody----->{}", body);
  35. return true;
  36. }
  37. }

4. 在配置类中注册过滤器与拦截器,使其生效

  1. import lombok.extern.slf4j.Slf4j;
  2. import org.springframework.boot.web.servlet.FilterRegistrationBean;
  3. import org.springframework.context.annotation.Bean;
  4. import org.springframework.context.annotation.Configuration;
  5. import org.springframework.web.cors.CorsConfiguration;
  6. import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
  7. import org.springframework.web.filter.CorsFilter;
  8. import org.springframework.web.servlet.config.annotation.*;
  9. @Slf4j
  10. @Configuration
  11. public class BaseConfig extends WebMvcConfigurationSupport {
  12. @Bean
  13. public MyRequestWrapperFilter getMyRequestWrapperFilter() {
  14. return new MyRequestWrapperFilter();
  15. }
  16. @Bean
  17. public FilterRegistrationBean someFilterRegistration() {
  18. FilterRegistrationBean registration = new FilterRegistrationBean();
  19. registration.setFilter(getMyRequestWrapperFilter());
  20. registration.addUrlPatterns("/*");
  21. registration.setName("myInputStreamFilter");
  22. // 设置过滤器执行优先级,数值越小优先级越高
  23. // 当前只要保证Filter过滤器在Interceptor拦截器之前执行即可,所以设置为最低优先级
  24. registration.setOrder(Ordered.LOWEST_PRECEDENCE);
  25. return registration;
  26. }
  27. @Bean
  28. public AccessInterceptor getAccessInterceptor() {
  29. return new AccessInterceptor();
  30. }
  31. @Override
  32. public void addInterceptors(InterceptorRegistry registry) {
  33. registry.addInterceptor(getAccessInterceptor())
  34. .addPathPatterns("/**")
  35. .excludePathPatterns("/static/**");
  36. }
  37. /**
  38. * 解决跨域问题
  39. */
  40. @Override
  41. public void addCorsMappings(CorsRegistry registry) {
  42. registry.addMapping("/**")
  43. .allowedOrigins("*")
  44. .allowedHeaders("Content-Type,X-Requested-With,Cookies,Cookie,X-Auth-Token,token,auth,Authorization")
  45. .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
  46. .allowCredentials(true)
  47. .maxAge(3600);
  48. }
  49. @Bean
  50. public CorsFilter corsFilter() {
  51. UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
  52. CorsConfiguration corsConfiguration = new CorsConfiguration();
  53. // 请求常用的三种配置,*代表允许所有,当时你也可以自定义属性(比如header只能带什么,method只能是post方式等等)
  54. corsConfiguration.addAllowedOrigin("*");
  55. corsConfiguration.addAllowedHeader("Content-Type,X-Requested-With,Cookies,Cookie,X-Auth-Token,token,auth,Authorization");
  56. corsConfiguration.addAllowedMethod("GET,POST,PUT,DELETE,OPTIONS");
  57. corsConfiguration.setAllowCredentials(true);
  58. corsConfiguration.setMaxAge(3600L);
  59. source.registerCorsConfiguration("/**", corsConfiguration);
  60. return new CorsFilter(source);
  61. }
  62. }

5. 定义Controller类,这里就不贴源码了,与正常写法无差异。

总结:最主要的在于请求包装器 MyRequestWrapper 中对输入流的处理和存储以及重写获取参数的几个方法,再加上过滤器中的请求包装器替换来保证后续输入流和参数的正常可重复获取。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/550854
推荐阅读
相关标签
  

闽ICP备14008679号