中文亚洲精品无码_熟女乱子伦免费_人人超碰人人爱国产_亚洲熟妇女综合网

當前位置: 首頁 > news >正文

騰訊云做淘客網(wǎng)站百度關鍵詞搜索排名代發(fā)

騰訊云做淘客網(wǎng)站,百度關鍵詞搜索排名代發(fā),店鋪推廣文案簡短,seo三人行論壇文章目錄 前情提要解決方案自定義 HttpServletRequest 包裝類 RequestWrapper自定義 HttpServletResponse 包裝類 ResponseWrapper自定義過濾器 MiddlewareFilter配置過濾器注解配置類 編寫 Controller 測試 前情提要 在項目中需要使用過濾器 在請求調用 Controller 方法前修改…

文章目錄

    • 前情提要
    • 解決方案
      • 自定義 HttpServletRequest 包裝類 RequestWrapper
      • 自定義 HttpServletResponse 包裝類 ResponseWrapper
      • 自定義過濾器 MiddlewareFilter
      • 配置過濾器
        • 注解
        • 配置類
      • 編寫 Controller 測試

前情提要

在項目中需要使用過濾器 在請求調用 Controller 方法前修改請求參數(shù)和在結果返回之前修改返回結果

在 Controller 中定義如下接口:

@PostMapping("/hello")
public JSONObject hello(@RequestBody Map<String, Object> params) {return JSONObject.parseObject(JSON.toJSONString(params));
}

定義的過濾器如下:

