HttpServletRequest does not provide a modify/delete Api
The operations on headers defined in HttpServletRequest are read-only and not modified.
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(a);
public int getIntHeader(String name); . }Copy the code
HttpServletRequest is simply an interface whose implementation is provided by the Servlet container. The implementation class, regardless of the container, must store the requested Header somewhere, so it can add or delete the container containing the Header by reflection.
Define a test Controller first
This Controller is very simple, and it sends all the headers back to the client in JSON form.
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 {
// Iterate over all request headers and respond to the client. map
,>
@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()); }}returnheaders; }}Copy the code
Use Tomcat as the container
Tomcat implementation of HttpServletRequest
Tomcat uses a Facade, which is a slightly more complex implementation
org.apache.catalina.connector.RequestFacade
|-org.apache.catalina.connector.Request
|-org.apache.coyote.Request
|-org.apache.tomcat.util.http.MimeHeaders
Copy the code
First is the org. Apache. Catalina. Connector. RequestFacade implementation, it has a org. Apache. Catalina. The Request object. This object has a org. Apache. Coyote. The Request object, the object has a org.. Apache tomcat. Util. HTTP. MimeHeaders fields, it is the storage of client Request header container, Just get the MimeHeaders by reflection and modify it.
org.apache.catalina.connector.RequestFacade
public class RequestFacade implements HttpServletRequest {
protected org.apache.catalina.connector.Request request = null; . }Copy the code
org.apache.catalina.connector.Request
public class Request implements HttpServletRequest {
protectedorg.apache.coyote.Request coyoteRequest; . }Copy the code
org.apache.coyote.Request coyoteRequest
public final class Request {
private final org.apache.tomcat.util.http.MimeHeaders headers = new MimeHeaders();
}
Copy the code
Add or delete request headers by reflection in Filter
The hypothetical scenario is that you need to add a uniform X-request-ID to the request Header to locate each request from the log.
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 {
/ / get from RequestFacade org. Apache. Catalina. The Request
Field connectorField = ReflectionUtils.findField(RequestFacade.class, "request", Request.class);
connectorField.setAccessible(true);
Request connectorRequest = (Request) connectorField.get(req);
/ / from org. Apache. Catalina. The Request gets 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);
/ / from org. Apache. Coyote. Gets MimeHeaders Request
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) {
// Add a Header to generate a random request ID
mimeHeaders.addValue("x-request-id").setString(UUID.randomUUID().toString());;
// Remove a header
mimeHeaders.removeHeader("User-Agent"); }}Copy the code
Request the Controller to obtain the response result
You can see that it was successfully addedx-request-id
Header, and deletedUser-Agent
The header.
The default Servlet container for SpringBoot is Tomcat
Use Undertow as the container
Undertow is increasingly being used as a Servlet container and is said to perform much better than Tomcat
SpringBoot replaces Tomcat with Undertow
You only need to exclude spring-boot-starter-tomcat from spring-boot-starter-web and manually add 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>
Copy the code
In the UndertowHttpServletRequest
implementation
Its implementation is relatively simple
io.undertow.servlet.spec.HttpServletRequestImpl
|-io.undertow.server.HttpServerExchange
|-io.undertow.util.HeaderMap
Copy the code
IO. Undertow. Servlet. Spec. HttpServletRequestImpl implementation class has a properties object. IO undertow. Server HttpServerExchange, This attribute object contains an IO. Undertow. Util. HeaderMap, HeaderMap is request Header storage containers, reflection to obtain it.
io.undertow.servlet.spec.HttpServletRequestImpl
public final class HttpServletRequestImpl implements HttpServletRequest {
private final io.undertow.server.HttpServerExchange exchange;
}
Copy the code
io.undertow.server.HttpServerExchange
public final class HttpServerExchange extends AbstractAttachable {
private final HeaderMap requestHeaders;
}
Copy the code
Add or delete request headers by reflection in Filter
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 {
// Get HttpServerExchange from HttpServletRequestImpl
Field exchangeField = ReflectionUtils.findField(HttpServletRequestImpl.class, "exchange", HttpServerExchange.class);
exchangeField.setAccessible(true);
HttpServerExchange httpServerExchange = (HttpServerExchange) exchangeField.get(req);
// Get HeaderMap from HttpServerExchange
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) {
/ / add the Header
requestHeaderMap.add(new HttpString("x-request-id"), UUID.randomUUID().toString());
/ / remove the Header
requestHeaderMap.remove("User-Agent"); }}Copy the code
Request the Controller to get the result
The last
There are other Servlet containers, such as Jetty. Adding and removing headers is easy, as long as you are familiar with basic reflection.
Original text: springboot. IO/topic / 364 / t…