实际工作中学习技术是最快、最深刻的。当然,自身的持续学习意识是必须的
技术栈版本:
spring boot 2.0.2
遇到事儿了
近来做业务需求,前端同学fe将 userId 和 userName 放到 request header 中了。
后端api接口要想使用 userId 和 userName ,每个接口都要从 header 中获取。
试想一下,如果你有十个接口,那么每个接口都要写一遍
1 |
Object.setUserId(request.getHeader( "userId" )) |
正如下面代码段
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
@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; } |
解决办法
大家都知道的两大利器,
tomcat 的 Filter 和 spring 的 Intercepter (具体为 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 。
如上文 controller 中 create 方法的入参: TemplateParam ,继承 UserInfoParam
1 2 3 4 5 6 7 8 9 10 11 12 13 |
@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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
@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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
@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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
@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访问
1 2 3 4 5 6 7 |
curl -X POST \ http: //localhost:8080/template/create -H 'Content-Type: application/json' -H 'username: tiankong天空' -H 'userId: 11' -d '{ "templateId" : 1000 } |
效果
可以看到 TemplateController.create(…) 打出的信息, userId 和 username 的值正是 header 中传的值
toDo
剩下的就看你的了,以上为个人经验,希望能给大家一个参考,也希望大家多多支持。
原文链接:https://yaoyuanyy.github.io/
查看更多关于springboot HandlerIntercepter拦截器修改request body数据的操的详细内容...