SpringBoot攔截器如何獲取http請求參數(shù)
1.1、獲取http請求參數(shù)是一種剛需
我想有的小伙伴肯定有過獲取http請求的需要,比如想
- 前置獲取參數(shù),統(tǒng)計請求數(shù)據(jù)
- 做服務(wù)的接口簽名校驗(yàn)
- 敏感接口監(jiān)控日志
- 敏感接口防重復(fù)提交
等等各式各樣的場景,這時你就需要獲取 HTTP 請求的參數(shù)或者請求body,一般思路有兩種,一種就是自定義個AOP去攔截目標(biāo)方法,第二種就是使用攔截器。整體比較來說,使用攔截器更靈活些,因?yàn)槊總€接口的請求參數(shù)定義不同,使用AOP很難細(xì)粒度的獲取到變量參數(shù),本文主線是采用攔截器來獲取HTTP請求。
1.2、定義攔截器獲取請求
基于 spring-boot-starter-parent 2.1.9.RELEASE
看起來這個很簡單,這里就直接上code,定義個攔截器
/**
* @author axin
* @summary HTTP請求攔截器
*/
@Slf4j
public class RequestInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//獲取請求參數(shù)
String queryString = request.getQueryString();
log.info("請求參數(shù):{}", queryString);
//獲取請求body
byte[] bodyBytes = StreamUtils.copyToByteArray(request.getInputStream());
String body = new String(bodyBytes, request.getCharacterEncoding());
log.info("請求體:{}", body);
return true;
}
}
然后把這個攔截器配置一下中:
/**
* WebMVC配置,你可以集中在這里配置攔截器、過濾器、靜態(tài)資源緩存等
*/
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new RequestInterceptor()).addPathPatterns("/**");
}
}
定義個接口測試一下
/**
* @author axin
* @summary 提交測試接口
*/
@Slf4j
@RestController
public class MyHTTPController {
@GetMapping("/v1/get")
public void get(@RequestParam("one") String one,
@RequestParam("two") BigDecimal number) {
log.info("參數(shù):{},{}", one, number);
}
@PostMapping("/v1/post")
public void check(@RequestBody User user) {
log.info("{}", JSON.toJSONString(user));
}
}
GET請求獲取請求參數(shù)示例:

POST請求獲取請求Body示例:

我們發(fā)現(xiàn)攔截器在獲取HTTP請求的body時出現(xiàn)了 400 (Required request body is missing: public void com.axin.world.controller.MyHTTPController.check(com.axin.world.domain.User));同時也發(fā)現(xiàn)攔截器竟然走了兩遍,這又是咋回事呢?

1.3、為什么攔截器會重復(fù)調(diào)兩遍呢?
其實(shí)是因?yàn)?tomcat截取到異常后就轉(zhuǎn)發(fā)到/error頁面,就在這個轉(zhuǎn)發(fā)的過程中導(dǎo)致了springmvc重新開始DispatcherServlet的整個流程,所以攔截器執(zhí)行了兩次,我們可以看下第二次調(diào)用時的url路徑:

1.4、ServletInputStream(CoyoteInputStream) 輸入流無法重復(fù)調(diào)用
而之前出現(xiàn)的 Required request body is missing 錯誤 其實(shí)是ServletInputStream被讀取后無法第二次再讀取了,所以我們要把讀取過的內(nèi)容存下來,然后需要的時候?qū)ν馓峁┛杀恢貜?fù)讀取的ByteArrayInputStream。
對于MVC的過濾器來說,我們就需要重寫 ServletInputStream 的 getInputStream()方法。
1.5、自定義 HttpServletRequestWrapper
為了 重寫 ServletInputStream 的 getInputStream()方法,我們需要自定義一個 HttpServletRequestWrapper :
/**
* @author Axin
* @summary 自定義 HttpServletRequestWrapper 來包裝輸入流
*/
public class AxinHttpServletRequestWrapper extends HttpServletRequestWrapper {
/**
* 緩存下來的HTTP body
*/
private byte[] body;
public AxinHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
super(request);
body = StreamUtils.copyToByteArray(request.getInputStream());
}
/**
* 重新包裝輸入流
* @return
* @throws IOException
*/
@Override
public ServletInputStream getInputStream() throws IOException {
InputStream bodyStream = new ByteArrayInputStream(body);
return new ServletInputStream() {
@Override
public int read() throws IOException {
return bodyStream.read();
}
/**
* 下面的方法一般情況下不會被使用,如果你引入了一些需要使用ServletInputStream的外部組件,可以重點(diǎn)關(guān)注一下。
* @return
*/
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return true;
}
@Override
public void setReadListener(ReadListener readListener) {
}
};
}
@Override
public BufferedReader getReader() throws IOException {
InputStream bodyStream = new ByteArrayInputStream(body);
return new BufferedReader(new InputStreamReader(getInputStream()));
}
}
然后定義一個 DispatcherServlet子類來分派 上面自定義的 AxinHttpServletRequestWrapper :
/**
* @author Axin
* @summary 自定義 DispatcherServlet 來分派 AxinHttpServletRequestWrapper
*/
public class AxinDispatcherServlet extends DispatcherServlet {
/**
* 包裝成我們自定義的request
* @param request
* @param response
* @throws Exception
*/
@Override
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
super.doDispatch(new AxinHttpServletRequestWrapper(request), response);
}
}
然后配置一下:
/**
* WebMVC配置,你可以集中在這里配置攔截器、過濾器、靜態(tài)資源緩存等
*/
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new RequestInterceptor()).addPathPatterns("/**");
}
@Bean
@Qualifier(DispatcherServletAutoConfiguration.DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServlet dispatcherServlet() {
return new AxinDispatcherServlet();
}
}
再調(diào)用一下 POST請求:

