springweb项目自定义拦截器修改请求报文头
面向切面,法力无边,任何脏活累活,都可以从干干净净整齐划一的业务代码中抽出来,无非就是加一层,项目里两个步骤间可以被分层的设计渗透成筛子。
举个例子:
最近我们对接某银行接口,我们的web服务都是标准的restful请求,所以所有的controller层代码都是整整齐齐的
@Slf4j
@RestController
@RequestMapping("xxx")
@NoticeGroup
public class XXXController {
@Autowired
XXXService xxxService;
@PostMapping("/fff")
public XXXRespVo fff(@RequestBody xxxReqVo reqVo){
log.info("传入参数:{}",reqVo);
return xxxService.doSomething(reqVo));
}
}
这样的话请求就是标准的Post ,json ,
不成想,该银行回调我方的报文竟然是一串字符串,而且请求头是 text/plain,好家伙所有的请求都要改,这不成,队伍要整齐,来加一层在过滤器中,不修改的话调用逻辑如此?
我们拿到报文后需要先进行解密,好的,如果有100个接口那解密100次也不现实,

我们可以自定义一个过滤器,在过滤器中将报文解密重新塞回请求体

实现起来还是比较简单的上网上摘抄即可:
由于request请求体流读一次之后就不能再读了,所以要做一个包装类,相当于将断开的流重新接起来,好比再管道上打个口子,取出东西来再放回这个东西。但是我们对这个东西做了点什么,什么也不做也行,就只是检查下。
参考代码
public class RequestWrapper extends HttpServletRequestWrapper {
private String body;
public RequestWrapper(HttpServletRequest request) throws IOException {
super(request);
StringBuilder sb =new StringBuilder();
InputStream inputStream=null;
BufferedReader reader=null;
try{
inputStream=cloneInputStream(request.getInputStream());
reader =new BufferedReader(new InputStreamReader(inputStream));
String line="";
while((line=reader.readLine())!=null){
sb.append(line);
}
}catch (IOException e){
e.printStackTrace();
}finally {
if (inputStream != null) {
try {
inputStream.close();
}
catch (IOException e) {
e.printStackTrace();
}
}
if (reader != null) {
try {
reader.close();
}
catch (IOException e) {
e.printStackTrace();
}
}
}
body=sb.toString();
}
public InputStream cloneInputStream(ServletInputStream inputStream) {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len;
try {
while ((len = inputStream.read(buffer)) > -1) {
byteArrayOutputStream.write(buffer, 0, len);
}
byteArrayOutputStream.flush();
}
catch (IOException e) {
e.printStackTrace();
}
InputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
return byteArrayInputStream;
}
@Override
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes());
ServletInputStream servletInputStream = new ServletInputStream() {
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener readListener) {}
@Override
public int read() throws IOException {
return byteArrayInputStream.read();
}
};
return servletInputStream;
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(this.getInputStream()));
}
public String getBody() {
return this.body;
}
public void setBody(String body) throws UnsupportedEncodingException {
this.body = body;
}
}
- 过滤器类怎么写呢?如下,@WebFilter(urlPatterns = "/XXX/") 注解起的作用是拦截匹配真个url的请求,这里需要再启动类里面配套加一个@ServletComponentScan(basePackages = "xxx.xxx.xxx.")
不加的话会把所有的请求都拦截,我在当初的某一版本上就没加,只能在下面doFilter方法里面加补丁,判断请求url是不是我想要的否则用不走解密的逻辑
@Order(12)
@Slf4j
@WebFilter(urlPatterns = "/XXX/*")
public class XXXFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
try {
RequestWrapper requestWrapper = new RequestWrapper((HttpServletRequest) request);
String bodyString = requestWrapper.getBody();
requestWrapper.setBody(decrypt(bodyString));
try {
filterChain.doFilter(requestWrapper, servletResponse);
} catch (Exception e) {
log.info("业务中的异常", e.getMessage());
throw e;
}
} catch (Exception e) {
e.printStackTrace();
}
}
@SneakyThrows
public String decrypt(String encrypedStr) {
String result = "根据密文解密后的数据巴拉巴拉";
return result;
}
}
- 这样程序运行的逻辑就成如下,我们在拦截器中把报文变漂亮了。返回的时候也可以在这里处理,嘿嘿,我们在对外提供服务的时候有使用需要把返回的东西加密,就在这里处理了。

