• 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();
                }
            };
        }
    
  • 相关阅读:
    Backtrader中文笔记之CSV Data Feed Development-General
    Backtrader中文笔记之CSV Data Feed Filters
    Backtrader中文笔记之CSV Data Feed Development
    Backtrader中文笔记之Extending a Datafeed
    Backtrader中文笔记之Data Feeds
    discuz功能列表
    phpcms功能列表
    empireCMS 帝国cms功能总结
    ecshop功能目录
    织梦所有功能导航列表介绍
  • 原文地址:https://www.cnblogs.com/shuiliuhualuo/p/15190924.html
Copyright © 2020-2023  润新知