@@ -4,6 +4,7 @@ import com.amazonaws.services.lambda.runtime.Context
44import com.amazonaws.services.lambda.runtime.RequestHandler
55import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent
66import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent
7+ import com.fasterxml.jackson.module.kotlin.MissingKotlinParameterException
78import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
89import com.github.mduesterhoeft.router.ProtoBufUtils.toJsonWithoutWrappers
910import com.google.common.net.MediaType
@@ -29,16 +30,16 @@ abstract class RequestHandler : RequestHandler<APIGatewayProxyRequestEvent, APIG
2930 log.debug(" match result for route '$routerFunction ' is '$matchResult '" )
3031 if (matchResult.match) {
3132 val handler: HandlerFunction <Any , Any > = routerFunction.handler
32- val requestBody = deserializeRequest(handler, input)
33- val request = Request (input, requestBody, routerFunction.requestPredicate.pathPattern)
3433 return try {
34+ val requestBody = deserializeRequest(handler, input)
35+ val request = Request (input, requestBody, routerFunction.requestPredicate.pathPattern)
3536 val response = router.filter.then(handler as HandlerFunction <* , * >).invoke(request)
3637 createResponse(input, response)
37- } catch (e: RuntimeException ) {
38+ } catch (e: Exception ) {
3839 when (e) {
39- is ApiException -> createErrorResponse (input, e)
40+ is ApiException -> createApiExceptionErrorResponse (input, e)
4041 .also { log.info(" Caught api error while handling ${input.httpMethod} ${input.path} - $e " ) }
41- else -> createInternalServerErrorResponse (input, e)
42+ else -> createUnexpectedErrorResponse (input, e)
4243 .also { log.error(" Caught exception handling ${input.httpMethod} ${input.path} - $e " , e) }
4344 }
4445 }
@@ -64,7 +65,7 @@ abstract class RequestHandler : RequestHandler<APIGatewayProxyRequestEvent, APIG
6465 private fun handleNonDirectMatch (matchResults : List <RequestMatchResult >, input : APIGatewayProxyRequestEvent ): APIGatewayProxyResponseEvent {
6566 // no direct match
6667 if (matchResults.any { it.matchPath && it.matchMethod && ! it.matchContentType }) {
67- return createErrorResponse (
68+ return createApiExceptionErrorResponse (
6869 input, ApiException (
6970 httpResponseStatus = 415 ,
7071 message = " Unsupported Media Type" ,
@@ -73,7 +74,7 @@ abstract class RequestHandler : RequestHandler<APIGatewayProxyRequestEvent, APIG
7374 )
7475 }
7576 if (matchResults.any { it.matchPath && it.matchMethod && ! it.matchAcceptType }) {
76- return createErrorResponse (
77+ return createApiExceptionErrorResponse (
7778 input, ApiException (
7879 httpResponseStatus = 406 ,
7980 message = " Not Acceptable" ,
@@ -82,15 +83,15 @@ abstract class RequestHandler : RequestHandler<APIGatewayProxyRequestEvent, APIG
8283 )
8384 }
8485 if (matchResults.any { it.matchPath && ! it.matchMethod }) {
85- return createErrorResponse (
86+ return createApiExceptionErrorResponse (
8687 input, ApiException (
8788 httpResponseStatus = 405 ,
8889 message = " Method Not Allowed" ,
8990 code = " METHOD_NOT_ALLOWED"
9091 )
9192 )
9293 }
93- return createErrorResponse (
94+ return createApiExceptionErrorResponse (
9495 input, ApiException (
9596 httpResponseStatus = 404 ,
9697 message = " Not found" ,
@@ -99,7 +100,7 @@ abstract class RequestHandler : RequestHandler<APIGatewayProxyRequestEvent, APIG
99100 )
100101 }
101102
102- open fun createErrorResponse (input : APIGatewayProxyRequestEvent , ex : ApiException ): APIGatewayProxyResponseEvent =
103+ open fun createApiExceptionErrorResponse (input : APIGatewayProxyRequestEvent , ex : ApiException ): APIGatewayProxyResponseEvent =
103104 APIGatewayProxyResponseEvent ()
104105 .withBody(objectMapper.writeValueAsString(mapOf (
105106 " message" to ex.message,
@@ -109,14 +110,28 @@ abstract class RequestHandler : RequestHandler<APIGatewayProxyRequestEvent, APIG
109110 .withStatusCode(ex.httpResponseStatus)
110111 .withHeaders(mapOf (" Content-Type" to " application/json" ))
111112
112- open fun createInternalServerErrorResponse (input : APIGatewayProxyRequestEvent , ex : java.lang.RuntimeException ): APIGatewayProxyResponseEvent =
113- APIGatewayProxyResponseEvent ()
114- .withBody(objectMapper.writeValueAsString(mapOf (
115- " message" to ex.message,
116- " code" to " INTERNAL_SERVER_ERROR"
117- )))
118- .withStatusCode(500 )
119- .withHeaders(mapOf (" Content-Type" to " application/json" ))
113+ open fun createUnexpectedErrorResponse (input : APIGatewayProxyRequestEvent , ex : Exception ): APIGatewayProxyResponseEvent =
114+ when (ex) {
115+ is MissingKotlinParameterException ->
116+ APIGatewayProxyResponseEvent ()
117+ .withBody(objectMapper.writeValueAsString(
118+ listOf (mapOf (
119+ " path" to ex.parameter.name.orEmpty(),
120+ " message" to " Missing required field" ,
121+ " code" to " MISSING_REQUIRED_FIELDS"
122+ ))))
123+ .withStatusCode(422 )
124+ .withHeaders(mapOf (" Content-Type" to " application/json" ))
125+ else ->
126+ APIGatewayProxyResponseEvent ()
127+ .withBody(objectMapper.writeValueAsString(mapOf (
128+ " message" to ex.message,
129+ " code" to " INTERNAL_SERVER_ERROR"
130+ )))
131+ .withStatusCode(500 )
132+ .withHeaders(mapOf (" Content-Type" to " application/json" ))
133+ }
134+
120135
121136 open fun <T > createResponse (input : APIGatewayProxyRequestEvent , response : ResponseEntity <T >): APIGatewayProxyResponseEvent {
122137 val accept = MediaType .parse(input.acceptHeader())
0 commit comments