自定义starter(实现HttpServletRequest重复读取)

简介: 自定义starter(实现HttpServletRequest重复读取)

一、概述



公司里面的祖传代码很多,然后对拦截器进行修改,在拦截器中添加一些参数校验,然而对于payload请求,不可以通过getParameter()方法直接获取body里面的参数,需要读取流,然后转换成json对象,然而,抽取出来了就不能自动装配了,这个之前有写过一篇文章描述如何解决该问题的。


目前又遇到了一个问题,这种参数校验的项目较多,每个项目添加几个类很麻烦,然后想着用spring-boot的starter来封装之前的解决方法,然后每次要用的时候,我只需要pom引入我的starter就行。


二、启动器starter基本规范



  1. 启动器只用来做依赖导入(maven工程)
  2. 写一个启动器自动配置模块(spring-boot工程)


1. @Configuration //指定这个类是一个配置类
@ConditionalOnXXX //在指定条件成立的情况下自动配置类生效
@AutoConfigureAfter //指定自动配置类的顺序
@Bean //给容器中添加组件
2. @ConfigurationPropertie结合相关xxxProperties类来绑定相关的配置
@EnableConfigurationProperties //让xxxProperties生效加入到容器中
3. 自动配置类要能加载
将需要启动就加载的自动配置类,配置在META‐INF/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\ org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\


  1. 自定义启动器名***-spring-boot-starter


推荐使用以下命名规约;
 • 官方命名空间
    – 前缀:“spring-boot-starter-”
    – 模式:spring-boot-starter-模块名
    – 举例:spring-boot-starter-web、spring-boot-starter-actuator、spring-boot-starter-jdbc
• 自定义命名空间
    – 后缀:“-spring-boot-starter ”
    – 模式:模块-spring-boot-starter
    – 举例:mybatis-spring-boot-starter


2.1 启动器模块


将自动配置类需要的jar包在starter中的pom文件引入进来。


<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.infosec</groupId>
    <artifactId>multiplyuse-spring-boot-starter</artifactId>
    <version>1.0-SNAPSHOT</version>
    <dependencies>
        <dependency>
            <groupId>com.infosec</groupId>
            <artifactId>multiplyuse-spring-boot-starter-autoconfigurer</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.6</version>
        </dependency>
    </dependencies>
</project>


2.2 自动配置模块


  1. 编写一个属性配置类,用来读取配置属性,这里是用来配置过滤器的过滤条件


import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.List;
@ConfigurationProperties(prefix = "httpservlet.mulitiplyuse")
public class MultiplyUseProperties {
    private List<String> urlParten;
    public List<String> getUrlParten() {
        return urlParten;
    }
    public void setUrlParten(List<String> urlParten) {
        this.urlParten = urlParten;
    }
}


  1. 编写用于实现重复读取HttpServletRequest请求的代码


import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import org.apache.commons.io.IOUtils;
public class ContentCachingRequestWrapper extends HttpServletRequestWrapper{
    private byte[] body;
    private BufferedReader reader;
    private ServletInputStream inputStream;
    public ContentCachingRequestWrapper(HttpServletRequest request) throws IOException{
        super(request);
        loadBody(request);
    }
    private void loadBody(HttpServletRequest request) throws IOException{
        body = IOUtils.toByteArray(request.getInputStream());
        inputStream = new RequestCachingInputStream(body);
    }
    public byte[] getBody() {
        return body;
    }
    @Override
    public ServletInputStream getInputStream() throws IOException {
        if (inputStream != null) {
            return inputStream;
        }
        return super.getInputStream();
    }
    @Override
    public BufferedReader getReader() throws IOException {
        if (reader == null) {
            reader = new BufferedReader(new InputStreamReader(inputStream, getCharacterEncoding()));
        }
        return reader;
    }
    private static class RequestCachingInputStream extends ServletInputStream {
        private final ByteArrayInputStream inputStream;
        public RequestCachingInputStream(byte[] bytes) {
            inputStream = new ByteArrayInputStream(bytes);
        }
        @Override
        public int read() throws IOException {
            return inputStream.read();
        }
        @Override
        public boolean isFinished() {
            return inputStream.available() == 0;
        }
        @Override
        public boolean isReady() {
            return true;
        }
        @Override
        public void setReadListener(ReadListener readlistener) {
        }
    }
}


添加过滤器


import org.apache.commons.io.IOUtils;
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
public class SignValidateFilter implements Filter{
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        ContentCachingRequestWrapper requestWrapper = new ContentCachingRequestWrapper((HttpServletRequest) request);
        String body = IOUtils.toString(requestWrapper.getBody(),request.getCharacterEncoding());
        chain.doFilter(requestWrapper, response);
    }
    @Override
    public void destroy() {
    }
}


  1. 编写配置类


