serverless-java-container
serverless-java-container copied to clipboard
Handling ResponseStatusException Results in 502
- Framework version: 1.5 / 5.2.6-RELEASE
- Implementations: Spring
Scenario
I am throwing a ResponseStatusException to handle 404's, 500's, etc but no matter when I throw one I am getting the following response with a 502 from the call:
{
"message": "Gateway timeout"
}
This is an example of how I am creating the ResponseStatusException:
if(queryResults.getSize() == 0) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, String.format("Unable To Find User for Email Address %s", email));
} else {
// Do something else
}
I am seeing the following in my logs so I know the exception is being thrown properly:
2020-05-04 21:20:04 f1ab9c4e-75ee-457f-b222-dedc69daf8f5 ERROR LambdaContainerHandler - Error while handling request
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.springframework.web.server.ResponseStatusException: 404 NOT_FOUND "Unable To Find User for Email Address [email protected]"
It doesn't seem to handle the ResponseStatusException being thrown. I added the following to my application config class:
/*
* optimization - avoids creating default exception resolvers; not required as the serverless container handles
* all exceptions
*
* By default, an ExceptionHandlerExceptionResolver is created which creates many dependent object, including
* an expensive ObjectMapper instance.
*
* To enable custom @ControllerAdvice classes remove this bean.
*/
@Bean
public HandlerExceptionResolver handlerExceptionResolver() {
return new HandlerExceptionResolver() {
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
return null;
}
};
}
Shouldn't the container handle the ResponseStatusException automatically?
Thanks, Steve
Expected behavior
The proper response code and response returned
Actual behavior
Always retuning a 502 and Bad Gateway reponse
+1
I attempted to replicate this in the SpringBoot 2 unit tests and it seems to work fine, the test below passes. Not sure what could be missing. Any hints?
Controller
@GetMapping(value = "/ex/customstatus")
public String throw404Exception() {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, EX_MESSAGE);
}
Test method
@Test
public void throw404Exception_callEndpoint_expect404StatusCode() {
LambdaContainerHandler.getContainerConfig().setDefaultContentCharset(ContainerConfig.DEFAULT_CONTENT_CHARSET);
AwsProxyRequestBuilder req = new AwsProxyRequestBuilder("/ex/customstatus", "GET");
AwsProxyResponse resp = handler.handleRequest(req, lambdaContext);
assertNotNull(resp);
assertEquals(404, resp.getStatusCode());
}
It does work fine with Spring Boot. It's not working with Spring MVC. Sorry for not being clearer in my original post.
Thanks @szaluk. I just added the same test to the Spring echo tests app (using Spring version 5.2.5.RELEASE
) and it also works fine. Wonder if there's anything different in the app config. Let me know if you see anything in the code.
Let me know if you can see what the difference is between my app's configuration and yours @szaluk, @dtelaroli
Removing this from 1.5.1 release since I still cannot replicate and the patch release needs to go out.
This really doesn´t work as posted by @szaluk. AwsProxyExceptionHandler handle exceptions with fixed error messages no matter what exception you throw.
Note that this is only true with the recommended approach of not using the Spring default ExceptionHandlerExceptionResolver.
I wonder how to set my own custom AwsProxyExceptionHandler to overcome this.
Other options are using the @ControllerAdvice with the default ExceptionHandlerExceptionResolver or creating a simpler implementation of HandlerExceptionResolver like in the example below:
public class CustomExceptionResolver implements HandlerExceptionResolver {
private static Logger log = LoggerFactory.getLogger(CustomExceptionResolver.class);
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler,
Exception ex) {
ErrorModel errorModel = new ErrorModel("Unknown error");
int status = HttpStatus.INTERNAL_SERVER_ERROR.value();
if (ex instanceof ResponseStatusException) {
ResponseStatusException responseException = (ResponseStatusException) ex;
status = responseException.getRawStatusCode();
errorModel.setMessage(responseException.getReason());
}
/* resolve other exceptions... */
String body = null;
try {
body = LambdaContainerHandler.getObjectMapper().writeValueAsString(errorModel);
} catch (JsonProcessingException e) {
log.error("Could not produce error JSON", e);
body = "{ \"message\": \"" + errorModel.getMessage() + "\" }";
}
try {
response.setStatus(status);
response.setContentType(ContentType.APPLICATION_JSON.getMimeType());
response.setCharacterEncoding(StandardCharsets.UTF_8.name());
response.getWriter().write(body);
} catch (IOException e) {
log.error("Could not write client http body", e);
}
return new ModelAndView();
}
}
Framework version: 1.6 / Srping 5.3.9
SpringProxyHandlerBuilder
and SpringBootProxyHandlerBuilder
allow to provide an ExceptionHandler
using the exceptionHandler()
method. By default AwsProxyExceptionHandler
will be used.
We are open for suggestions and will also review PRs that optimize it.