原理
分析HttpServletRequest对象的数据结构,同时查看源码可知:
HttpServletRequest
的实现类RequestFacade
中有变量org.apache.catalina.connector.Request request
org.apache.catalina.connector.Request
类中有变量org.apache.coyote.Request coyoteRequest
org.apache.coyote.Request
中有变量MimeHeaders headers
,且提供方法public MimeHeaders getMimeHeaders()
- 而MimeHeaders就是head信息的容器,setValue(key).setString(value)即可添加指定head信息
- 所以,我们只需要通过反射获取
RequestFacade
中的org.apache.coyote.Request
,再调用getMimeHeadersd
得到MimeHeaders
即可。
代码
注意class别import错了。
package com.zhilei.common.util.http;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.RequestFacade;
import org.apache.tomcat.util.http.MimeHeaders;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Field;
/**
* @author chenzy
* @since 2020-11-30
*/
public class RequestUtil {
private RequestUtil(){}
public static boolean setHead(HttpServletRequest request, String key, String value){
try {
Field requestField = RequestFacade.class.getDeclaredField("request");
requestField.setAccessible(true);
Request requestReal= (Request) requestField.get(request);
Field coyoteRequestField =Request.class.getDeclaredField("coyoteRequest");
coyoteRequestField.setAccessible(true);
org.apache.coyote.Request coyoteRequest= (org.apache.coyote.Request) coyoteRequestField.get(requestReal);
MimeHeaders headers= coyoteRequest.getMimeHeaders();
headers.setValue(key).setString(value);
return true;
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return false;
}
}
<tomcat.version>9.0.29</tomcat.version>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<version>${tomcat.version}</version>
</dependency>
在springboot中的使用
当controller的参数转换错误时,setHead方法异常
- 分析:我的setHead方法是在HandlerInterceptor.preHandle中调用,当controller的参数转换错误时,preHandle会执行两次,第一次可以正常setHead,第二次报错,因为HttpServletRequest参数的实际类型是
org.apache.catalina.core.ApplicationHttpRequest
,此类不是RequestFacade的子类。但是调试发现ApplicationHttpRequest中有属性RequestFacade request,且发现此属性继承于ServletRequestWrapper。所以改进setHead方法。 - 代码:
package com.zhilei.common.util.http;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.RequestFacade;
import org.apache.tomcat.util.http.MimeHeaders;
import javax.servlet.ServletRequestWrapper;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Field;
/**
* @author chenzy
* @since 2020-11-30
*/
public class RequestUtil {
private RequestUtil() {
}
public static boolean setHead(HttpServletRequest request, String key, String value) {
if (request instanceof RequestFacade) {
return setHead((RequestFacade) request, key, value);
} else if (request instanceof ServletRequestWrapper) {
/*当controller参数转换错误时 request为org.apache.catalina.core.ApplicationHttpRequest,且此类继承ServletRequestWrapper,再次父类有字段request,为RequestFacade实例*/
return setHead(getRequestFacade(request), key, value);
}
return false;
}
private static RequestFacade getRequestFacade(HttpServletRequest request) {
try {
if (request instanceof ServletRequestWrapper) {
Field requestField = ServletRequestWrapper.class.getDeclaredField("request");
requestField.setAccessible(true);
HttpServletRequest requestTarget = (HttpServletRequest) requestField.get(request);
if (requestTarget instanceof RequestFacade){
return (RequestFacade) requestTarget;
}
}
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
private static boolean setHead(RequestFacade request, String key, String value) {
if (request==null){
return false;
}
try {
Field requestField = RequestFacade.class.getDeclaredField("request");
requestField.setAccessible(true);
Request requestReal = (Request) requestField.get(request);
Field coyoteRequestField = Request.class.getDeclaredField("coyoteRequest");
coyoteRequestField.setAccessible(true);
org.apache.coyote.Request coyoteRequest = (org.apache.coyote.Request) coyoteRequestField.get(requestReal);
MimeHeaders headers = coyoteRequest.getMimeHeaders();
headers.setValue(key).setString(value);
return true;
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return false;
}
}