对HttpServletRequest中的Header进行增删实现过程

 更新时间:2025年12月18日 10:59:36   作者:_派大星  
文章介绍了如何通过反射来修改和删除HttpServletRequest中的Header,以实现对外部请求数据的自定义处理,通过示例代码和分析,展示了如何在Tomcat和Undertow容器中实现这一功能

HttpServletRequest 没有提供修改/删除的 Api

HttpServletRequest中定义的对 Header 的操作全是只读,没有修改。

代码实现如下:

public interface HttpServletRequest extends ServletRequest {
    ...
    public long getDateHeader(String name);
    public String getHeader(String name);
    public Enumeration<String> getHeaders(String name);
    public Enumeration<String> getHeaderNames();
    public int getIntHeader(String name);
    ...
}

HttpServletRequest 只是一个接口,实现由 Servlet 容器提供。

不管是任何容器,实现类,肯定是要把请求的 Header 存储在某个地方,于是可以通过反射来对存储Header的容器进行增删。

先定义一个测试的 Controller

这个 Controller 很简单,把客户端的所有 Header,以 JSON 形似响应给客户端。

import java.util.ArrayList;
import java.util.Enumeration;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/demo")
public class DemoController {

	// 遍历所有请求Header,响应给客户端。 map<string, string[]>
	@GetMapping
	public Object demo (HttpServletRequest request) {
		Map<String, List<String>> headers = new LinkedHashMap<>();
		Enumeration<String> nameEnumeration = request.getHeaderNames();
		while (nameEnumeration.hasMoreElements()) {
			String name = nameEnumeration.nextElement();
			List<String> values = headers.get(name);
			if (values == null) {
				values = new ArrayList<>();
				headers.put(name, values);
			}
			Enumeration<String> valueEnumeration = request.getHeaders(name);
			while (valueEnumeration.hasMoreElements()) {
				values.add(valueEnumeration.nextElement());
			}
		}
		return headers;
	}
}

使用 Tomcat 作为容器

Tomcat 对 HttpServletRequest 的实现

Tomcat 使用了外观模式(Facade),这个实现稍微有一点点复杂

org.apache.catalina.connector.RequestFacade
|-org.apache.catalina.connector.Request
  |-org.apache.coyote.Request
    |-org.apache.tomcat.util.http.MimeHeaders

首先是 org.apache.catalina.connector.RequestFacade 实现,它有一个org.apache.catalina.connector.Request 的对象。

这个对象又有一个org.apache.coyote.Request的对象,这个对象又有一个org.apache.tomcat.util.http.MimeHeaders 字段,它就是存储了客户端请求头的容器,只要通过反射获取到这个MimeHeaders,对它进行修改即可。

org.apache.catalina.connector.RequestFacade

public class RequestFacade implements HttpServletRequest {
    protected org.apache.catalina.connector.Request request = null;
    ...
}

org.apache.catalina.connector.Request

public class Request implements HttpServletRequest {
    protected org.apache.coyote.Request coyoteRequest;
    ...
}

org.apache.coyote.Request coyoteRequest

public final class Request {
    private final org.apache.tomcat.util.http.MimeHeaders headers = new MimeHeaders();
}

在 Filter 中通过反射对请求 Header 进行增删

假设的场景是,需要对请求 Header 统一添加一个x-request-id,通过这个 ID 来从日志中定位每一个请求。

import java.io.IOException;
import java.lang.reflect.Field;
import java.util.UUID;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.RequestFacade;
import org.apache.tomcat.util.http.MimeHeaders;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.ReflectionUtils;



@WebFilter(urlPatterns = "/*")
@Component
@Order(-999)
public class RequestIdGenFilter extends HttpFilter {

	/**
	 *
	 */
	private static final long serialVersionUID = 1787347739651657706L;

	@Override
	protected void doFilter(HttpServletRequest req, HttpServletResponse res, FilterChain chain) throws IOException, ServletException {
		try {
			// 从 RequestFacade 中获取 org.apache.catalina.connector.Request
			Field connectorField = ReflectionUtils.findField(RequestFacade.class, "request", Request.class);
			connectorField.setAccessible(true);
			Request connectorRequest = (Request) connectorField.get(req);

			// 从 org.apache.catalina.connector.Request 中获取 org.apache.coyote.Request
			Field coyoteField = ReflectionUtils.findField(Request.class, "coyoteRequest", org.apache.coyote.Request.class);
			coyoteField.setAccessible(true);
			org.apache.coyote.Request coyoteRequest = (org.apache.coyote.Request) coyoteField.get(connectorRequest);

			// 从 org.apache.coyote.Request 中获取 MimeHeaders
			Field mimeHeadersField =  ReflectionUtils.findField(org.apache.coyote.Request.class, "headers", MimeHeaders.class);
			mimeHeadersField.setAccessible(true);
			MimeHeaders mimeHeaders =  (MimeHeaders) mimeHeadersField.get(coyoteRequest);

			this.mineHeadersHandle(mimeHeaders);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
		super.doFilter(req, res, chain);
	}

	protected void mineHeadersHandle (MimeHeaders mimeHeaders) {
		// 添加一个Header,随机生成请求ID
		mimeHeaders.addValue("x-request-id").setString(UUID.randomUUID().toString());;
		// 移除一个header
		mimeHeaders.removeHeader("User-Agent");
	}
}

请求 Controller 获取响应结果

可以看到成功添加了x-request-id header,并且删除了User-Agent header。

使用 Undertow 作为容器

越来越多人使用 Undertow 作为 Servlet 容器,据说性能比 Tomcat 要好很多

SpringBoot 替换 Tomcat 为 Undertow

只需要把spring-boot-starter-web中的spring-boot-starter-tomcat排除,然后手动添加spring-boot-starter-undertow即可

<dependencies>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
      <exclusion>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-tomcat</artifactId>
      </exclusion>
    </exclusions>
  </dependency>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-undertow</artifactId>
  </dependency>
</dependencies>

Undertow 中的HttpServletRequest实现

它的实现就比较简单,结构如下:

io.undertow.servlet.spec.HttpServletRequestImpl
|-io.undertow.server.HttpServerExchange
  |-io.undertow.util.HeaderMap
  • io.undertow.servlet.spec.HttpServletRequestImpl实现类中有一个属性对象
  • io.undertow.server.HttpServerExchange,这个属性对象又包含了一个io.undertow.util.HeaderMapHeaderMap就是请求 Header 的存储容器,反射获取它就行。

io.undertow.servlet.spec.HttpServletRequestImpl

public final class HttpServletRequestImpl implements HttpServletRequest {
    private final io.undertow.server.HttpServerExchange exchange;
}

io.undertow.server.HttpServerExchange

public final class HttpServerExchange extends AbstractAttachable {
    private final HeaderMap requestHeaders;
}

在 Filter 中通过反射对请求 Header 进行增删

import java.io.IOException;
import java.lang.reflect.Field;
import java.util.UUID;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.ReflectionUtils;

import io.undertow.server.HttpServerExchange;
import io.undertow.servlet.spec.HttpServletRequestImpl;
import io.undertow.util.HeaderMap;
import io.undertow.util.HttpString;



@WebFilter(urlPatterns = "/*")
@Component
@Order(-999)
public class RequestIdGenFilter extends HttpFilter {

