From 44ef962a82cf3a6ac6b18e263724a1d774ef118e Mon Sep 17 00:00:00 2001 From: Harry9656 Date: Wed, 10 Apr 2024 19:25:52 +0200 Subject: [PATCH] BAEL-7676: Get the Response Body in Spring Boot Filter (#16186) --- .../baeldung/responsebody/FilterConfig.java | 16 +++++++ .../responsebody/HelloWorldController.java | 15 ++++++ .../com/baeldung/responsebody/MD5Filter.java | 39 +++++++++++++++ .../responsebody/ResponseBodyApplication.java | 12 +++++ .../responsebody/ResponseBodyUnitTest.java | 48 +++++++++++++++++++ 5 files changed, 130 insertions(+) create mode 100644 spring-boot-modules/spring-boot-basic-customization-3/src/main/java/com/baeldung/responsebody/FilterConfig.java create mode 100644 spring-boot-modules/spring-boot-basic-customization-3/src/main/java/com/baeldung/responsebody/HelloWorldController.java create mode 100644 spring-boot-modules/spring-boot-basic-customization-3/src/main/java/com/baeldung/responsebody/MD5Filter.java create mode 100644 spring-boot-modules/spring-boot-basic-customization-3/src/main/java/com/baeldung/responsebody/ResponseBodyApplication.java create mode 100644 spring-boot-modules/spring-boot-basic-customization-3/src/test/java/com/baeldung/responsebody/ResponseBodyUnitTest.java diff --git a/spring-boot-modules/spring-boot-basic-customization-3/src/main/java/com/baeldung/responsebody/FilterConfig.java b/spring-boot-modules/spring-boot-basic-customization-3/src/main/java/com/baeldung/responsebody/FilterConfig.java new file mode 100644 index 0000000000..212dbd8626 --- /dev/null +++ b/spring-boot-modules/spring-boot-basic-customization-3/src/main/java/com/baeldung/responsebody/FilterConfig.java @@ -0,0 +1,16 @@ +package com.baeldung.responsebody; + +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class FilterConfig { + + @Bean + public FilterRegistrationBean loggingFilter() { + FilterRegistrationBean registrationBean = new FilterRegistrationBean<>(); + registrationBean.setFilter(new MD5Filter()); + return registrationBean; + } +} \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-basic-customization-3/src/main/java/com/baeldung/responsebody/HelloWorldController.java b/spring-boot-modules/spring-boot-basic-customization-3/src/main/java/com/baeldung/responsebody/HelloWorldController.java new file mode 100644 index 0000000000..657b7972bb --- /dev/null +++ b/spring-boot-modules/spring-boot-basic-customization-3/src/main/java/com/baeldung/responsebody/HelloWorldController.java @@ -0,0 +1,15 @@ +package com.baeldung.responsebody; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/api/example") +public class HelloWorldController { + + @GetMapping + public String getExample() { + return "Hello, World!"; + } +} \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-basic-customization-3/src/main/java/com/baeldung/responsebody/MD5Filter.java b/spring-boot-modules/spring-boot-basic-customization-3/src/main/java/com/baeldung/responsebody/MD5Filter.java new file mode 100644 index 0000000000..8f6e86f17e --- /dev/null +++ b/spring-boot-modules/spring-boot-basic-customization-3/src/main/java/com/baeldung/responsebody/MD5Filter.java @@ -0,0 +1,39 @@ +package com.baeldung.responsebody; + +import java.io.IOException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.logging.Logger; + +import org.springframework.stereotype.Component; +import org.springframework.web.util.ContentCachingResponseWrapper; + +import jakarta.servlet.Filter; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.ServletResponse; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.xml.bind.DatatypeConverter; + +@Component +public class MD5Filter implements Filter { + + @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(); + + try { + MessageDigest md5Digest = MessageDigest.getInstance("MD5"); + byte[] md5Hash = md5Digest.digest(responseBody); + String md5HashString = DatatypeConverter.printHexBinary(md5Hash); + responseCacheWrapperObject.setHeader("Response-Body-MD5", md5HashString); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } + responseCacheWrapperObject.copyBodyToResponse(); + } +} \ No newline at end of file diff --git a/spring-boot-modules/spring-boot-basic-customization-3/src/main/java/com/baeldung/responsebody/ResponseBodyApplication.java b/spring-boot-modules/spring-boot-basic-customization-3/src/main/java/com/baeldung/responsebody/ResponseBodyApplication.java new file mode 100644 index 0000000000..4084dffccf --- /dev/null +++ b/spring-boot-modules/spring-boot-basic-customization-3/src/main/java/com/baeldung/responsebody/ResponseBodyApplication.java @@ -0,0 +1,12 @@ +package com.baeldung.responsebody; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication(scanBasePackages = "com.baeldung.responsebody") +public class ResponseBodyApplication { + + public static void main(String[] args) { + SpringApplication.run(ResponseBodyApplication.class, args); + } +} diff --git a/spring-boot-modules/spring-boot-basic-customization-3/src/test/java/com/baeldung/responsebody/ResponseBodyUnitTest.java b/spring-boot-modules/spring-boot-basic-customization-3/src/test/java/com/baeldung/responsebody/ResponseBodyUnitTest.java new file mode 100644 index 0000000000..bebbd5a4b9 --- /dev/null +++ b/spring-boot-modules/spring-boot-basic-customization-3/src/test/java/com/baeldung/responsebody/ResponseBodyUnitTest.java @@ -0,0 +1,48 @@ +package com.baeldung.responsebody; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; + +import jakarta.xml.bind.DatatypeConverter; + +@SpringBootTest(classes = ResponseBodyApplication.class) +@AutoConfigureMockMvc +class ResponseBodyUnitTest { + + @Autowired + private MockMvc mockMvc; + + @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); + } + + 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); + } +} \ No newline at end of file