springboot HandlerIntercepter拦截器修改request body数据的操作

 更新时间:2021年06月29日 09:07:05   作者:Skyler  
这篇文章主要介绍了springboot HandlerIntercepter拦截器修改request body数据的操作,具有很好的参考价值,希望对大家有所帮助。

实际工作中学习技术是最快、最深刻的。当然,自身的持续学习意识是必须的

技术栈版本:

spring boot 2.0.2

遇到事儿了

近来做业务需求,前端同学fe将userIduserName放到request header中了。

后端api接口要想使用userIduserName,每个接口都要从header中获取。

试想一下,如果你有十个接口,那么每个接口都要写一遍

Object.setUserId(request.getHeader("userId"))

正如下面代码段

@RestController
@Validated
@RequestMapping("/template")
public class TemplateController {
    // 一个feign client
    @Autowired 
    TemplateClient templateClient
    @PostMapping(value = "/create", produces = MediaType.APPLICATION_JSON_VALUE)
    public ResultVO create(@RequestBody @Valid TemplateParam param, HttpServletRequest request) {
        // 每个接口都要写一遍setXXX()方法
        param.setUserId(request.getHeader("userId"));
        param.setUserName(request.getHeader("userName"));
        return templateClient.createTemplate(param).toResultVO();
    }
}   
@Data
public class TemplateParam{
    private Long templateId; 
    private Long userId;
    private String userName;
}

解决办法

大家都知道的两大利器,

tomcatFilterspringIntercepter(具体为HandlerIntercepter)

实现原理

具体方法为定义一个Filter实现类和一个HandlerIntercepter的实现类。再定义一个HttpServletRequest实现类,其作用分别为

Filter实现类:UserInfoFilter

创建一个入口,在这个入口中定义一个机会:将我们自定义的CustomHttpServletRequestWrapper代替HttpServletRequest随着请求传递下去

HttpServletRequest实现类:customHttpServletRequestWrapper

因为HttpServletRequest对象的body数据只能get,不能set,即不能再次赋值。

而我们的需求是需要给HttpServletRequest赋值,所以需要定义一个HttpServletRequest实现类:customHttpServletRequestWrapper,这个实现类可以被赋值来满足我们的需求。

HandlerIntercepter的实现类:CustomInterceptor

拦截请求,获取接口方法相关信息(方法名,参数,返回值等)。从而实现统一的给request body动态赋值

实现思路如上所述,具体的实现代码如下

代码实现

声明基础bean: UserInfoParam

UserInfoParam:定义了包含userId,userName的实体bean,要想将用户信息注入进入,需要入参对象XXXParam继承UserInfoParam,拦截器中只处理@Requestbody中实现了UserInfoParam类的bean

如上文controllercreate方法的入参:TemplateParam,继承UserInfoParam

@Data
public class TemplateParam extends UserInfoParam{
    private Long templateId; 
    // private Long userId;
    // private String userName;
}
@Data
public class UserInfoParam {
// 用户id
private Long userId;
// 用户名称
private String userName;
}

定义Filter实现类: UserInfoFilter

@Slf4j
public class UserInfoFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        CustomHttpServletRequestWrapper customHttpServletRequestWrapper = null;
        try {
            HttpServletRequest req = (HttpServletRequest)request;
            customHttpServletRequestWrapper = new CustomHttpServletRequestWrapper(req);
        }catch (Exception e){
            log.warn("customHttpServletRequestWrapper Error:", e);
        }
        chain.doFilter((Objects.isNull(customHttpServletRequestWrapper) ? request : customHttpServletRequestWrapper), response);
    }
}

HttpServletRequest实现类:CustomHttpServletRequestWrapper

public class CustomHttpServletRequestWrapper extends HttpServletRequestWrapper {
    // 保存request body的数据
    private String body;
    // 解析request的inputStream(即body)数据,转成字符串
    public CustomHttpServletRequestWrapper(HttpServletRequest request) {
        super(request);
        StringBuilder stringBuilder = new StringBuilder();
        BufferedReader bufferedReader = null;
        InputStream inputStream = null;
        try {
            inputStream = request.getInputStream();
            if (inputStream != null) {
                bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
                char[] charBuffer = new char[128];
                int bytesRead = -1;
                while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
                    stringBuilder.append(charBuffer, 0, bytesRead);
                }
            } else {
                stringBuilder.append("");
            }
        } catch (IOException ex) {
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                }
                catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (bufferedReader != null) {
                try {
                    bufferedReader.close();
                }
                catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        body = stringBuilder.toString();
    }
    @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;
    }
    // 赋值给body字段
    public void setBody(String body) {
        this.body = body;
    }
}

HandlerIntercepter的实现类:CustomInterceptor

