当前位置:   article > 正文

【SpringBoot】获取request请求参数,多次读取报错问题 (has already been called for this request)_getinputstream() has already been called for this

getinputstream() has already been called for this request

应用场景 :

        因项目中接口请求时, 需要对请求参数进行签名验证。 当请求参数的body中有基本类型时(例: int, long, boolean等),因为基本类型如果没传值,序列化的时候会有默认值的问题, 最后导致实际接口调用生成的签名和项目中进行校验的签名不匹配。如果直接从request中获取请求参数body, 会出现request请求流重复读取异常,因此需要实现HttpServletRequestWrapper 重写getInputStream()和getReader()方法,将请求参数body复制到自己requestWrapper中, 后续只操作自己的requestWrapper

代码实现

启动类加上@ServletComponentScan 注解,开启Servlet、Filter、Listener可以直接通过@WebServlet、@WebFilter、@WebListener注解自动注册

  1. //开启Servlet、Filter、Listener可以直接通过@WebServlet、@WebFilter、@WebListener注解自动注册
  2. @ServletComponentScan
  3. @SpringBootApplication(scanBasePackages = {"xxx.xxx.xx"})
  4. public class StartApplication {
  5. public static void main(String[] args) {
  6. SpringApplication.run(StartApplication.class, args);
  7. }
  8. }

自定义filter, 必须将自定义的wrapper通过过滤器传下去, 不传不会调用重写后的getInputStream()和getReader()方法

  1. @Component
  2. @WebFilter(filterName = "RewriteRequestFilter", urlPatterns = "/*")
  3. @Order(1)
  4. public class RewriteRequestFilter implements Filter {
  5. @Override
  6. public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
  7. //文件上传类型 不需要处理,否则会报java.nio.charset.MalformedInputException: Input length = 1异常
  8. if (Objects.isNull(request) || Optional.ofNullable(request.getContentType()).orElse(StringUtils.EMPTY).startsWith("multipart/")) {
  9. chain.doFilter(request, response);
  10. return;
  11. }
  12. //自定义wrapper 处理流,必须在过滤器中处理,然后通过FilterChain传下去, 否则重写后的getInputStream()方法不会被调用
  13. MyHttpServletRequestWrapper requestWrapper = new MyHttpServletRequestWrapper((HttpServletRequest)request);
  14. chain.doFilter(requestWrapper,response);
  15. }
  16. }

自定义wrapper复制请求流, 如果不重写会报 java.lang.IllegalStateException: getInputStream() has already been called for this request 异常, 原因是request请求流不能重复读取。

  1. @Slf4j
  2. @Getter
  3. public class MyHttpServletRequestWrapper extends HttpServletRequestWrapper {
  4. /** 复制请求body */
  5. private final String body;
  6. public MyHttpServletRequestWrapper (HttpServletRequest request) {
  7. super(request);
  8. try {
  9. //设置编码格式, 防止中文乱码
  10. request.setCharacterEncoding("UTF-8");
  11. //将请求中的流取出来放到body里,后面都只操作body就行
  12. this.body = RequestReadUtils.read(request);
  13. } catch (Exception e) {
  14. log.error("MyHttpServletRequestWrapper exception", e);
  15. throw new RuntimeException("MyHttpServletRequestWrapper 拦截器异常");
  16. }
  17. }
  18. @Override
  19. public ServletInputStream getInputStream() {
  20. //返回body的流信息即可
  21. try(final ByteArrayInputStream bais = new ByteArrayInputStream(body.getBytes())){
  22. return getServletInputStream(bais);
  23. }catch(IOException e){
  24. log.error("MyHttpServletRequestWrapper.getInputStream() exception", e);
  25. throw new RuntimeException("MyHttpServletRequestWrapper 获取input流异常");
  26. }
  27. }
  28. @Override
  29. public BufferedReader getReader(){
  30. return new BufferedReader(new InputStreamReader(this.getInputStream()));
  31. }
  32. /**
  33. * 重写getInputStream流
  34. * @param bais
  35. * @return
  36. */
  37. private static ServletInputStream getServletInputStream(ByteArrayInputStream bais) {
  38. return new ServletInputStream() {
  39. @Override
  40. public boolean isFinished() {
  41. return false;
  42. }
  43. @Override
  44. public boolean isReady() {
  45. return false;
  46. }
  47. @Override
  48. public void setReadListener(ReadListener readListener) {
  49. }
  50. @Override
  51. public int read() {
  52. return bais.read();
  53. }
  54. };
  55. }
  56. }

读取请求流工具类

  1. @Slf4j
  2. public class RequestReadUtils {
  3. /**
  4. * 读取请求流
  5. * @param request
  6. * @return
  7. * @throws UnsupportedEncodingException
  8. */
  9. public static String read(HttpServletRequest request){
  10. try(BufferedReader reader = request.getReader()){
  11. StringBuilder sb = new StringBuilder();
  12. String line;
  13. while ((line = reader.readLine()) != null) {
  14. sb.append(line);
  15. }
  16. return sb.toString();
  17. }catch (Exception e){
  18. log.error("MyHttpServletRequestWrapper.RequestReadUtils.readexception", e);
  19. throw new RuntimeException("MyHttpServletRequestWrapper.RequestReadUtils.read 获取请求流异常");
  20. }
  21. }
  22. }

