【JAVA】拦截请求参数并设置请求头

    业务中遇到了一些场景,需要对请求做统一拦截,用请求参数计算新的变量设置到请求头中。

    以下分别用Filter和Interceptor两种方式实现,(建议使用Filter的方法,因为Interceptor的方法仅仅对Post和GET方法有效,并不支持PUT等其他方法,主要原因是因为HttpServletRequest接口的实现类不同,以下仅支持了POST和GET方法)

场景:

我们有两类用户,一类用户的请求中头部header中有用户名字参数(userName),另一类用户的请求并无请求header,但是请求参数requestParam中有userId参数,我们可以通过userId查库等计算出userName。

需求:

我们想统一两类用户,使请求达到我们的controller层的时候头部统一都有userName

具体:

以下的两种方式实现了这个需求,controller层获取userName,将优先使用从requestParam中的userId计算出来的userName。

 

1、Filter实现
 

package com.paomo.filter;

import org.springframework.context.annotation.Configuration;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.IOException;
import java.util.*;

@Configuration
@WebFilter(value = "/*")
public class ParamFilter implements Filter {

    private static final String USER_PARAM = "userId";
    
    private static final String USER_HEADER = "userName";

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;
        HeaderMapRequestWrapper requestWrapper = new HeaderMapRequestWrapper(req);
        String userId = request.getParameter(USER_PARAM);


        //依据你自己的业务通过userId获取userName
        String userName = getUserNameById(userId);

        requestWrapper.addHeader(USER_HEADER, userName);
        chain.doFilter(requestWrapper, response);
    }

    @Override
    public void destroy() {
    }

    public class HeaderMapRequestWrapper extends HttpServletRequestWrapper {

        public HeaderMapRequestWrapper(HttpServletRequest request) {
            super(request);
        }

        private Map<String, String> headerMap = new HashMap<String, String>();

        public void addHeader(String name, String value) {
            headerMap.put(name, value);
        }

        @Override
        public String getHeader(String name) {
            String headerValue = super.getHeader(name);
            if (headerMap.containsKey(name)) {
                headerValue = headerMap.get(name);
            }
            return headerValue;
        }

        @Override
        public Enumeration<String> getHeaderNames() {
            List<String> names = Collections.list(super.getHeaderNames());
            for (String name : headerMap.keySet()) {
                names.add(name);
            }
            return Collections.enumeration(names);
        }

        @Override
        public Enumeration<String> getHeaders(String name) {
            List<String> values = Collections.list(super.getHeaders(name));
            if (headerMap.containsKey(name)) {
                values.add(headerMap.get(name));
            }
            return Collections.enumeration(values);
        }
    }

}

2、Interceptor实现

@Component
public class ParamInterceptor extends HandlerInterceptorAdapter {

    private static final Logger logger = LoggerFactory.getLogger(ParamInterceptor.class);

    private static final String USER_PARAM = "userId";

    private static final String USER_HEADER = "userName";

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (handler instanceof HandlerMethod) {
            String userId = request.getParameter(USER_PARAM);
            if (StringUtils.isNotBlank(userId)) {

                //依据你自己的业务通过userId获取userName
                String userName = getUserNameById(userId);

                reflectSetHeader(request, USER_HEADER, userName);
            }
        }
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        super.postHandle(request, response, handler, modelAndView);
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        super.afterCompletion(request, response, handler, ex);
    }

    private void reflectSetHeader(HttpServletRequest request, String key, String value){
        Class<? extends HttpServletRequest> requestClass = request.getClass();
        logger.info("request实现类={}", requestClass.getName());
        try {
            Field request1 = requestClass.getDeclaredField("request");
            request1.setAccessible(true);
            Object o = request1.get(request);
            Field coyoteRequest = o.getClass().getDeclaredField("coyoteRequest");
            coyoteRequest.setAccessible(true);
            Object o1 = coyoteRequest.get(o);
            Field headers = o1.getClass().getDeclaredField("headers");
            headers.setAccessible(true);
            MimeHeaders o2 = (MimeHeaders)headers.get(o1);
            o2.removeHeader(key);
            o2.addValue(key).setString(value);
        } catch (Exception e) {
            logger.info("reflect set header error {}", e);
        }
    }
}

 

