Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

handle Spring's ErrorResponse in separate default ExceptionHandlers #691

Merged
merged 1 commit into from
Nov 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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();
}
}