自定义interceptor拦截器, 判断如果是自己的wrapper, 从wrapper中获取请求参数。只能在拦截器中获取请求参数,

1.如果在自定义的Filter中获取请求参数, restful风格的请求参数无法获取。

2.如果在AOP中获取请求参数, 获取的是序列化后的请求参数(基本类型默认值也会被获取) 

  1. @Slf4j
  2. public class CommonInterceptor extends BaseInterceptor {
  3. @Override
  4. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
  5. //如果是自定义wrapper, 从自定义wrapper中获取请求参数, 必须在interceptor拦截器中处理, 否则restful风格的请求参数获取不到。
  6. if(request instanceof MyHttpServletRequestWrapper){
  7. Map<String, Object> requestParam = getRequestParam((MyHttpServletRequestWrapper)request);
  8. //放到ThreadLocal中, 这里可以根据自己的项目业务处理
  9. CommonData.set("param", requestParam);
  10. }
  11. return true;
  12. }
  13. @Override
  14. public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,
  15. Object o, Exception e) throws Exception {
  16. //清空ThreadLocal, 防止内存泄漏
  17. clearAllData();
  18. }
  19. private void clearAllData() {
  20. CommonData.clearAll();
  21. }
  22. /**
  23. * 从request获取参数
  24. * @param request
  25. * @return
  26. * @throws IOException
  27. */
  28. private Map<String, Object> getRequestParam(CallHttpServletRequestWrapper request){
  29. Map<String, Object> paramMap = Maps.newHashMap();
  30. //获取使用@RequestParam注解的参数
  31. Map<String, String[]> parameterMap = request.getParameterMap();
  32. if(!CollectionUtils.isEmpty(parameterMap)){
  33. parameterMap.forEach((k,v)->{
  34. if(Objects.nonNull(v) && v.length > 0){
  35. paramMap.put(k, v[0]);
  36. }
  37. });
  38. }
  39. //获取restful请求参数,必须在interceptor拦截其中才能这样获取到restful参数
  40. Object attribute = request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
  41. if(Objects.nonNull(attribute)){
  42. Map<String, Object> attributeMap = (Map<String, Object>)attribute;
  43. if(!CollectionUtils.isEmpty(attributeMap)){
  44. paramMap.putAll(attributeMap);
  45. }
  46. }
  47. //从自定义wrapper中, 获取body体参数
  48. String bodyString = request.getBody();
  49. if(StringUtils.isBlank(bodyString)){
  50. return paramMap;
  51. }
  52. //解析body参数
  53. Map<String, Object> bodyMap = parseRequestMap(bodyString);
  54. if(CollectionUtils.isEmpty(bodyMap)){
  55. return paramMap;
  56. }
  57. paramMap.putAll(bodyMap);
  58. return paramMap;
  59. }
  60. /**
  61. * 解析body请求参数
  62. * @param bodyString
  63. * @return
  64. */
  65. private Map<String, Object> parseRequestMap(String bodyString) {
  66. Map<String, Object> paramMap = Maps.newHashMap();
  67. boolean validObject = JSONObject.isValidObject(bodyString);
  68. //解析@ReqeustBody注解参数
  69. if(validObject){
  70. JSONObject jsonObject = JSONObject.parseObject(bodyString);
  71. paramMap.putAll(jsonObject);
  72. }else{
  73. //解析url拼接参数 例 a=123&b=456, 没有加@RequestBoyd注解的post请求
  74. String[] param = bodyString.split(SpecialConstant.AND);
  75. if(param.length == 0){
  76. return paramMap;
  77. }
  78. Stream.of(param).forEach(e->{
  79. String[] split = e.split(SpecialConstant.EQ);
  80. if(split.length == 0){
  81. return;
  82. }
  83. paramMap.put(split[0], split[1]);
  84. });
  85. }
  86. return paramMap;
  87. }
  88. }

自定义ThreadLocal工具类

  1. @Slf4j
  2. @Getter
  3. @Setter
  4. public class CommonData {
  5. private static ThreadLocal<Map<String, Object>> threadLocal = new ThreadLocal<>();
  6. /**
  7. * 添加数据
  8. * @param key
  9. * @param value
  10. */
  11. public static void set(String key, Object value) {
  12. if (threadLocal.get() == null) {
  13. Map<String, Object> map = new HashMap<>();
  14. threadLocal.set(map);
  15. }
  16. threadLocal.get().put(key, value);
  17. }
  18. /**
  19. * 清除数据
  20. */
  21. public static void clearAll() {
  22. threadLocal.set(null);
  23. }
  24. public static Map<String, Object> getSignParam() {
  25. Object o = threadLocal.get().get("param");
  26. if (Objects.isNull(o)) {
  27. log.info("CommonData.getSignParam is null");
  28. return null;
  29. }
  30. return (Map<String, Object>) o;
  31. }
  32. }

至此通过自定义wrapper重复读取request请求流的方式完成, 也不会再报 java.lang.IllegalStateException: getInputStream() has already been called for this request异常

最后感谢大家的阅读, 如问题请随时指出!!!

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

闽ICP备14008679号