對(duì)HttpServletRequest中的Header進(jìn)行增刪實(shí)現(xiàn)過程
HttpServletRequest 沒有提供修改/刪除的 Api
HttpServletRequest中定義的對(duì) Header 的操作全是只讀,沒有修改。
代碼實(shí)現(xiàn)如下:
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 只是一個(gè)接口,實(shí)現(xiàn)由 Servlet 容器提供。
不管是任何容器,實(shí)現(xiàn)類,肯定是要把請(qǐng)求的 Header 存儲(chǔ)在某個(gè)地方,于是可以通過反射來對(duì)存儲(chǔ)Header的容器進(jìn)行增刪。
先定義一個(gè)測(cè)試的 Controller
這個(gè) Controller 很簡單,把客戶端的所有 Header,以 JSON 形似響應(yīng)給客戶端。
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 {
// 遍歷所有請(qǐng)求Header,響應(yīng)給客戶端。 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 對(duì) HttpServletRequest 的實(shí)現(xiàn)
Tomcat 使用了外觀模式(Facade),這個(gè)實(shí)現(xiàn)稍微有一點(diǎn)點(diǎn)復(fù)雜
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 實(shí)現(xiàn),它有一個(gè)org.apache.catalina.connector.Request 的對(duì)象。
這個(gè)對(duì)象又有一個(gè)org.apache.coyote.Request的對(duì)象,這個(gè)對(duì)象又有一個(gè)org.apache.tomcat.util.http.MimeHeaders 字段,它就是存儲(chǔ)了客戶端請(qǐng)求頭的容器,只要通過反射獲取到這個(gè)MimeHeaders,對(duì)它進(jìn)行修改即可。
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 中通過反射對(duì)請(qǐng)求 Header 進(jìn)行增刪
假設(shè)的場(chǎng)景是,需要對(duì)請(qǐng)求 Header 統(tǒng)一添加一個(gè)x-request-id,通過這個(gè) ID 來從日志中定位每一個(gè)請(qǐng)求。
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) {
// 添加一個(gè)Header,隨機(jī)生成請(qǐng)求ID
mimeHeaders.addValue("x-request-id").setString(UUID.randomUUID().toString());;
// 移除一個(gè)header
mimeHeaders.removeHeader("User-Agent");
}
}
請(qǐng)求 Controller 獲取響應(yīng)結(jié)果

可以看到成功添加了x-request-id header,并且刪除了User-Agent header。
使用 Undertow 作為容器
越來越多人使用 Undertow 作為 Servlet 容器,據(jù)說性能比 Tomcat 要好很多
SpringBoot 替換 Tomcat 為 Undertow
只需要把spring-boot-starter-web中的spring-boot-starter-tomcat排除,然后手動(dòng)添加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實(shí)現(xiàn)
它的實(shí)現(xiàn)就比較簡單,結(jié)構(gòu)如下:
io.undertow.servlet.spec.HttpServletRequestImpl |-io.undertow.server.HttpServerExchange |-io.undertow.util.HeaderMap
io.undertow.servlet.spec.HttpServletRequestImpl實(shí)現(xiàn)類中有一個(gè)屬性對(duì)象io.undertow.server.HttpServerExchange,這個(gè)屬性對(duì)象又包含了一個(gè)io.undertow.util.HeaderMap,HeaderMap就是請(qǐng)求 Header 的存儲(chǔ)容器,反射獲取它就行。
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 中通過反射對(duì)請(qǐng)求 Header 進(jìn)行增刪
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");
}
}
請(qǐng)求 Controller 獲取結(jié)果

總結(jié)
還有其他的 Servlet 容器,例如 Jetty。只要熟悉基本的反射,實(shí)現(xiàn)對(duì)請(qǐng)求頭的增刪,都很簡單。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
springboot中的controller參數(shù)映射問題小結(jié)
這篇文章主要介紹了springboot中的controller參數(shù)映射問題小結(jié),本文通過實(shí)例代碼給大家介紹的非常詳細(xì),感興趣的朋友一起看看吧2024-12-12
詳解Mybatis中javaType和ofType的區(qū)別
本文主要介紹了詳解Mybatis中javaType和ofType的區(qū)別,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-05-05
Spring使用@Autowired注解實(shí)現(xiàn)自動(dòng)裝配方式
這篇文章主要介紹了Spring使用@Autowired注解實(shí)現(xiàn)自動(dòng)裝配方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-09-09
SpringBoot+SseEmitter和Vue3+EventSource實(shí)現(xiàn)實(shí)時(shí)數(shù)據(jù)推送
本文主要介紹了SpringBoot+SseEmitter和Vue3+EventSource實(shí)現(xiàn)實(shí)時(shí)數(shù)據(jù)推送,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2025-03-03
詳解Java在redis中進(jìn)行對(duì)象的緩存
這篇文章主要介紹了Java在redis中進(jìn)行對(duì)象的緩存,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-03-03
Java并發(fā)工具之Exchanger線程間交換數(shù)據(jù)詳解
這篇文章主要介紹了Java并發(fā)工具之Exchanger線程間交換數(shù)據(jù)詳解,Exchanger是一個(gè)用于線程間協(xié)作的工具類,Exchanger用于進(jìn)行線程間的數(shù)據(jù)交 換,它提供一個(gè)同步點(diǎn),在這個(gè)同步點(diǎn),兩個(gè)線程可以交換彼此的數(shù)據(jù),需要的朋友可以參考下2023-12-12
Java 實(shí)戰(zhàn)項(xiàng)目之CRM客戶管理系統(tǒng)的實(shí)現(xiàn)流程
讀萬卷書不如行萬里路,只學(xué)書上的理論是遠(yuǎn)遠(yuǎn)不夠的,只有在實(shí)戰(zhàn)中才能獲得能力的提升,本篇文章手把手帶你用java+SSM+jsp+mysql+maven實(shí)現(xiàn)一個(gè)CRM客戶管理系統(tǒng),大家可以在過程中查缺補(bǔ)漏,提升水平2021-11-11
一步步教你搭建Scala開發(fā)環(huán)境(非常詳細(xì)!)
Scala是一門基于jvm的函數(shù)式的面向?qū)ο缶幊陶Z言,擁有比java更加簡潔的語法,下面這篇文章主要給大家介紹了關(guān)于搭建Scala開發(fā)環(huán)境的相關(guān)資料,文中通過圖文介紹的非常詳細(xì),需要的朋友可以參考下2022-04-04
Java調(diào)用Python服務(wù)的三種實(shí)現(xiàn)過程
這篇文章主要介紹了Java調(diào)用Python服務(wù)的三種實(shí)現(xiàn)過程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2025-10-10

