当前位置: 代码网 > 服务器>网络>https > 对HttpServletRequest中的Header进行增删实现过程

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

2025年12月18日 https 我要评论
httpservletrequest 没有提供修改/删除的 apihttpservletrequest中定义的对 header 的操作全是只读,没有修改。代码实现如下:public interface

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。只要熟悉基本的反射,实现对请求头的增删,都很简单。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com