<think>我们正在讨论如何在Java中通过拦截器获取请求参数。根据引用内容,主要涉及在SpringBoot或SpringMVC中使用拦截器获取HTTP请求参数的方法。关键点:1.拦截器需要实现HandlerInterceptor接口,重写preHandle方法。2.在preHandle方法中,我们可以通过HttpServletRequest对象获取请求参数。3.获取参数的方式有两种:一种是获取URL中的查询参数(即?后的参数),另一种是获取请求体(如POST请求的表单数据或JSON)。4.需要注意的是,请求体(InputStream)只能被读取一次,如果重复读取可能会导致问题(如引用[3]中提到的Requiredrequestbodyismissing错误)。根据引用[1]和[2],我们可以通过以下方式获取参数:-对于查询参数:使用request.getParameterMap()或request.getParameterNames()等。-对于请求体:如果是表单数据,同样可以使用request.getParameter系列方法;如果是JSON等数据,则需要从InputStream读取,但要注意不能重复读取。引用[3]提醒我们,在拦截器中读取请求体时,如果后续还需要使用(例如在Controller中),我们需要将请求体内容缓存下来,以便多次读取。因此,我们可以编写一个拦截器,在preHandle方法中获取请求参数打印或处理。下面是一个示例:示例代码:我们将创建一个拦截器,用于获取GET请求的查询参数和POST请求的表单参数。对于JSON等请求体,我们可以通过包装HttpServletRequest来实现多次读取(使用ContentCachingRequestWrapper)。但是注意:在拦截器中直接读取请求体可能会影响性能,且需要谨慎处理。这里我们主要展示如何获取查询参数和表单参数,对于JSON请求体,我们使用缓存的方式。步骤:1.创建拦截器类,实现HandlerInterceptor接口。2.在preHandle方法中,使用HttpServletRequest获取参数。3.注册拦截器。我们先展示一个简单的版本,只获取查询参数和表单参数(不处理JSON请求体):```javaimportjavax.servlet.http.HttpServletRequest;importjavax.servlet.http.HttpServletResponse;importorg.springframework.web.servlet.HandlerInterceptor;publicclassParamsInterceptorimplementsHandlerInterceptor{@OverridepublicbooleanpreHandle(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler)throwsException{//获取所有请求参数(包括查询参数和表单参数)System.out.println("请求参数:");request.getParameterMap().forEach((key,value)->{System.out.println(key+":"+String.join(",",value));});returntrue;}}```但是,上述代码无法获取请求体中的JSON数据。为了能够获取JSON请求体,我们需要将请求包装成可缓存的请求,然后读取缓存的内容。我们可以使用ContentCachingRequestWrapper来缓存请求体。但是注意,这个类在Spring中提供,我们需要在拦截器中对原始请求进行包装。然而,在拦截器中,我们通常不会修改请求对象。因此,我们可以通过过滤器(Filter)来包装请求,然后在拦截器中使用包装后的请求。但是,问题要求使用拦截器,所以我们这里有两种选择:1.在拦截器中,如果请求是JSON类型,我们可以通过读取InputStream来获取请求体,但这样会消耗掉InputStream,导致后续无法读取。所以不推荐。2.使用ContentCachingRequestWrapper,但需要在过滤器中进行包装,这样拦截器就可以使用包装后的请求。这里我们假设已经有一个过滤器做了包装。由于引用[3]提到了读取两次的问题,我们采用第二种方式,即通过过滤器包装请求。因此,我们首先创建一个过滤器,将请求包装为ContentCachingRequestWrapper:```javaimportjavax.servlet.*;importjavax.servlet.http.HttpServletRequest;importjava.io.IOException;importorg.springframework.web.util.ContentCachingRequestWrapper;publicclassCachingRequestBodyFilterimplementsFilter{@OverridepublicvoiddoFilter(ServletRequestrequest,ServletResponseresponse,FilterChainchain)throwsIOException,ServletException{HttpServletRequesthttpServletRequest=(HttpServletRequest)request;ContentCachingRequestWrapperwrappedRequest=newContentCachingRequestWrapper(httpServletRequest);chain.doFilter(wrappedRequest,response);//将包装后的请求传递下去}}```然后,在拦截器中,我们就可以从ContentCachingRequestWrapper中获取请求体:```javaimportorg.springframework.web.servlet.HandlerInterceptor;importorg.springframework.web.util.ContentCachingRequestWrapper;importjavax.servlet.http.HttpServletRequest;importjavax.servlet.http.HttpServletResponse;importjava.nio.charset.StandardCharsets;publicclassParamsInterceptorimplementsHandlerInterceptor{@OverridepublicbooleanpreHandle(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler)throwsException{//如果请求已经被包装为ContentCachingRequestWrapperif(requestinstanceofContentCachingRequestWrapper){ContentCachingRequestWrapperwrappedRequest=(ContentCachingRequestWrapper)request;//获取查询参数System.out.println("QueryStringParameters:");request.getParameterMap().forEach((key,value)->{System.out.println(key+":"+String.join(",",value));});//获取请求体byte[]contentAsByteArray=wrappedRequest.getContentAsByteArray();if(contentAsByteArray.length>0){Stringbody=newString(contentAsByteArray,StandardCharsets.UTF_8);System.out.println("RequestBody:"+body);}}else{//如果没有包装,只获取查询参数和表单参数System.out.println("RequestParameters:");request.getParameterMap().forEach((key,value)->{System.out.println(key+":"+String.join(",",value));});}returntrue;}}```最后,我们需要注册过滤器和拦截器。在SpringBoot中注册过滤器:```javaimportorg.springframework.boot.web.servlet.FilterRegistrationBean;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;@ConfigurationpublicclassFilterConfig{@BeanpublicFilterRegistrationBean<CachingRequestBodyFilter>filterRegistrationBean(){FilterRegistrationBean<CachingRequestBodyFilter>registrationBean=newFilterRegistrationBean<>();registrationBean.setFilter(newCachingRequestBodyFilter());registrationBean.addUrlPatterns("/*");returnregistrationBean;}}```注册拦截器:```javaimportorg.springframework.context.annotation.Configuration;importorg.springframework.web.servlet.config.annotation.InterceptorRegistry;importorg.springframework.web.servlet.config.annotation.WebMvcConfigurer;@ConfigurationpublicclassInterceptorConfigimplementsWebMvcConfigurer{@OverridepublicvoidaddInterceptors(InterceptorRegistryregistry){registry.addInterceptor(newParamsInterceptor());}}```这样,我们就可以在拦截器获取请求参数(包括查询参数请求体)了。注意:ContentCachingRequestWrapper是Spring提供的,所以需要引入spring-web模块。总结:1.使用过滤器将请求包装为ContentCachingRequestWrapper,以便多次读取请求体。2.在拦截器中,通过ContentCachingRequestWrapper的getContentAsByteArray方法获取请求体内容。3.同时,通过getParameterMap获取查询参数和表单参数。但是,需要注意的是,getParameterMap只能获取到查询参数和表单参数(application/x-www-form-urlencoded或multipart/form-data),对于JSON(application/json)等,getParameterMap是获取不到的,所以需要从请求体中读取。因此,我们在拦截器中同时打印了查询参数请求体,这样就能覆盖大多数情况。引用说明:引用[1]和[2]提到了在拦截器获取请求参数,引用[3]提醒了请求体只能读取一次的问题,因此我们采用包装请求的方式解决。注意:这个示例可能会影响性能,因为缓存请求体会消耗内存,特别是大文件上传时。因此,在实际应用中,需要根据情况决定是否缓存请求体。以上就是在Java中通过拦截器获取请求参数的示例。§§相关问题§§1.拦截器和过滤器有什么区别?2.除了拦截器,还有哪些方式可以获取HTTP请求参数?3.如何解决拦截器中读取请求体导致后续Controller无法读取的问题?</think>在 Java 中通过拦截器获取 HTTP 请求参数的实现步骤如下,结合 Spring Boot 示例代码说明: ### 实现步骤 1. **创建拦截器类**:实现 `HandlerInterceptor` 接口 2. **重写 `preHandle` 方法**:在请求处理获取参数 3. **处理不同参数类型**: - **查询参数**:`request.getParameterMap()` - **表单参数**:`request.getParameter()` - **JSON 请求体**:使用缓存请求包装器 4. **注册拦截器**:配置拦截规则 ### 完整示例代码 ```java import org.springframework.web.servlet.HandlerInterceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.Map; public class ParamsInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { // 1. 获取URL查询参数 (?key=value) Map<String, String[]> queryParams = request.getParameterMap(); System.out.println("URL参数: " + queryParams); // 2. 获取表单参数 (application/x-www-form-urlencoded) String formParam = request.getParameter("formKey"); System.out.println("表单参数: " + formParam); // 3. 获取JSON请求体 (application/json) if ("POST".equalsIgnoreCase(request.getMethod()) && request.getContentType() != null && request.getContentType().contains("application/json")) { // 使用缓存包装器解决多次读取问题[^3] CachedBodyHttpServletRequest cachedRequest = new CachedBodyHttpServletRequest(request); try { String jsonBody = new String( cachedRequest.getInputStream().readAllBytes(), StandardCharsets.UTF_8 ); System.out.println("JSON请求体: " + jsonBody); } catch (IOException e) { e.printStackTrace(); } return true; // 继续执行后续处理 } return true; } } ``` ### 关键点说明 1. **请求体缓存包装器**(解决多次读取问题) ```java import javax.servlet.*; import javax.servlet.http.*; import java.io.*; public class CachedBodyHttpServletRequest extends HttpServletRequestWrapper { private byte[] cachedBody; public CachedBodyHttpServletRequest(HttpServletRequest request) throws IOException { super(request); this.cachedBody = request.getInputStream().readAllBytes(); } @Override public ServletInputStream getInputStream() { return new CachedBodyServletInputStream(this.cachedBody); } // 内部实现 static class CachedBodyServletInputStream extends ServletInputStream { private final ByteArrayInputStream buffer; public CachedBodyServletInputStream(byte[] body) { this.buffer = new ByteArrayInputStream(body); } // 实现必要方法... } } ``` 2. **注册拦截器** ```java 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 { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new ParamsInterceptor()) .addPathPatterns("/api/**"); // 拦截指定路径 } } ``` ### 注意事项 1. **请求体只能读取一次**:未缓存时读取后 InputStream 会关闭,导致后续处理报错 `Required request body is missing`[^3] 2. **性能影响**:大文件上传时谨慎使用缓存 3. **参数类型判断**:根据 `Content-Type` 头区分表单/JSON 4. **拦截器与过滤器区别**:拦截器Controller 执行,过滤器在更早阶段处理 > 最佳实践:对于 JSON 请求体,建议在过滤器层进行缓存包装,再在拦截器获取内容[^2][^3]。
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值