serverless-java-container icon indicating copy to clipboard operation
serverless-java-container copied to clipboard

Handling ResponseStatusException Results in 502

Open szaluk opened this issue 4 years ago • 8 comments

  • 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

szaluk avatar May 04 '20 21:05 szaluk

+1

dtelaroli avatar May 20 '20 18:05 dtelaroli

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());
}

sapessi avatar Jul 10 '20 21:07 sapessi

It does work fine with Spring Boot. It's not working with Spring MVC. Sorry for not being clearer in my original post.

szaluk avatar Jul 10 '20 21:07 szaluk

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.

sapessi avatar Jul 10 '20 22:07 sapessi

Let me know if you can see what the difference is between my app's configuration and yours @szaluk, @dtelaroli

sapessi avatar Jul 11 '20 20:07 sapessi

Removing this from 1.5.1 release since I still cannot replicate and the patch release needs to go out.

sapessi avatar Jul 15 '20 17:07 sapessi

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

mrgatto avatar Sep 09 '21 17:09 mrgatto

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.

deki avatar Dec 29 '21 18:12 deki