- 这样只是解决的报文明文的问题,还没有解决报文头,但是我们能修改报文体就能修改报文头不是么?代码如下: 塞进requestWapper里面
@Override
public Enumeration<String> getHeaders(String name) {
List<String> list=Collections.list(super.getHeaders(name));
//处理一下大小写,可能是content-type 或 Content-Type
if(name.equalsIgnoreCase(HttpHeaders.CONTENT_TYPE)){
list=new ArrayList<>();
list.add(MediaType.APPLICATION_JSON_VALUE);
}
Enumeration<String> re= Collections.enumeration(list);
return re;
}
- 为啥这么写呢? debug追溯可以找到类:AbstractMessageConverterMethodArgumentResolver
@Nullable
protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter,
Type targetType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {
MediaType contentType;
boolean noContentType = false;
try {
// 这里的inputMessage
contentType = inputMessage.getHeaders().getContentType();
}
...
}

@Override
public HttpHeaders getHeaders() {
if (this.headers == null) {
this.headers = new HttpHeaders();
for (Enumeration<?> names = this.servletRequest.getHeaderNames(); names.hasMoreElements();) {
String headerName = (String) names.nextElement();
for (Enumeration<?> headerValues = this.servletRequest.getHeaders(headerName);
headerValues.hasMoreElements();) {
String headerValue = (String) headerValues.nextElement();
this.headers.add(headerName, headerValue);
}
}
......
}
看上面的代码,这里是 requestWapper 作为构造参数在创建inputMessage的时候弄进去的。 this.servletRequest==requestWapper, 这样 this.headers = new HttpHeaders();其实取值的时候就是
this.servletRequest.getHeaders(headerName); 这段代码了,我们wapper中重写的也就是这一块,将原先的报文头强行修改为了application/json。
到此就完成了。
Enumeration是个啥呢?
Enumeration接口中定义了一些方法,通过这些方法可以枚举(一次获得一个)对象集合中的元素。
这种传统接口已被迭代器取代,虽然Enumeration 还未被遗弃,但在现代代码中已经被很少使用了。尽管如此,它还是使用在诸如Vector和Properties这些传统类所定义的方法中,除此之外,还用在一些API类,并且在应用程序中也广泛被使用。
public interfaceEnumeration<E> {
/**
* Tests if this enumeration contains more elements.
*
*@return<code>true</code>if and only if this enumeration object
* contains at least one more element to provide;
*<code>false</code>otherwise.
*/
booleanhasMoreElements();
/**
* Returns the next element of this enumeration if this enumeration
* object has at least one more element to provide.
*
*@returnthe next element of this enumeration.
*@exceptionNoSuchElementExceptionif no more elements exist.
*/
E nextElement();
}
实现类虽然多但是没有我认识的呵呵,
Collections.enumeration(list); 这个方法,如下,看来就是用Iterator 来实现。这种写法还挺妙的。
public static <T> Enumeration<T> enumeration(final Collection<T> c) {
return new Enumeration<T>() {
private final Iterator<T> i = c.iterator();
public boolean hasMoreElements() {
return i.hasNext();
}
public T nextElement() {
return i.next();
}
};
}
springweb项目自定义拦截器修改请求报文头的更多相关文章
- springboot项目使用拦截器修改/添加前端传输到后台header和cookie参数
本质上来讲,request请求当中的参数是无法更改的,也不能添加或者删除. 但在后台程序中,一般对request的参数的操作,都是通过request的getParameter.getParameter ...
- Struts2自定义拦截器处理全局异常
今天在整理之前的项目的时候想着有的action层没有做异常处理,于是想着自定义拦截器处理一下未拦截的异常. 代码: package cn.xm.exam.action.safeHat; import ...
- 【struts2】自定义拦截器
1)什么是自定义的拦截器 所谓自定义的拦截器,就是由我们自己定义并实现的拦截器,而不是由Struts2定义好的拦截器.虽然Struts2的预定义拦截器已经满足了大多数情况的需要.但在有些时候,我们可能 ...
- Struts学习之自定义拦截器
* 所有的拦截器都需要实现Interceptor接口或者继承Interceptor接口的扩展实现类 * 要重写init().intercept().destroy()方法 * in ...
- 从struts2拦截器到自定义拦截器
拦截器可谓struts2的核心了,最基本的bean的注入就是通过默认的拦截器实现的,一般在struts2.xml的配置中,package内直接或间接继承了struts-default.xml,这样st ...
- 5、Struts2自定义拦截器
一.拦截器相关知识 1.Struts2框架剖析 Holly版本生活案例: 影视公司(拍电影) ActionMapper 传媒公司(包装明星) ActionMapping 明星 ...
- SpringMVC(四)-- 文件下载、自定义拦截器、异常处理
1.文件下载 用ResponseEntity<byte[]> 返回值完成文件下载 具体参见本博客之前的<文件上传下载> @RequestMapping(value=" ...
- struts2--实现自定义拦截器
前言: 本篇文章,我想说清实现拦截器的办法,还有为什么要这样做: 目录: 1.需求目的 2.实现步骤及原理(文字怕描述不清,画图描述) 3.代码 4.总结 一.需求目的 规范或限制越级访问(例如:一个 ...
- SpringMVC系列(十二)自定义拦截器
Spring MVC也可以使用拦截器对请求进行拦截处理,用户可以自定义拦截器来实现特定的功能,自定义的拦截器必须实现HandlerInterceptor接口– preHandle():这个方法在业务处 ...
随机推荐
- 【java虚拟机】jvm内存模型
作者:pengjunlee原文链接:https://blog.csdn.net/pengjunlee/article/details/71909239 目录 一.运行时数据区域 1.程序计数器 2.J ...
- 从eclipse转idea不适记录【持续更新】
使用eclipse和idea时,快捷键对比 从一些最扎心的开始: 关于print一类的打印输出:System.out.println()补全 idea中可以sout.souf.serr:分别对应out ...
- HbaseWAL
1.WAL意为 Write Ahead Log ,类似MySQL中的binlog,用来做灾难恢复之用,HLog记录数据的所有变更,一旦数据修改,就可以从Log中进行恢复. Hbase采用类LSM的架构 ...
- idea上传项目到github 2019
记录一下自己查找的从idea上传项目到github的总结 1.默认本地已经安装好git.exe ,idea也已经和git进行匹配 File-setting-versionControl-git-Tes ...
- Ubuntu16.04 Linux 下安装、配置SSH
本人在Win7+VMware下利用两个ubuntu虚拟机安装.配置.测试了SSH. 在Server端安装openssh-server. sudo apt-get install ssh # 安装ssh ...
- 性能测试工具JMeter 基础(三)—— 创建测试计划
如何创建一个完整的测试计划? 安装好JMeter后,通过根目录下的bin目录中的jmeter.bat启动JMeter 添加线程组(Thread Group) 在 Test Plan 鼠标右键 Add- ...
- C#新版本风格(NetCore)项目文件
在VisualStudio中创建NetCore以上版本的项目,使用的都是新版本风格的项目文件. 和旧版本.NetFramework版本的项目文件区别: 双击项目可直接打开csproj文件进行编辑配置 ...
- Python 利用GDAL对图像进行几何校正
原文链接:https://blog.csdn.net/qq_27045589/article/details/81062586 一.几何校正方法 图像校正本质是建立一种从原始图像行列号到某种投影的数学 ...
- 哦?原来这就是 JVM 垃圾!
大家都知道,JVM 有垃圾回收的机制,垃圾回收的前提是要知道:什么是垃圾!然后再是如何识别垃圾! 什么是垃圾 垃圾,本质上就是没有引用的对象(们),下面来介绍两种垃圾 1. 没有引用指向的对象 下图是 ...
- Mysql常用sql语句(11)- between and 范围查询
测试必备的Mysql常用sql语句系列 https://www.cnblogs.com/poloyy/category/1683347.html 前言 between and可以判断值是否在指定范围内 ...