請求成功!
1.5、總結(jié)一下 展望一下
如果你想對HTTP請求做些騷操作,那么前置獲取HTTP請求參數(shù)是前提,為此文本給出了使用MVC攔截器獲取參數(shù)的樣例。
在獲取HTTP Body 的時候,出現(xiàn)了 Required request body is missing 的錯誤,同時攔截器還出現(xiàn)執(zhí)行了兩遍的問題,這是因?yàn)?ServletInputStream被讀取了兩遍導(dǎo)致的,tomcat截取到異常后就轉(zhuǎn)發(fā)到 /error 頁面 被攔截器攔截到了,攔截器也就執(zhí)行了兩遍。
為此我們通過自定義 HttpServletRequestWrapper 來包裝一個可被重讀讀取的輸入流,來達(dá)到期望的攔截效果。
在獲取到HTTP的請求參數(shù)后,我們可以前置做很多操作,比如常用的服務(wù)端接口簽名驗(yàn)證,敏感接口防重復(fù)請求等等。
個人水平有限,如果文章有邏輯錯誤或表述問題還請指出,歡迎一起交流。
到此這篇關(guān)于SpringBoot攔截器如何獲取http請求參數(shù)的文章就介紹到這了,更多相關(guān)SpringBoot攔截器獲取http請求參數(shù)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java程序運(yùn)行時出現(xiàn)亂碼問題的排查與解決方法
本文主要介紹了Java程序運(yùn)行時出現(xiàn)亂碼問題的排查與解決方法,包括檢查Java源文件編碼、檢查編譯時的編碼設(shè)置、檢查運(yùn)行時的編碼設(shè)置、檢查命令提示符的代碼頁、檢查命令提示符的字體、檢查 Java 程序的輸出代碼以及檢查環(huán)境變量,需要的朋友可以參考下2025-03-03
高分面試從Hotspot源碼層面剖析java多態(tài)實(shí)現(xiàn)原理
這篇文章主要為大家介紹了在面試中從Hotspot源碼層面來剖析java多態(tài)的實(shí)現(xiàn)原理,這樣回答薪資隨你開,有需要的朋友可以借鑒參考下,希望大家多多加薪2022-01-01
學(xué)習(xí)SpringMVC——國際化+上傳+下載詳解
本篇文章主要介紹了學(xué)習(xí)SpringMVC——國際化+上傳+下載,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。2016-12-12
SpringBoot整合EasyExcel實(shí)現(xiàn)批量導(dǎo)入導(dǎo)出
這篇文章主要為大家詳細(xì)介紹了SpringBoot整合EasyExcel實(shí)現(xiàn)批量導(dǎo)入導(dǎo)出功能的相關(guān)知識,文中的示例代碼講解詳細(xì),需要的小伙伴可以參考下2024-03-03
spring data jpa 創(chuàng)建方法名進(jìn)行簡單查詢方式
這篇文章主要介紹了spring data jpa 創(chuàng)建方法名進(jìn)行簡單查詢方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-02-02
Java實(shí)現(xiàn)注冊登錄跳轉(zhuǎn)
這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)注冊登錄跳轉(zhuǎn),文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-06-06
Spring?invokeBeanFactoryPostProcessors方法刨析源碼
invokeBeanFactoryPostProcessors該方法會實(shí)例化所有BeanDefinitionRegistryPostProcessor和BeanFactoryPostProcessor的實(shí)例并且執(zhí)行postProcessBeanFactory與postProcessBeanDefinitionRegistry方法2023-01-01
Java 使用JdbcTemplate 中的queryForList發(fā)生錯誤解決辦法
這篇文章主要介紹了Java 使用JdbcTemplate 中的queryForList發(fā)生錯誤解決辦法的相關(guān)資料,需要的朋友可以參考下2017-07-07
java項(xiàng)目打包成可執(zhí)行jar用log4j將日志寫在jar所在目錄操作
這篇文章主要介紹了java項(xiàng)目打包成可執(zhí)行jar用log4j將日志寫在jar所在目錄操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-08-08

