欢迎访问悦橙教程(wld5.com),关注java教程。悦橙教程  java问答|  每日更新
页面导航 : > > 文章正文

如何使用拦截器获取请求的入参并将其转化为Java对象详解,

来源: javaer 分享于  点击 22378 次 点评:266

如何使用拦截器获取请求的入参并将其转化为Java对象详解,


目录
  • 方式一:拦截器配合过滤器
    • 1. 设计思路
      • 输入流的问题
      • 解决方案
    • 2. 如何实现
      • 3. 实现步骤
        • 3.1 创建 HttpServletRequestWrapper
        • 3.2 创建 Filter 以包装 HttpServletRequest
        • 3.3 创建拦截器 HandlerInterceptor 以处理请求参数
        • 3.4 注册拦截器和过滤器
      • 4. 总结
      • 方式二:拦截器中使用包装类ContentCachingRequestWrapper
        • 总结

          在SpringBoot中实现一个拦截器(HandlerInterceptor),用于获取请求的入参并将其转化为 Java 对象。

          方式一:拦截器配合过滤器

          1. 设计思路

          在 Web 开发中,当 HTTP 请求发送到服务器时,Spring 会通过一个链条处理这个请求,这个链条包括很多组件,比如:拦截器(HandlerInterceptor)、过滤器(Filter)、控制器(Controller)、参数解析器(比如 Spring MVC 的 @RequestParam 或 @RequestBody 等注解)等。

          输入流的问题

          当客户端通过 POST 请求发送数据时,数据通常是包含在请求体中的(比如表单数据或者 JSON 数据)。Spring 的 HttpServletRequest 提供了 getInputStream() 方法来读取请求体中的数据。

          问题: HttpServletRequest.getInputStream() 只能读取一次。也就是说,当你在拦截器中调用了 getInputStream() 读取数据时,流就被消费掉了,后续的组件(例如,Spring 的参数解析器)再调用 getInputStream() 就无法读取到数据了,因为流已经被关闭了。

          解决方案

          解决这个问题的思路是:在拦截器中读取请求体的数据时,不直接从 HttpServletRequest 中读取,而是通过包装(HttpServletRequestWrapper)的方式,重新实现 getInputStream(),将读取的数据缓存下来,确保后续的处理链依然能够访问到请求体的内容。

          2. 如何实现

          • 创建一个 HttpServletRequestWrapper 类:它将重写 getInputStream() 方法,让流的数据可以多次读取。通过这个类缓存请求体的内容。
          • 创建一个 Filter:用于包装请求,将 HttpServletRequest 包装成我们自己的 HttpServletRequestWrapper
          • 在拦截器中获取请求体:在 HandlerInterceptor 中获取请求体并进行解析。

          3. 实现步骤

          3.1 创建 HttpServletRequestWrapper

          这个类主要作用是缓存请求体内容,并且重写 getInputStream(),让它能够多次读取。

          import javax.servlet.http.HttpServletRequest;
          import javax.servlet.http.HttpServletRequestWrapper;
          import java.io.ByteArrayInputStream;
          import java.io.IOException;
          import java.io.InputStream;
          
          public class CachedBodyHttpServletRequestWrapper extends HttpServletRequestWrapper {
          
              private byte[] requestBody;
          
              public CachedBodyHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
                  super(request);
                  // 从 InputStream 读取数据,缓存请求体内容
                  InputStream inputStream = request.getInputStream();
                  this.requestBody = inputStream.readAllBytes();  // 将流中的数据读取到字节数组中
              }
          
              @Override
              public InputStream getInputStream() throws IOException {
                  // 返回缓存的数据
                  return new ByteArrayInputStream(requestBody);
              }
          
              public byte[] getRequestBody() {
                  return requestBody;
              }
          }
          

          3.2 创建 Filter 以包装 HttpServletRequest

          这个过滤器的作用是将原始的 HttpServletRequest 替换为我们自定义的 CachedBodyHttpServletRequestWrapper

          import javax.servlet.Filter;
          import javax.servlet.FilterChain;
          import javax.servlet.FilterConfig;
          import javax.servlet.ServletException;
          import javax.servlet.ServletRequest;
          import javax.servlet.ServletResponse;
          import javax.servlet.annotation.WebFilter;
          import javax.servlet.http.HttpServletRequest;
          import java.io.IOException;
          
          @WebFilter("/*")  // 这个过滤器会拦截所有请求
          public class RequestWrapperFilter implements Filter {
          
              @Override
              public void init(FilterConfig filterConfig) throws ServletException {
                  // 初始化操作
              }
          
              @Override
              public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
                      throws IOException, ServletException {
                  // 仅对 HttpServletRequest 进行包装
                  if (request instanceof HttpServletRequest) {
                      CachedBodyHttpServletRequestWrapper wrappedRequest =
                              new CachedBodyHttpServletRequestWrapper((HttpServletRequest) request);
                      // 将包装后的请求传递给下一个过滤器
                      chain.doFilter(wrappedRequest, response);
                  } else {
                      // 对非 HttpServletRequest 请求不做任何处理
                      chain.doFilter(request, response);
                  }
              }
          
              @Override
              public void destroy() {
                  // 销毁操作
              }
          }
          

          3.3 创建拦截器 HandlerInterceptor 以处理请求参数

          接下来,在 Spring 的拦截器中获取请求体并解析成 Java 对象。这个拦截器将会在请求进入控制器之前进行拦截。

          import org.springframework.web.servlet.HandlerInterceptor;
          
          import javax.servlet.http.HttpServletRequest;
          import javax.servlet.http.HttpServletResponse;
          import java.io.IOException;
          
          public class RequestBodyInterceptor implements HandlerInterceptor {
          
              @Override
              public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
                      throws IOException {
                  if (request instanceof CachedBodyHttpServletRequestWrapper) {
                      // 获取包装后的请求体
                      CachedBodyHttpServletRequestWrapper wrappedRequest = (CachedBodyHttpServletRequestWrapper) request;
                      String requestBody = new String(wrappedRequest.getRequestBody(), "UTF-8");
                      // 打印或处理请求体内容
                      System.out.println("Request Body: " + requestBody);
          
                      // 将请求体转换成 Java 对象
                      MyRequestObject myRequestObject = new ObjectMapper().readValue(requestBody, MyRequestObject.class);
                      System.out.println("Parsed Java Object: " + myRequestObject);
                  }
          
                  return true; // 返回 true,表示继续处理请求
              }
          }
          

          3.4 注册拦截器和过滤器

          • 在 Spring Boot 中注册 Filter
            提示 :在Spring Boot项目中,Filter会自动注册到应用上下文中,可以不手动注册。
          import org.springframework.boot.web.servlet.FilterRegistrationBean;
          import org.springframework.context.annotation.Bean;
          import org.springframework.context.annotation.Configuration;
          
          @Configuration
          public class FilterConfig {
          
              @Bean
              public FilterRegistrationBean<RequestWrapperFilter> loggingFilter() {
                  FilterRegistrationBean<RequestWrapperFilter> registrationBean = new FilterRegistrationBean<>();
                  registrationBean.setFilter(new RequestWrapperFilter());
                  registrationBean.addUrlPatterns("/api/*");  // 这里根据需要配置拦截的 URL
                  return registrationBean;
              }
          }
          
          • 在 Spring Boot 中注册 HandlerInterceptor
          import org.springframework.beans.factory.annotation.Autowired;
          import org.springframework.context.annotation.Bean;
          import org.springframework.context.annotation.Configuration;
          import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
          import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
          
          @Configuration
          public class WebConfig implements WebMvcConfigurer {
          
              @Autowired
              private RequestBodyInterceptor requestBodyInterceptor;
          
              @Override
              public void addInterceptors(InterceptorRegistry registry) {
                  registry.addInterceptor(requestBodyInterceptor).addPathPatterns("/api/*");  // 根据需要配置路径
              }
          }
          

          4. 总结

          通过以上步骤,你可以实现一个能够多次读取 HttpServletRequest.getInputStream() 数据的机制。基本思路是:

          • 创建一个 HttpServletRequestWrapper 类来缓存请求体内容;
          • 通过 Filter 来包装 HttpServletRequest
          • 在 HandlerInterceptor 中获取请求体并进行处理。

          这样,无论在拦截器还是后续的参数解析过程中,都会能够多次访问请求体数据。

          方式二:拦截器中使用包装类ContentCachingRequestWrapper

          在 HTTP 请求中,HttpServletRequest 的请求体(POST 请求中的 JSON 数据)是一次性的流,读取完一次之后,如果没有特殊处理,就不能再次读取它。

          ContentCachingRequestWrapper 是 Spring 框架提供的一个包装类,它的作用是“包装”原始的 HttpServletRequest 对象,使得请求体内容可以被多次读取。

          使用ContentCachingRequestWrapper ,省去方法一中创建的 HttpServletRequestWrapper和RequestWrapperFilter。

          import org.springframework.web.servlet.HandlerInterceptor;
          
          import javax.servlet.http.HttpServletRequest;
          import javax.servlet.http.HttpServletResponse;
          import java.io.IOException;
          
          public class RequestBodyInterceptor implements HandlerInterceptor {
          
              @Override
              public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
                      throws IOException {
          		if (HttpMethod.POST.matches(request.getMethod())) {
           			// 包装请求,使其可以多次读取请求体
                      ContentCachingRequestWrapper wrappedRequest = new ContentCachingRequestWrapper(request);
          
          			// 读取请求体,明确指定字符编码为 UTF-8
          			String requestBody = new String(wrappedRequest.getContentAsByteArray(), "UTF-8");
          
                      // 打印或处理请求体内容
                      System.out.println("Request Body: " + requestBody);
          
                      // 将请求体转换成 Java 对象
                      MyRequestObject myRequestObject = new ObjectMapper().readValue(requestBody, MyRequestObject.class);
                      System.out.println("Parsed Java Object: " + myRequestObject);
                  }
          
                  return true; // 返回 true,表示继续处理请求
              }
          }
          

          总结

          ContentCachingRequestWrapper 是一种非常有用的工具,允许缓存并多次读取请求体内容,尤其需要在拦截器中处理请求体数据时,它非常有效。

          到此这篇关于如何使用拦截器获取请求的入参并将其转化为Java对象的文章就介绍到这了,更多相关拦截器获取请求入参并转化Java对象内容请搜索3672js教程以前的文章或继续浏览下面的相关文章希望大家以后多多支持3672js教程!

          您可能感兴趣的文章:
          • SpringBoot拦截器如何获取http请求参数
          • axios全局请求参数设置,请求及返回拦截器的方法
          • Struts2之Action接收请求参数和拦截器详解
          • Spring RestTemplate如何利用拦截器打印请求参数和返回状态
          相关栏目:

          用户点评