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

 更新时间:2025年02月11日 08:52:57   作者:m0_69893292  
这篇文章主要介绍了如何使用拦截器获取请求的入参并将其转化为Java对象的相关资料,文中介绍了两种实现的方法,并给出了详细的代码示例,需要的朋友可以参考下

在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对象内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • java批量下载生成zip压缩包的思路详解

    java批量下载生成zip压缩包的思路详解

    这篇文章主要介绍了java批量下载生成zip压缩包的思路详解,设计思路大概是本地先创建一个zip文件,将批量下载的文件依次放入zip文件中,将zip文件返回给前端,本文结合实例代码讲解的非常详细,需要的朋友可以参考下
    2024-01-01
  • idea不能自动补全yml配置文件的原因分析

    idea不能自动补全yml配置文件的原因分析

    这篇文章主要介绍了idea不能自动补全yml配置文件的原因,通过添加yml文件为配置文件能够很快的解决,具体解决步骤跟随小编一起通过本文学习下吧
    2021-06-06
  • Springboot实现异步任务线程池代码实例

    Springboot实现异步任务线程池代码实例

    这篇文章主要介绍了Springboot实现异步任务线程池代码实例,异步任务线程池是一种用于处理异步任务的机制,它可以提高程序的并发性能和响应速度,通过将任务提交给线程池,线程池会自动管理线程的创建和销毁,从而避免了频繁创建和销毁线程的开销,需要的朋友可以参考下
    2023-10-10
  • 浅析Spring工厂的反射和配置文件

    浅析Spring工厂的反射和配置文件

    这篇文章主要介绍了浅析Spring工厂的反射和配置文件,spring是通过反射和配置文件的方式来获取 JavaBean 对象,需要的朋友可以参考下
    2023-04-04
  • Java开发支付宝PC支付完整版

    Java开发支付宝PC支付完整版

    这篇文章主要介绍了Java开发支付宝PC支付完整版,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-08-08
  • Spring Boot 简介(入门篇)

    Spring Boot 简介(入门篇)

    Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。下面通过本文给大家介绍spring boot相关知识,需要的的朋友参考下吧
    2017-04-04
  • Mybatis返回类型为Map时遇到的类型转化的异常问题

    Mybatis返回类型为Map时遇到的类型转化的异常问题

    这篇文章主要介绍了Mybatis返回类型为Map时遇到的类型转化的异常问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-12-12
  • 使用Netty解码自定义通信协议方式

    使用Netty解码自定义通信协议方式

    这篇文章主要介绍了使用Netty解码自定义通信协议方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2025-05-05
  • Java中双大括号初始化的理解与使用

    Java中双大括号初始化的理解与使用

    最近重读Java 编程思想,读到有关实例化代码块儿的内容,使我对于使用两个大括号进行初始化有了更深的理解,下面这篇文章主要给大家介绍了关于Java中双大括号初始化的理解与使用的相关资料,需要的朋友可以参考下
    2022-06-06
  • java捕获AOP级别的异常并将其传递到Controller层

    java捕获AOP级别的异常并将其传递到Controller层

    如何在一个现代的Java应用中,捕获AOP(面向切面编程)级别的异常,并将这些异常传递到Controller层进行合适的处理,异常处理在构建可靠的应用程序中起着关键作用,而AOP则可以帮助我们更好地管理和组织代码,我们将深入研究如何结合AOP和异常处理来构建健壮的应用
    2023-09-09

最新评论