1. 引言
本文將探討如何在 Spring Boot 過濾器中檢索 <em >ServletResponse</em> 的響應體。
本質上,我們將定義問題,然後使用一種解決方案,通過緩存響應體使其在 Spring Boot 過濾器中可用。現在,讓我們開始吧。
2. 瞭解問題
首先,讓我們瞭解我們試圖解決的問題。
當使用 Spring Boot 過濾器時,從 ServletResponse 中訪問響應體可能會比較棘手。 這是因為響應體是在過濾器鏈完成執行後寫入輸出流中的,因此並非直接可訪問。
然而,某些操作,例如生成哈希簽名,需要在將它發送到客户端之前需要響應體的內容。因此,我們需要找到一種讀取響應體的方法。
3. 在過濾器中使用 ContentCachingResponseWrapper
為了解決之前定義的難題,我們將創建一個自定義過濾器並使用 Spring Framework 提供的 ContentCachingResponseWrapper 類:
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
ContentCachingResponseWrapper responseCacheWrapperObject =
new ContentCachingResponseWrapper((HttpServletResponse) servletResponse);
filterChain.doFilter(servletRequest, responseCacheWrapperObject);
byte[] responseBody = responseCacheWrapperObject.getContentAsByteArray();
MessageDigest md5Digest = MessageDigest.getInstance("MD5");
byte[] md5Hash = md5Digest.digest(responseBody);
String md5HashString = DatatypeConverter.printHexBinary(md5Hash);
responseCacheWrapperObject.getResponse().setHeader("Response-Body-MD5", md5HashString);
// ...
}簡而言之,包裝類允許我們封裝 HttpServletResponse,緩存響應體內容並調用 doFilter() 將請求傳遞給下一個過濾器。
請記住,在這裏必須調用 doFilter()。否則,傳入的請求將不會到達 Spring 過濾器鏈中的下一個過濾器,應用程序也不會按照預期處理請求。事實上,未調用 doFilter() 違反 Servlet 規範。
此外,必須調用 doFilter(),並傳入 responseCacheWrapperObject。否則,響應體將不會被緩存。簡而言之,ContentCachingResponseWrapper 將過濾器置於響應輸出流和客户端之間的 HTTP 請求之間,因此,在創建響應體輸出流時,即在 doFilter() 調用之後,內容將可供過濾器處理。
使用包裝器後,響應體將通過 getContentAsByteArray() 方法在過濾器中可用。我們使用此方法計算 MD5 哈希值。
首先,我們使用 MessageDigest 類計算響應體的 MD5 哈希值。其次,我們將字節數組轉換為十六進制字符串。第三,我們使用 setHeader() 方法將結果哈希字符串設置為響應對象的頭部。
如果需要,我們可以將字節數組轉換為字符串,使響應體內容更明確。
最後,在退出 doFilter() 方法之前,必須調用 copyBodyToResponse(),將更新後的響應體複製回原始響應:
responseCacheWrapperObject.copyBodyToResponse();在退出 doFilter() 方法之前,必須調用 copyBodyToResponse()。否則,客户端將無法收到完整的響應。
4. 配置過濾器
現在,我們需要在 Spring Boot 中添加過濾器:
@Bean
public FilterRegistrationBean loggingFilter() {
FilterRegistrationBean registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new MD5Filter());
return registrationBean;
}在這裏,我們正在配置一個 FilterRegistrationBean,並使用我們之前創建的過濾器進行實現。
5. 測試 MD5
最後,我們可以使用 Spring 中的集成測試來驗證一切都按預期工作:
@Test
void whenExampleApiCallThenResponseHasMd5Header() throws Exception {
String endpoint = "/api/example";
String expectedResponse = "Hello, World!";
String expectedMD5 = getMD5Hash(expectedResponse);
MvcResult mvcResult = mockMvc.perform(get(endpoint).accept(MediaType.TEXT_PLAIN_VALUE))
.andExpect(status().isOk())
.andReturn();
String md5Header = mvcResult.getResponse()
.getHeader("Response-Body-MD5");
assertThat(md5Header).isEqualTo(expectedMD5);
}在這裏,我們調用了 /api/example 控制器,它在響應體中返回“Hello, World!”文本。我們定義了 getMD5Hash() 方法,該方法將響應轉換為與過濾器中使用的類似 MD5 值:
private String getMD5Hash(String input) throws NoSuchAlgorithmException {
MessageDigest md5Digest = MessageDigest.getInstance("MD5");
byte[] md5Hash = md5Digest.digest(input.getBytes(StandardCharsets.UTF_8));
return DatatypeConverter.printHexBinary(md5Hash);
}6. 結論
在本文中,我們學習瞭如何使用 Spring Boot 過濾器和 <em >ContentCachingResponseWrapper</em> 類來從 <em >ServletResponse</em> 中檢索響應體。我們利用這個機制來在 HTTP 響應頭中實現響應體的 MD5 編碼。