Invoke handleEmptyBody if there is no content-type

Closes gh-30522
This commit is contained in:
rstoyanchev
2023-11-09 12:52:14 +00:00
parent d8ed7c7906
commit d8441fc80c
2 changed files with 104 additions and 17 deletions

View File

@@ -25,6 +25,7 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.apache.groovy.util.Maps;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -45,6 +46,8 @@ import org.springframework.lang.Nullable;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.context.support.StaticWebApplicationContext;
import org.springframework.web.method.HandlerMethod;
@@ -109,7 +112,7 @@ public class RequestMappingHandlerAdapterTests {
@Test
public void cacheControlWithoutSessionAttributes() throws Exception {
HandlerMethod handlerMethod = handlerMethod(new SimpleController(), "handle");
HandlerMethod handlerMethod = handlerMethod(new TestController(), "handle");
this.handlerAdapter.setCacheSeconds(100);
this.handlerAdapter.afterPropertiesSet();
@@ -197,7 +200,7 @@ public class RequestMappingHandlerAdapterTests {
this.webAppContext.registerSingleton("maa", ModelAttributeAdvice.class);
this.webAppContext.refresh();
HandlerMethod handlerMethod = handlerMethod(new SimpleController(), "handle");
HandlerMethod handlerMethod = handlerMethod(new TestController(), "handle");
this.handlerAdapter.afterPropertiesSet();
ModelAndView mav = this.handlerAdapter.handle(this.request, this.response, handlerMethod);
@@ -210,7 +213,7 @@ public class RequestMappingHandlerAdapterTests {
this.webAppContext.registerPrototype("maa", ModelAttributeAdvice.class);
this.webAppContext.refresh();
HandlerMethod handlerMethod = handlerMethod(new SimpleController(), "handle");
HandlerMethod handlerMethod = handlerMethod(new TestController(), "handle");
this.handlerAdapter.afterPropertiesSet();
Map<String, Object> model1 = this.handlerAdapter.handle(this.request, this.response, handlerMethod).getModel();
Map<String, Object> model2 = this.handlerAdapter.handle(this.request, this.response, handlerMethod).getModel();
@@ -226,7 +229,7 @@ public class RequestMappingHandlerAdapterTests {
this.webAppContext.setParent(parent);
this.webAppContext.refresh();
HandlerMethod handlerMethod = handlerMethod(new SimpleController(), "handle");
HandlerMethod handlerMethod = handlerMethod(new TestController(), "handle");
this.handlerAdapter.afterPropertiesSet();
ModelAndView mav = this.handlerAdapter.handle(this.request, this.response, handlerMethod);
@@ -240,7 +243,7 @@ public class RequestMappingHandlerAdapterTests {
this.webAppContext.registerSingleton("manupa", ModelAttributeNotUsedPackageAdvice.class);
this.webAppContext.refresh();
HandlerMethod handlerMethod = handlerMethod(new SimpleController(), "handle");
HandlerMethod handlerMethod = handlerMethod(new TestController(), "handle");
this.handlerAdapter.afterPropertiesSet();
ModelAndView mav = this.handlerAdapter.handle(this.request, this.response, handlerMethod);
@@ -249,9 +252,7 @@ public class RequestMappingHandlerAdapterTests {
assertThat(mav.getModel().get("attr3")).isNull();
}
// SPR-10859
@Test
@Test // gh-15486
public void responseBodyAdvice() throws Exception {
List<HttpMessageConverter<?>> converters = new ArrayList<>();
converters.add(new MappingJackson2HttpMessageConverter());
@@ -263,7 +264,7 @@ public class RequestMappingHandlerAdapterTests {
this.request.addHeader("Accept", MediaType.APPLICATION_JSON_VALUE);
this.request.setParameter("c", "callback");
HandlerMethod handlerMethod = handlerMethod(new SimpleController(), "handleBadRequest");
HandlerMethod handlerMethod = handlerMethod(new TestController(), "handleBadRequest");
this.handlerAdapter.afterPropertiesSet();
this.handlerAdapter.handle(this.request, this.response, handlerMethod);
@@ -271,6 +272,19 @@ public class RequestMappingHandlerAdapterTests {
assertThat(this.response.getContentAsString()).isEqualTo("{\"status\":400,\"message\":\"body\"}");
}
@Test // gh-30522
public void responseBodyAdviceWithEmptyBody() throws Exception {
this.webAppContext.registerBean("rba", EmptyBodyAdvice.class);
this.webAppContext.refresh();
HandlerMethod handlerMethod = handlerMethod(new TestController(), "handleBody", Map.class);
this.handlerAdapter.afterPropertiesSet();
this.handlerAdapter.handle(this.request, this.response, handlerMethod);
assertThat(this.response.getStatus()).isEqualTo(200);
assertThat(this.response.getContentAsString()).isEqualTo("Body: {foo=bar}");
}
private HandlerMethod handlerMethod(Object handler, String methodName, Class<?>... paramTypes) throws Exception {
Method method = handler.getClass().getDeclaredMethod(methodName, paramTypes);
return new InvocableHandlerMethod(handler, method);
@@ -284,7 +298,7 @@ public class RequestMappingHandlerAdapterTests {
@SuppressWarnings("unused")
private static class SimpleController {
private static class TestController {
@ModelAttribute
public void addAttributes(Model model) {
@@ -296,14 +310,17 @@ public class RequestMappingHandlerAdapterTests {
}
public ResponseEntity<Map<String, String>> handleWithResponseEntity() {
return new ResponseEntity<>(Collections.singletonMap(
"foo", "bar"), HttpStatus.OK);
return new ResponseEntity<>(Collections.singletonMap("foo", "bar"), HttpStatus.OK);
}
public ResponseEntity<String> handleBadRequest() {
return new ResponseEntity<>("body", HttpStatus.BAD_REQUEST);
}
@ResponseBody
public String handleBody(@Nullable @RequestBody Map<String, String> body) {
return "Body: " + body;
}
}
@@ -360,6 +377,7 @@ public class RequestMappingHandlerAdapterTests {
}
}
/**
* This class additionally implements {@link RequestBodyAdvice} solely for the purpose
* of verifying that controller advice implementing both {@link ResponseBodyAdvice}
@@ -368,7 +386,8 @@ public class RequestMappingHandlerAdapterTests {
* @see <a href="https://github.com/spring-projects/spring-framework/pull/22638">gh-22638</a>
*/
@ControllerAdvice
private static class ResponseCodeSuppressingAdvice extends AbstractMappingJacksonResponseBodyAdvice implements RequestBodyAdvice {
private static class ResponseCodeSuppressingAdvice
extends AbstractMappingJacksonResponseBodyAdvice implements RequestBodyAdvice {
@Override
protected void beforeBodyWriteInternal(MappingJacksonValue bodyContainer, MediaType contentType,
@@ -405,12 +424,42 @@ public class RequestMappingHandlerAdapterTests {
}
@Override
public Object handleEmptyBody(@Nullable Object body, HttpInputMessage inputMessage, MethodParameter parameter,
public Object handleEmptyBody(Object body, HttpInputMessage inputMessage, MethodParameter parameter,
Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
return "default value for empty body";
}
}
@ControllerAdvice
private static class EmptyBodyAdvice implements RequestBodyAdvice {
@Override
public boolean supports(MethodParameter param, Type targetType, Class<? extends HttpMessageConverter<?>> type) {
return true;
}
@Override
public HttpInputMessage beforeBodyRead(HttpInputMessage message, MethodParameter param,
Type targetType, Class<? extends HttpMessageConverter<?>> type) {
throw new UnsupportedOperationException();
}
@Override
public Object afterBodyRead(Object body, HttpInputMessage message, MethodParameter param,
Type targetType, Class<? extends HttpMessageConverter<?>> type) {
throw new UnsupportedOperationException();
}
@Override
public Object handleEmptyBody(Object body, HttpInputMessage message, MethodParameter param,
Type targetType, Class<? extends HttpMessageConverter<?>> type) {
return Maps.of("foo", "bar");
}
}
}