public class ServNoFilter extends OncePerRequestFilter {@Overrideprotected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {// 獲取請求體內容String requestBody = getRequestBody(httpServletRequest);// 業(yè)務處理......// 放行filterChain.doFilter(httpServletRequest, httpServletResponse);}private String getRequestBody(HttpServletRequest request) throws IOException {BufferedReader reader = new BufferedReader(request.getReader());StringBuilder sb = new StringBuilder();String line;while ((line = reader.readLine()) != null) {sb.append(line);}return sb.toString();}
}

此時啟動項目,訪問接口,則會在控制臺打印如下異常信息:Request processing failed; nested exception is java.lang.IllegalStateException: getReader() has already been called for this request。

表示在過濾器中已經(jīng)通過 request.getReader() 方法將請求流讀取。

如果在過濾器中將 getReader() 換成 getInputStream() 就會報請求體為空異常:org.springframework.http.converter.HttpMessageNotReadableException: Required request body is missing。

這是因為在 Servlet 中,請求對象的輸入流只能被讀取一次。而在第一次讀取請求體時,Servlet 容器會將請求體保存在內存中,并將其解析成相應的請求參數(shù)和請求頭信息。如果在后續(xù)的處理中再次讀取請求體,就可能會導致數(shù)據(jù)錯誤或異常。

解決方案

自定義 HttpServletRequest 包裝類 RequestWrapper

在 Servlet 中,原始的 HttpServletRequest 對象中的請求流(即請求體)只能讀取一次。這是因為 HTTP 協(xié)議是基于流的協(xié)議,服務器在讀取請求流時會將其消耗掉,一旦讀取完畢,就無法再次讀取

當 Servlet 容器讀取完請求流后,會將請求的內容解析并儲存在相應的屬性中,如請求參數(shù)、請求頭信息等。在后續(xù)的處理過程中,Servlet 可以從這些屬性中獲取請求內容,而不必再次讀取請求流。

因此,我們需要自定義 RequestWrapper 將請求流保存下來,并提供方法來多次讀取請求體的內容。

自定義 HttpServletRequest 包裝類 RequestWrapper 如下:

/*** HttpServletRequest 包裝類,允許在 Servlet 中多次讀取請求體內容* 重寫了 getInputStream()方法和 getReader() 方法,返回可以多次讀取的流。*/
public class RequestWrapper extends HttpServletRequestWrapper {private final byte[] body;/*** 構造 RequestWrapper 對象** @param request 原始 HttpServletRequest 對象* @param context 請求體內容*/public RequestWrapper(HttpServletRequest request, String context) {super(request);this.body = context.getBytes(StandardCharsets.UTF_8);}/*** 重寫 getInputStream 方法,返回經(jīng)過包裝后的 ServletInputStream 對象** @return 經(jīng)過包裝后的 ServletInputStream 對象*/@Overridepublic ServletInputStream getInputStream() {return new ServletInputStreamWrapper(new ByteArrayInputStream(body));}/*** 重寫 getReader 方法,返回經(jīng)過包裝后的 BufferedReader 對象** @return 經(jīng)過包裝后的 BufferedReader 對象*/@Overridepublic BufferedReader getReader() {return new BufferedReader(new InputStreamReader(getInputStream(), StandardCharsets.UTF_8));}/*** 私有內部類,用于包裝 ServletInputStream 對象*/private static class ServletInputStreamWrapper extends ServletInputStream {private final ByteArrayInputStream inputStream;/*** 構造函數(shù),傳入待包裝的 ByteArrayInputStream 對象** @param inputStream 待包裝的 ByteArrayInputStream 對象*/public ServletInputStreamWrapper(ByteArrayInputStream inputStream) {this.inputStream = inputStream;}/*** 重寫 read 方法,讀取流中的下一個字節(jié)** @return 讀取到的下一個字節(jié),如果已達到流的末尾,則返回-1*/@Overridepublic int read() {return inputStream.read();}/*** 覆蓋 isFinished 方法,指示流是否已完成讀取數(shù)據(jù)** @return 始終返回 false,表示流未完成讀取數(shù)據(jù)*/@Overridepublic boolean isFinished() {return false;}/*** 重寫 isReady 方法,指示流是否準備好進行讀取操作** @return 始終返回 false,表示流未準備好進行讀取操作*/@Overridepublic boolean isReady() {return false;}/*** 重寫 setReadListener 方法,設置讀取監(jiān)聽器** @param readListener 讀取監(jiān)聽器*/@Overridepublic void setReadListener(ReadListener readListener) {}}
}

自定義 HttpServletResponse 包裝類 ResponseWrapper

與請求流(即請求體)一樣,原始的 HttpServletResponse 對象中的響應流(即響應體)只能寫入一次。當服務器在向客戶端發(fā)送響應時,會將響應流寫入到網(wǎng)絡傳輸通道中,一旦寫入完畢,就無法再次修改或寫入。

因此我們需要通過自定義 ResponseWrapper 包裝原始的 HttpServletResponse 對象并重寫其輸出流或者輸出寫方法,從而實現(xiàn)對響應流的修改和控制。

自定義 HttpServletResponse 包裝類 ResponseWrapper 如下:

/*** HttpServletResponse 包裝類對,提供對響應數(shù)據(jù)的處理和操作。*/
public class ResponseWrapper extends HttpServletResponseWrapper {private final ByteArrayOutputStream outputStream;private ServletOutputStream servletOutputStream;private PrintWriter writer;/*** 構造函數(shù),傳入原始的 HttpServletResponse 對象** @param response 原始的 HttpServletResponse 對象*/public ResponseWrapper(HttpServletResponse response) {super(response);this.outputStream = new ByteArrayOutputStream();}/*** 重寫 getOutputStream 方法,返回經(jīng)過包裝后的 ServletOutputStream 對象** @return 經(jīng)過包裝后的 ServletOutputStream 對象*/@Overridepublic ServletOutputStream getOutputStream() {if (servletOutputStream == null) {servletOutputStream = new ServletOutputStreamWrapper(outputStream);}return servletOutputStream;}/*** 重寫 getWriter 方法,返回經(jīng)過包裝后的 PrintWriter 對象** @return 經(jīng)過包裝后的 PrintWriter 對象*/@Overridepublic PrintWriter getWriter() {if (writer == null) {writer = new PrintWriter(getOutputStream());}return writer;}/*** 獲取響應數(shù)據(jù),并指定字符集** @param charsetName 字符集名稱* @return 響應數(shù)據(jù)字符串*/public String getResponseData(String charsetName) {Charset charset = Charset.forName(charsetName);byte[] bytes = outputStream.toByteArray();return new String(bytes, charset);}/*** 設置響應數(shù)據(jù),并指定字符集** @param responseData 響應數(shù)據(jù)字符串* @param charsetName  字符集名稱*/public void setResponseData(String responseData, String charsetName) {Charset charset = Charset.forName(charsetName);byte[] bytes = responseData.getBytes(charset);outputStream.reset();try {outputStream.write(bytes);} catch (IOException e) {// 處理異常}setCharacterEncoding(charsetName);}/*** 私有內部類,用于包裝 ServletOutputStream 對象*/private static class ServletOutputStreamWrapper extends ServletOutputStream {private final ByteArrayOutputStream outputStream;/*** 構造函數(shù),傳入待包裝的 ByteArrayOutputStream 對象** @param outputStream 待包裝的 ByteArrayOutputStream 對象*/public ServletOutputStreamWrapper(ByteArrayOutputStream outputStream) {this.outputStream = outputStream;}/*** 重寫 write 方法,將指定字節(jié)寫入輸出流** @param b 字節(jié)*/@Overridepublic void write(int b) {outputStream.write(b);}/*** 重寫 isReady 方法,指示輸出流是否準備好接收寫入操作** @return 始終返回 false,表示輸出流未準備好接收寫入操作*/@Overridepublic boolean isReady() {return false;}/*** 重寫 setWriteListener 方法,設置寫入監(jiān)聽器** @param writeListener 寫入監(jiān)聽器*/@Overridepublic void setWriteListener(WriteListener writeListener) {}}
}

自定義過濾器 MiddlewareFilter

我們的需求是:在請求到達服務器之前,對請求參數(shù)進行修改;在響應返回之前,對響應結果進行處理。

對于這樣的需求,我們可以通過自定義過濾器來實現(xiàn)。大致實現(xiàn)思路如下:

