springboot HandlerIntercepter攔截器修改request body數(shù)據(jù)的操作
實際工作中學(xué)習(xí)技術(shù)是最快、最深刻的。當(dāng)然,自身的持續(xù)學(xué)習(xí)意識是必須的
技術(shù)棧版本:
spring boot 2.0.2
遇到事兒了
近來做業(yè)務(wù)需求,前端同學(xué)fe將userId和userName放到request header中了。
后端api接口要想使用userId和userName,每個接口都要從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;
}
解決辦法
大家都知道的兩大利器,
tomcat的Filter和spring的Intercepter(具體為HandlerIntercepter)
實現(xiàn)原理
具體方法為定義一個Filter實現(xiàn)類和一個HandlerIntercepter的實現(xiàn)類。再定義一個HttpServletRequest實現(xiàn)類,其作用分別為
Filter實現(xiàn)類:UserInfoFilter
創(chuàng)建一個入口,在這個入口中定義一個機會:將我們自定義的CustomHttpServletRequestWrapper代替HttpServletRequest隨著請求傳遞下去
HttpServletRequest實現(xiàn)類:customHttpServletRequestWrapper
因為HttpServletRequest對象的body數(shù)據(jù)只能get,不能set,即不能再次賦值。
而我們的需求是需要給HttpServletRequest賦值,所以需要定義一個HttpServletRequest實現(xiàn)類:customHttpServletRequestWrapper,這個實現(xiàn)類可以被賦值來滿足我們的需求。
HandlerIntercepter的實現(xiàn)類:CustomInterceptor
攔截請求,獲取接口方法相關(guān)信息(方法名,參數(shù),返回值等)。從而實現(xiàn)統(tǒng)一的給request body動態(tài)賦值
實現(xiàn)思路如上所述,具體的實現(xiàn)代碼如下
代碼實現(xiàn)
聲明基礎(chǔ)bean: UserInfoParam
UserInfoParam:定義了包含userId,userName的實體bean,要想將用戶信息注入進入,需要入?yún)ο?code>XXXParam繼承UserInfoParam,攔截器中只處理@Requestbody中實現(xiàn)了UserInfoParam類的bean。
如上文controller中create方法的入?yún)ⅲ?code>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實現(xiàn)類: 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實現(xiàn)類:CustomHttpServletRequestWrapper
public class CustomHttpServletRequestWrapper extends HttpServletRequestWrapper {
// 保存request body的數(shù)據(jù)
private String body;
// 解析request的inputStream(即body)數(shù)據(jù),轉(zhuǎn)成字符串
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的實現(xiàn)類: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;
}
}
到此,實現(xiàn)功能的代碼擼完了。啟動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(…)打出的信息,userId和username的值正是header中傳的值
toDo
剩下的就看你的了,以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java字符編碼原理(動力節(jié)點Java學(xué)院整理)
Java開發(fā)中,常常會遇到亂碼的問題,一旦遇到這種問題,常常比較煩惱,大家都不想承認是自己的代碼問題,其實搞明白編碼的本質(zhì)過程就簡單多了,接下來小編給大家?guī)韏ava字符編碼原理,要求看看吧2017-04-04
Java 中 Date 與 Calendar 之間的編輯與轉(zhuǎn)換實例詳解
這篇文章主要介紹了Java 中 Date 與 Calendar 之間的編輯與轉(zhuǎn)換 ,非常不錯,具有一定的參考借鑒價值,需要的朋友可以參考下2018-07-07
解決springboot使用logback日志出現(xiàn)LOG_PATH_IS_UNDEFINED文件夾的問題
這篇文章主要介紹了解決springboot使用logback日志出現(xiàn)LOG_PATH_IS_UNDEFINED文件夾的問題,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04
springboot項目訪問圖片的3種實現(xiàn)方法(親測可用)
本文主要介紹了springboot項目訪問圖片的3種實現(xiàn)方法,通過springboot項目訪問除項目根目錄之外的其它目錄的圖片,具有一定的參考價值,感興趣的可以了解一下2023-09-09