import com.infosec.multiplyusespringbootstarterautoconfigurer.MultiplyUseProperties;
import com.infosec.multiplyusespringbootstarterautoconfigurer.SignValidateFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@ConditionalOnWebApplication
@ConditionalOnClass(name = "org.apache.commons.io.IOUtils")
@EnableConfigurationProperties(value = MultiplyUseProperties.class)
public class MultiplyUseAutoConfiguration
{
    @Autowired
    private MultiplyUseProperties multiplyUseProperties;
    @Bean
    public FilterRegistrationBean myFilter(){
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
        filterRegistrationBean.setFilter(new SignValidateFilter());
        filterRegistrationBean.setUrlPatterns(multiplyUseProperties.getUrlParten());
        return filterRegistrationBean;
    }
}


  1. 设置自动配置类启动加载,配置在META‐INF/spring.factories


org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.infosec.multiplyusespringbootstarterautoconfigurer.configuration.MultiplyUseAutoConfiguration


三、测试



拦截器


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.example.testforauto.annotatin.LoginRequired;
import com.infosec.multiplyusespringbootstarterautoconfigurer.ContentCachingRequestWrapper;
import org.apache.commons.io.IOUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
public class UserInceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("========================");
        ContentCachingRequestWrapper requestWapper = null;
        if(request instanceof HttpServletRequest){
            requestWapper = (ContentCachingRequestWrapper) request;
        }
        System.out.println(123456);
        String body = IOUtils.toString(requestWapper.getBody(),request.getCharacterEncoding());
        JSONObject obj = JSON.parseObject(body);
        System.out.println(obj);
        return true;
    }
}


请求


import com.example.testforauto.annotatin.LoginRequired;
import com.example.testforauto.entity.User;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class UserController {
    @LoginRequired
    @PostMapping(value = "/user")
    public String User(@RequestBody User user){
        System.out.println(123);
        System.out.println(user);
        return user.getName();
    }
}


测试结果:


========================
123456
{"name":"ljl","id":1}
123
User{id=1, name='ljl'}


从结果上看,httpServletRequest在拦截器中被调用之后,自动装配也是成功的,所以实现了重复读取的功能。

目录
相关文章
|
2月前
|
安全 Java 数据安全/隐私保护
|
前端开发 Java 数据库
Spring Entity数据校验,分组校验,返回校验结果给前端
Spring Entity数据校验,分组校验,返回校验结果给前端
91 0
|
11月前
|
XML JSON Java
SpringMVC获取请求中的参数值不同方式总结
SpringMVC获取请求中的参数值不同方式总结
106 0
|
12月前
|
缓存 前端开发 安全
SpringBoot如何缓存方法返回值?
为什么要对方法的返回值进行缓存呢? 简单来说是为了提升后端程序的性能和提高前端程序的访问速度。减小对db和后端应用程序的压力。 一般而言,缓存的内容都是不经常变化的,或者轻微变化对于前端应用程序是可以容忍的。 否则,不建议加入缓存,因为增加缓存会使程序复杂度增加,还会出现一些其他的问题,比如缓存同步,数据一致性,更甚者,可能出现经典的缓存穿透、缓存击穿、缓存雪崩问题。
55 0
|
12月前
|
Java 数据库 Spring
spring boot 查询到的数据返回null
spring boot 查询到的数据返回null
342 0
|
存储 编解码 算法
如何在 Spring Boot 中实现在 Request 里解密参数返回的功能?
如何在 Spring Boot 中实现在 Request 里解密参数返回的功能?
201 0
|
监控 Java 容器
Spring项目中修改javamelody映射url以及其它参数失效的问题
首先,servlet3.0支持模块化,在jar包中如果有web-fragmenet.xml,则servlet容器会先加载web.xml里的listener、filter和servlet,再加载web-fragment.xml里的组件。关于模块化的介绍:
Spring项目中修改javamelody映射url以及其它参数失效的问题
|
存储 JSON 数据格式
解决HttpServletRequest中的流无法重复读取的问题
解决HttpServletRequest中的流无法重复读取的问题
493 0
|
JSON Java 数据格式
Springboot | @RequestBody 接收到的参数对象属性为空
Springboot | @RequestBody 接收到的参数对象属性为空
1011 0
Springboot | @RequestBody 接收到的参数对象属性为空
|
JSON Java 数据格式
Spring Boot @ControllerAdvice 处理全局异常,返回固定格式Json
参考文章: Spring Boot @ControllerAdvice 处理全局异常,返回固定格式Json 解决spring boot中rest接口404,500等错误返回统一的json格式 spring boot最新教程(五):404错误500错误统...
3110 0