Infer HTTP 404 from empty Optional/Publisher types
This commit handles "empty" cases for `ResponseEntity` controller
handler return types when wrapped with a `java.util.Optional` in Spring
MVC or a single `Publisher` like `Mono`.
Given the following example for Spring MVC:
```
@GetMapping("/user")
public Optional<ResponseEntity<User>> fetchUser() {
Optional<User> user = //...
return user.map(ResponseEntity::ok);
}
```
If the resulting `Optional` is empty, Spring MVC will infer a
`ResponseEntity` with an empty body and a 404 HTTP response status.
The same reasoning is applied to Spring WebFlux with Publisher types:
```
@GetMapping("/user")
public Mono<ResponseEntity<User>> fetchUser() {
Mono<User> user = //...
return user.map(ResponseEntity::ok);
}
```
This feature is only valid for `HttpEntity` return types and does not
apply to `@ResponseBody` controller handlers.
Issue: SPR-13281
This commit is contained in:
@@ -24,6 +24,7 @@ import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
@@ -33,6 +34,7 @@ import org.springframework.core.ResolvableType;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.RequestEntity;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
@@ -121,8 +123,9 @@ public class HttpEntityMethodProcessor extends AbstractMessageConverterMethodPro
|
||||
|
||||
@Override
|
||||
public boolean supportsReturnType(MethodParameter returnType) {
|
||||
return (HttpEntity.class.isAssignableFrom(returnType.getParameterType()) &&
|
||||
!RequestEntity.class.isAssignableFrom(returnType.getParameterType()));
|
||||
Class<?> parameterType = returnType.nestedIfOptional().getNestedParameterType();
|
||||
return (HttpEntity.class.isAssignableFrom(parameterType) &&
|
||||
!RequestEntity.class.isAssignableFrom(parameterType));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -150,7 +153,7 @@ public class HttpEntityMethodProcessor extends AbstractMessageConverterMethodPro
|
||||
|
||||
@Nullable
|
||||
private Type getHttpEntityType(MethodParameter parameter) {
|
||||
Assert.isAssignable(HttpEntity.class, parameter.getParameterType());
|
||||
Assert.isAssignable(HttpEntity.class, parameter.getNestedParameterType());
|
||||
Type parameterType = parameter.getGenericParameterType();
|
||||
if (parameterType instanceof ParameterizedType) {
|
||||
ParameterizedType type = (ParameterizedType) parameterType;
|
||||
@@ -169,6 +172,7 @@ public class HttpEntityMethodProcessor extends AbstractMessageConverterMethodPro
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
|
||||
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
|
||||
|
||||
@@ -180,6 +184,12 @@ public class HttpEntityMethodProcessor extends AbstractMessageConverterMethodPro
|
||||
ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
|
||||
ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);
|
||||
|
||||
if (returnType.getParameterType() == Optional.class) {
|
||||
Optional<HttpEntity<?>> optionalEntity = (Optional<HttpEntity<?>>) returnValue;
|
||||
returnValue = optionalEntity.orElseGet(() -> new ResponseEntity<>(HttpStatus.NOT_FOUND));
|
||||
returnType = returnType.nestedIfOptional();
|
||||
}
|
||||
|
||||
Assert.isInstanceOf(HttpEntity.class, returnValue);
|
||||
HttpEntity<?> responseEntity = (HttpEntity<?>) returnValue;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user