	/**
	 *
	 */
	private static final long serialVersionUID = 1787347739651657706L;

	@Override
	protected void doFilter(HttpServletRequest req, HttpServletResponse res, FilterChain chain) throws IOException, ServletException {
		try {
			// 从HttpServletRequestImpl中获取HttpServerExchange
			Field exchangeField = ReflectionUtils.findField(HttpServletRequestImpl.class, "exchange", HttpServerExchange.class);
			exchangeField.setAccessible(true);
			HttpServerExchange httpServerExchange = (HttpServerExchange) exchangeField.get(req);

			// 从HttpServerExchange中获取HeaderMap
			Field headerMapField = ReflectionUtils.findField(HttpServerExchange.class, "requestHeaders", HeaderMap.class);
			headerMapField.setAccessible(true);

			HeaderMap requestHeaderMap = (HeaderMap) headerMapField.get(httpServerExchange);
			this.handleRequestHeaderMap(requestHeaderMap);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
		super.doFilter(req, res, chain);
	}

	private void handleRequestHeaderMap(HeaderMap requestHeaderMap) {
		// 添加Header
		requestHeaderMap.add(new HttpString("x-request-id"), UUID.randomUUID().toString());
		// 移除Header
		requestHeaderMap.remove("User-Agent");
	}
}

请求 Controller 获取结果

总结

还有其他的 Servlet 容器,例如 Jetty。只要熟悉基本的反射,实现对请求头的增删,都很简单。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • springboot中的controller参数映射问题小结

    springboot中的controller参数映射问题小结

    这篇文章主要介绍了springboot中的controller参数映射问题小结,本文通过实例代码给大家介绍的非常详细,感兴趣的朋友一起看看吧
    2024-12-12
  • 详解Mybatis中javaType和ofType的区别

    详解Mybatis中javaType和ofType的区别

    本文主要介绍了详解Mybatis中javaType和ofType的区别,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-05-05
  • Spring使用@Autowired注解实现自动装配方式

    Spring使用@Autowired注解实现自动装配方式

    这篇文章主要介绍了Spring使用@Autowired注解实现自动装配方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-09-09
  • SpringBoot+SseEmitter和Vue3+EventSource实现实时数据推送

    SpringBoot+SseEmitter和Vue3+EventSource实现实时数据推送

    本文主要介绍了SpringBoot+SseEmitter和Vue3+EventSource实现实时数据推送,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2025-03-03
  • 详解Java在redis中进行对象的缓存

    详解Java在redis中进行对象的缓存

    这篇文章主要介绍了Java在redis中进行对象的缓存,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-03-03
  • Java并发工具之Exchanger线程间交换数据详解

    Java并发工具之Exchanger线程间交换数据详解

    这篇文章主要介绍了Java并发工具之Exchanger线程间交换数据详解,Exchanger是一个用于线程间协作的工具类,Exchanger用于进行线程间的数据交 换,它提供一个同步点,在这个同步点,两个线程可以交换彼此的数据,需要的朋友可以参考下
    2023-12-12
  • Java本地部署DeepSeek的详细步骤

    Java本地部署DeepSeek的详细步骤

    本文主要介绍了使用Java在本地部署DeepSeek的详细步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2025-10-10
  • Java 实战项目之CRM客户管理系统的实现流程

    Java 实战项目之CRM客户管理系统的实现流程

    读万卷书不如行万里路,只学书上的理论是远远不够的,只有在实战中才能获得能力的提升,本篇文章手把手带你用java+SSM+jsp+mysql+maven实现一个CRM客户管理系统,大家可以在过程中查缺补漏,提升水平
    2021-11-11
  • 一步步教你搭建Scala开发环境(非常详细!)

    一步步教你搭建Scala开发环境(非常详细!)

    Scala是一门基于jvm的函数式的面向对象编程语言,拥有比java更加简洁的语法,下面这篇文章主要给大家介绍了关于搭建Scala开发环境的相关资料,文中通过图文介绍的非常详细,需要的朋友可以参考下
    2022-04-04
  • Java调用Python服务的三种实现过程

    Java调用Python服务的三种实现过程

    这篇文章主要介绍了Java调用Python服务的三种实现过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2025-10-10

最新评论