@Slf4j
public class CustomInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
        throws Exception {
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }
        HandlerMethod handlerMethod = (HandlerMethod)handler;
        pushUserInfo2Body(request, handlerMethod);
        return true;
    }
    private void pushUserInfo2Body(HttpServletRequest request, HandlerMethod handlerMethod) {
        try{
            String userId = request.getHeader("userId");
            String userName = request.getHeader("userName");
            MethodParameter[] methodParameters = handlerMethod.getMethodParameters();
            if(ArrayUtils.isEmpty(methodParameters)) {
                return;
            }
            for (MethodParameter methodParameter : methodParameters) {
                Class clazz = methodParameter.getParameterType();
                if(ClassUtils.isAssignable(UserInfoParam.class, clazz)){
                    if(request instanceof CustomHttpServletRequestWrapper){
                        CustomHttpServletRequestWrapper requestWrapper = (CustomHttpServletRequestWrapper)request;
                        String body = requestWrapper.getBody();
                        JSONObject param = JSONObject.parseObject(body);
                        param.put("userId", userId);
                        param.put("userName", Objects.isNull(userName) ? null : URLDecoder.decode(userName, "UTF-8"));
                        requestWrapper.setBody(JSON.toJSONString(param));
                    }
                }
            }
        }catch (Exception e){
            log.warn("fill userInfo to request body Error ", e);
        }
    }

定义Configuration class,增加拦截器和过滤器的配置

WebMvcConfigurer子类:CustomWebMvcConfigurer

@Configuration
public class CustomWebMvcConfigurer implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        CustomInterceptor customInterceptor= new CustomInterceptor();
        registry.addInterceptor(customInterceptor);
    }
    @Bean
    public FilterRegistrationBean servletRegistrationBean() {
        UserInfoFilter userInfoFilter = new UserInfoFilter();
        FilterRegistrationBean<UserInfoFilter> bean = new FilterRegistrationBean<>();
        bean.setFilter(userInfoFilter);
        bean.setName("userInfoFilter");
        bean.addUrlPatterns("/*");
        bean.setOrder(Ordered.LOWEST_PRECEDENCE);
        return bean;
    }
}

到此,实现功能的代码撸完了。启动spring boot App,就可以curl访问restful接口了

http访问

curl -X POST \
  http://localhost:8080/template/create
  -H 'Content-Type: application/json'
  -H 'username: tiankong天空'
  -H 'userId: 11'
  -d '{
  "templateId": 1000}

效果

可以看到TemplateController.create(…)打出的信息,userIdusername的值正是header中传的值

toDo

剩下的就看你的了,以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • java开发就业信息管理系统

    java开发就业信息管理系统

    这篇文章主要为大家详细介绍了java开发就业信息管理平台,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-06-06
  • 详解JAVA中获取文件MD5值的四种方法

    详解JAVA中获取文件MD5值的四种方法

    这篇文章主要介绍了JAVA中获取文件MD5值的四种方法,获取文件MD5值主要分为三个步骤,第一步获取文件的byte信息,第二步通过MessageDigest类进行MD5加密,第三步转换成16进制的MD5码值,需要的朋友可以参考下
    2022-08-08
  • Java字符编码原理(动力节点Java学院整理)

    Java字符编码原理(动力节点Java学院整理)

    Java开发中,常常会遇到乱码的问题,一旦遇到这种问题,常常比较烦恼,大家都不想承认是自己的代码问题,其实搞明白编码的本质过程就简单多了,接下来小编给大家带来java字符编码原理,要求看看吧
    2017-04-04
  • Java中RabbitMQ延迟队列实现详解

    Java中RabbitMQ延迟队列实现详解

    这篇文章主要介绍了Java中RabbitMQ延迟队列实现详解,消息过期后,根据routing-key的不同,又会被死信交换机路由到不同的死信队列中,消费者只需要监听对应的死信队列进行消费即可,需要的朋友可以参考下
    2023-09-09
  • springboot整合vue实现上传下载文件

    springboot整合vue实现上传下载文件

    这篇文章主要为大家详细介绍了springboot整合vue实现上传下载文件,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-11-11
  • Java代码精简之道(推荐)

    Java代码精简之道(推荐)

    这篇文章主要给大家介绍了Java代码精简之道,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-11-11
  • Java 中 Date 与 Calendar 之间的编辑与转换实例详解

    Java 中 Date 与 Calendar 之间的编辑与转换实例详解

    这篇文章主要介绍了Java 中 Date 与 Calendar 之间的编辑与转换 ,非常不错,具有一定的参考借鉴价值,需要的朋友可以参考下
    2018-07-07
  • SpringMVC基于配置的异常处理器

    SpringMVC基于配置的异常处理器

    这篇文章主要为大家介绍了SpringMVC基于配置的异常处理器,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-05-05
  • 解决springboot使用logback日志出现LOG_PATH_IS_UNDEFINED文件夹的问题

    解决springboot使用logback日志出现LOG_PATH_IS_UNDEFINED文件夹的问题

    这篇文章主要介绍了解决springboot使用logback日志出现LOG_PATH_IS_UNDEFINED文件夹的问题,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-04-04
  • springboot项目访问图片的3种实现方法(亲测可用)

    springboot项目访问图片的3种实现方法(亲测可用)

    本文主要介绍了springboot项目访问图片的3种实现方法,通过springboot项目访问除项目根目录之外的其它目录的图片,具有一定的参考价值,感兴趣的可以了解一下
    2023-09-09

最新评论