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.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。只有相熟根本的反射,实现对申请头的增删,都很简略。


原文:https://springboot.io/t/topic...