共计 7646 个字符,预计需要花费 20 分钟才能阅读完成。
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。
SpringBoot 默认的 Servlet 容器就是 Tomcat
应用 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.HeaderMap
,HeaderMap
就是申请 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。只有相熟根本的反射,实现对申请头的增删,都很简略。
原文:https://springboot.io/t/topic…