实际工作中学习技术是最快、最深刻的。当然,自身的持续学习意识是必须的
技术栈版本:
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/