  • 修改請求參數(shù)(請求體),我們可以:

    1. 獲取請求體內容。
    2. 修改請求體內容。
    3. 將修改后的請求對象替換原來的請求對象,以便后續(xù)獲取修改后的參數(shù)。
  • 修改響應結果(響應體),我們可以:

    1. 獲取響應數(shù)據(jù)。
    2. 對響應數(shù)據(jù)進行處理。
    3. 將修改后的數(shù)據(jù)作為最終結果返回。

同時為了確保每個請求在請求時只會被過濾一次,我們可以繼承 OncePerRequestFilter 來定義自己的過濾器。

最終,自定義過濾器如下:

public class MiddlewareFilter extends OncePerRequestFilter {@Overrideprotected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {// 1. 從 HttpServletRequest 對象中獲取請求體內容String requestBody = getRequestBody(httpServletRequest);// 2. 解析請求體內容為JSON對象JSONObject jsonBody = JSONObject.parseObject(requestBody);// 3. 修改請求體內容jsonBody.put("paramKey","paramValue");// 4. 包裝 HttpServletRequest 對象為自定義的 RequestWrapper 對象,以便后續(xù)的處理RequestWrapper requestWrapper = new RequestWrapper(httpServletRequest, jsonBody.toJSONString());// 5. 包裝 HttpServletResponse 對象為自定義的 ResponseWrapper 對象,以便后續(xù)的處理ResponseWrapper responseWrapper = new ResponseWrapper(httpServletResponse);// 6. 調用下一個過濾器或 ServletfilterChain.doFilter(requestWrapper, responseWrapper);// 7. 獲取響應數(shù)據(jù)String responseData = responseWrapper.getResponseData(StandardCharsets.UTF_8.name());// 8. 解析響應數(shù)據(jù)為JSON對象JSONObject jsonData = JSONObject.parseObject(responseData);// 9. 在這里可以對響應數(shù)據(jù)進行處理jsonData.put("responseKey", "responseValue");// 10. 將修改后的 JSON 對象轉換為字符串responseData = jsonData.toJSONString();// 11. 將修改后的 JSON 對象設置為最終的響應數(shù)據(jù)responseWrapper.setResponseData(responseData, StandardCharsets.UTF_8.name());// 12. 將響應數(shù)據(jù)寫入原始的響應對象,解決響應數(shù)據(jù)無法被多個過濾器處理問題OutputStream outputStream = httpServletResponse.getOutputStream();outputStream.write(responseData.getBytes(StandardCharsets.UTF_8));outputStream.flush();}/*** 獲取請求體內容。** @param request HttpServletRequest對象* @return 請求體內容* @throws IOException 如果讀取請求體內容時發(fā)生I/O異常*/private String getRequestBody(HttpServletRequest request) throws IOException {BufferedReader reader = new BufferedReader(new InputStreamReader(request.getInputStream()));StringBuilder sb = new StringBuilder();String line;while ((line = reader.readLine()) != null) {sb.append(line);}return sb.toString();}
}

配置過濾器

注解

通過 Java Servlet 3.0 規(guī)范中引入的 @WebFilter 注解配置過濾器。

@WebFilter 注解可以應用在實現(xiàn)了 Filter 接口或繼承自 OncePerRequestFilter 的類上,標識該類為過濾器,并指定過濾器的相關配置,包括攔截的 URL 路徑、執(zhí)行順序以及初始化參數(shù)等。

我們可以在 MiddlewareFilter 過濾器上使用 @WebFilter 注解注冊該過濾器并指定執(zhí)行該過濾器執(zhí)行的順序和攔截的 URL:

@WebFilter(value = "1000", urlPatterns = "/hello")
public class MiddlewareFilter extends OncePerRequestFilter {......
}
  • value:設置過濾器的執(zhí)行順序,數(shù)字越小,優(yōu)先級越高。
  • urlPatterns:指定要攔截的 URL 路徑,允許指定多個 URL 路徑urlPatterns = {"/hello","/hello1"}

還需要再啟動類上使用@ServletComponentScan注解掃描和注冊帶有 @WebServlet、@WebFilter@WebListener 注解的組件:

@ServletComponentScan
@SpringBootApplication
public class Demo1Application {public static void main(String[] args) {SpringApplication.run(Demo1Application.class, args);}}
配置類

除了注解的形式配置過濾器,我們還可以通過配置類的形式進行配置。

創(chuàng)建 FilterConfig 類用于配置需要注冊的過濾器,同時在類上添加 @Configuration 注解,標識該類為配置類,在項目啟動時 Spring 會自動掃描該類中的 Bean 定義,并將其加載到容器中:

@Configuration
public class FilterConfig {@Beanpublic FilterRegistrationBean<MiddlewareFilter> middlewareFilter() {FilterRegistrationBean<MiddlewareFilter> registration = new FilterRegistrationBean<>();registration.setFilter(new MiddlewareFilter()); // 設置過濾器實例registration.addUrlPatterns("/hello"); // 攔截的 URL 路徑registration.setOrder(1000); // 設置過濾器執(zhí)行順序(數(shù)字越小,越先執(zhí)行)return registration;}}

在類中我們定義了名為 middlewareFilter 的方法,用于注冊我們自定義的 MiddlewareFilter 過濾器。

在 方法中,創(chuàng)建了一個 FilterRegistrationBean 對象用于注冊和配置過濾器,并設置 MiddlewareFilter 對象作為過濾器實例,指定了過濾器要攔截的 URL 路徑,濾器執(zhí)行順序。

最后將 FilterRegistrationBean 對象返回,以便 Spring 自動進行注冊和管理。

編寫 Controller 測試

創(chuàng)建兩個接口,同樣的邏輯,接收一個請求體參數(shù) params,再將接收的參數(shù)以 JSON 格式返回:

@RestController
public class BasicController {/*** 處理 /hello 請求的方法* @param params 請求體參數(shù),以鍵值對的形式傳遞* @return 經(jīng)過轉換后的 JSONObject 對象*/@PostMapping("/hello")public JSONObject hello(@RequestBody Map<String, Object> params) {return JSONObject.parseObject(JSON.toJSONString(params));}@PostMapping("/hello1")public JSONObject hello1(@RequestBody Map<String,Object> params) {return JSONObject.parseObject(JSON.toJSONString(params));}
}

啟動項目,在 ApiFox 中分別以同樣的請求參數(shù)發(fā)送 POST 請求調用 /hello/hello1 接口:

