Skip to content

Commit

Permalink
Merge pull request #691 from awslabs/690-exception-handler
Browse files Browse the repository at this point in the history
handle Spring's ErrorResponse in separate default ExceptionHandlers
  • Loading branch information
deki authored Nov 29, 2023
2 parents 040112a + ffd858e commit 3c7dbae
Show file tree
Hide file tree
Showing 8 changed files with 99 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import com.amazonaws.serverless.proxy.model.Headers;

import com.fasterxml.jackson.core.JsonProcessingException;
import jakarta.ws.rs.core.Response;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -47,22 +48,22 @@ public class AwsProxyExceptionHandler
// Constants
//-------------------------------------------------------------

static final String INTERNAL_SERVER_ERROR = "Internal Server Error";
static final String GATEWAY_TIMEOUT_ERROR = "Gateway timeout";
static final String INTERNAL_SERVER_ERROR = Response.Status.INTERNAL_SERVER_ERROR.getReasonPhrase();
static final String GATEWAY_TIMEOUT_ERROR = Response.Status.GATEWAY_TIMEOUT.getReasonPhrase();


//-------------------------------------------------------------
// Variables - Private - Static
//-------------------------------------------------------------

private static Headers headers = new Headers();
protected static final Headers HEADERS = new Headers();

//-------------------------------------------------------------
// Constructors
//-------------------------------------------------------------

static {
headers.putSingle(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON);
HEADERS.putSingle(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON);
}


Expand All @@ -79,9 +80,9 @@ public AwsProxyResponse handle(Throwable ex) {
// output to go to the stderr.
ex.printStackTrace();
if (ex instanceof InvalidRequestEventException || ex instanceof InternalServerErrorException) {
return new AwsProxyResponse(500, headers, getErrorJson(INTERNAL_SERVER_ERROR));
return new AwsProxyResponse(500, HEADERS, getErrorJson(INTERNAL_SERVER_ERROR));
} else {
return new AwsProxyResponse(502, headers, getErrorJson(GATEWAY_TIMEOUT_ERROR));
return new AwsProxyResponse(502, HEADERS, getErrorJson(GATEWAY_TIMEOUT_ERROR));
}
}