  • 請求參數(shù):

    {"name": "hello","age": 20
    }
    
  • /hello 接口返回結果:

    {"paramKey": "paramValue","responseKey": "responseValue","name": "hello","age": 20
    }
    
  • /hello1 接口返回結果:

    {"name": "hello","age": 20
    }
    

復制多個 MiddlewareFilter 過濾器模擬多層過濾器修改請求體參數(shù)和返回結果,測試結果如下:

{"paramKey": "paramValue",	//過濾器1"responseKey2": "responseValue2",	//過濾器2"responseKey": "responseValue",	//過濾器2"paramKey2": "paramValue2",	//過濾器1"name": "hello","age": 20
}
http://www.risenshineclean.com/news/49087.html

相關文章:

  • 網(wǎng)站備案密碼怎么找回自己開一個培訓機構流程
  • 蘇州市建設工程交易中心網(wǎng)站河南百度關鍵詞優(yōu)化排名軟件
  • 小程序怎么做微網(wǎng)站鏈接微信上如何投放廣告
  • 營銷手機網(wǎng)站seo技術自學
  • 簡述使用asp建設動態(tài)網(wǎng)站頁面優(yōu)化
  • 招聘網(wǎng)站設計方案培訓網(wǎng)站搭建
  • 簡約型網(wǎng)站建設站外推廣方式有哪些
  • 企業(yè)為什么做網(wǎng)站如何做網(wǎng)站賺錢
  • saas 做網(wǎng)站qq群怎么優(yōu)化排名靠前
  • 免費個人簡歷表電子版武漢企業(yè)seo推廣
  • 做網(wǎng)站平臺成本石家莊今天最新新聞頭條
  • 網(wǎng)站如何做點擊鏈接廣州aso優(yōu)化公司 有限公司
  • c2b模式的電商平臺網(wǎng)站有哪些好看的友情鏈接代碼
  • 友情鏈接添加在網(wǎng)站中有什么用友情鏈接交換平臺有哪些
  • 怎樣做外貿網(wǎng)站建設怎么出售友情鏈接
  • 找個網(wǎng)站開發(fā)的師傅外鏈工具xg
  • 易語言可以做網(wǎng)站后端東莞市優(yōu)速網(wǎng)絡科技有限公司
  • 舟山網(wǎng)站開發(fā)網(wǎng)絡營銷和網(wǎng)上銷售的區(qū)別
  • 高端建設網(wǎng)站公司哪家好網(wǎng)頁版百度云
  • 在家建設一個網(wǎng)站需要什么材料企業(yè)網(wǎng)站設計畢業(yè)論文
  • 武漢網(wǎng)站建設公司 排名如何免費制作自己的網(wǎng)站
  • wordpress網(wǎng)站秒開上海seo網(wǎng)站優(yōu)化
  • 奧鵬網(wǎng)頁設計與網(wǎng)站建設百度開發(fā)者平臺
  • 怎么做自己優(yōu)惠券網(wǎng)站天門seo
  • 阿里云服務器搭建多個網(wǎng)站seo推廣經(jīng)驗
  • 企業(yè)網(wǎng)站 夢織百度網(wǎng)址大全首頁
  • 圖書館網(wǎng)站建設目標seo優(yōu)化 搜 盈seo公司
  • wordpress自定義末班寧波seo優(yōu)化公司排名
  • 做淘寶客新增網(wǎng)站推廣被逆冬seo課程欺騙了
  • 北京電商網(wǎng)站開發(fā)公司網(wǎng)絡熱詞作文