Expand All @@ -98,7 +99,7 @@ public void handle(Throwable ex, OutputStream stream) throws IOException {
// Methods - Protected
//-------------------------------------------------------------

String getErrorJson(String message) {
protected String getErrorJson(String message) {

try {
return LambdaContainerHandler.getObjectMapper().writeValueAsString(new ErrorModel(message));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ public Builder defaultProxy() {
.requestReader((RequestReader<RequestType, ContainerRequestType>) new AwsProxyHttpServletRequestReader())
.responseWriter((ResponseWriter<AwsHttpServletResponse, ResponseType>) new AwsProxyHttpServletResponseWriter())
.securityContextWriter((SecurityContextWriter<RequestType>) new AwsProxySecurityContextWriter())
.exceptionHandler((ExceptionHandler<ResponseType>) new AwsProxyExceptionHandler())
.exceptionHandler(defaultExceptionHandler())
.requestTypeClass((Class<RequestType>) AwsProxyRequest.class)
.responseTypeClass((Class<ResponseType>) AwsProxyResponse.class);
return self();
Expand All @@ -112,13 +112,17 @@ public Builder defaultHttpApiV2Proxy() {
.requestReader((RequestReader<RequestType, ContainerRequestType>) new AwsHttpApiV2HttpServletRequestReader())
.responseWriter((ResponseWriter<AwsHttpServletResponse, ResponseType>) new AwsProxyHttpServletResponseWriter(true))
.securityContextWriter((SecurityContextWriter<RequestType>) new AwsHttpApiV2SecurityContextWriter())
.exceptionHandler((ExceptionHandler<ResponseType>) new AwsProxyExceptionHandler())
.exceptionHandler(defaultExceptionHandler())
.requestTypeClass((Class<RequestType>) HttpApiV2ProxyRequest.class)
.responseTypeClass((Class<ResponseType>) AwsProxyResponse.class);
return self();

}

protected ExceptionHandler<ResponseType> defaultExceptionHandler() {
return (ExceptionHandler<ResponseType>) new AwsProxyExceptionHandler();
}

/**
* Sets the initialization wrapper to be used by the {@link ServletLambdaContainerHandlerBuilder#buildAndInitialize()}
* method to start the framework implementations
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ void errorMessage_InternalServerError_staticString() {

@Test
void errorMessage_GatewayTimeout_staticString() {
assertEquals("Gateway timeout", AwsProxyExceptionHandler.GATEWAY_TIMEOUT_ERROR);
assertEquals("Gateway Timeout", AwsProxyExceptionHandler.GATEWAY_TIMEOUT_ERROR);
}

@Test
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.amazonaws.serverless.proxy.spring;

import com.amazonaws.serverless.proxy.AwsProxyExceptionHandler;
import com.amazonaws.serverless.proxy.ExceptionHandler;
import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
import org.springframework.web.ErrorResponse;

/**
* This ExceptionHandler implementation enhances the standard AwsProxyExceptionHandler
* by mapping additional details from org.springframework.web.ErrorResponse
*/
public class SpringAwsProxyExceptionHandler extends AwsProxyExceptionHandler
implements ExceptionHandler<AwsProxyResponse> {
@Override
public AwsProxyResponse handle(Throwable ex) {
if (ex instanceof ErrorResponse) {
return new AwsProxyResponse(((ErrorResponse) ex).getStatusCode().value(),
HEADERS, getErrorJson(ex.getMessage()));
} else {
return super.handle(ex);
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
package com.amazonaws.serverless.proxy.spring;

import com.amazonaws.serverless.exceptions.ContainerInitializationException;
import com.amazonaws.serverless.proxy.ExceptionHandler;
import com.amazonaws.serverless.proxy.internal.servlet.ServletLambdaContainerHandlerBuilder;
import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
import org.springframework.web.context.ConfigurableWebApplicationContext;
Expand Down Expand Up @@ -86,4 +87,9 @@ public SpringLambdaContainerHandler<RequestType, AwsProxyResponse> buildAndIniti
initializationWrapper.start(handler);
return handler;
}

@Override
protected ExceptionHandler<AwsProxyResponse> defaultExceptionHandler() {
return new SpringAwsProxyExceptionHandler();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.amazonaws.serverless.proxy.spring;

import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
import jakarta.ws.rs.core.Response;
import org.junit.jupiter.api.Test;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.web.servlet.NoHandlerFoundException;

import static org.junit.jupiter.api.Assertions.assertEquals;

public class SpringAwsProxyExceptionHandlerTest {

@Test
void noHandlerFoundExceptionResultsIn404() {
AwsProxyResponse response = new SpringAwsProxyExceptionHandler().
handle(new NoHandlerFoundException(HttpMethod.GET.name(), "https://atesturl",
HttpHeaders.EMPTY));
assertEquals(Response.Status.NOT_FOUND.getStatusCode(), response.getStatusCode());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.amazonaws.serverless.proxy.spring;

import com.amazonaws.serverless.proxy.AwsProxyExceptionHandler;
import com.amazonaws.serverless.proxy.ExceptionHandler;
import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
import org.springframework.web.ErrorResponse;

/**
* This ExceptionHandler implementation enhances the standard AwsProxyExceptionHandler
* by mapping additional details from org.springframework.web.ErrorResponse
*
* As of now this class is identical with SpringAwsProxyExceptionHandler. We may consider
* moving it to a common module to share it in the future.
*/
public class SpringBootAwsProxyExceptionHandler extends AwsProxyExceptionHandler
implements ExceptionHandler<AwsProxyResponse> {
@Override
public AwsProxyResponse handle(Throwable ex) {
if (ex instanceof ErrorResponse) {
return new AwsProxyResponse(((ErrorResponse) ex).getStatusCode().value(),
HEADERS, getErrorJson(ex.getMessage()));
} else {
return super.handle(ex);
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
package com.amazonaws.serverless.proxy.spring;

import com.amazonaws.serverless.exceptions.ContainerInitializationException;
import com.amazonaws.serverless.proxy.ExceptionHandler;
import com.amazonaws.serverless.proxy.internal.servlet.ServletLambdaContainerHandlerBuilder;
import com.amazonaws.serverless.proxy.model.AwsProxyResponse;
import org.springframework.boot.WebApplicationType;
Expand Down Expand Up @@ -79,4 +80,9 @@ public SpringBootLambdaContainerHandler<RequestType, AwsProxyResponse> buildAndI
initializationWrapper.start(handler);
return handler;
}

@Override
protected ExceptionHandler<AwsProxyResponse> defaultExceptionHandler() {
return new SpringBootAwsProxyExceptionHandler();
}
}

0 comments on commit 3c7dbae

Please sign in to comment.