I'm trying to use CloudFormation to create an API Gateway but I have CORS issue with it.
Error on the front-end:
POST https://<>.execute-api.us-east-1.amazonaws.com/prod/<> 500
new:1 Access to XMLHttpRequest at '<>' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
The API is created without any issue and I even double check every single page on the console against the working API and find no differences in their Method Request, Integration Request, Integration Response and Method Response for all the methods (including the OPTIONS).
If I remove the resources created by the template and create them manually in the same API gateway then my code works as expected. I've tested with the localhost, front-end code in S3 bucket and PostMan, so I can verify that my front-end code, lambda functions and database are working correctly.
I understand that people have had this issue before but I haven't been able to find any answer that solves my issue.
Here's my template.
Please note that the "method.response.header.Access-Control-Allow-Origin": false actually creates the API with the same settings as the working one.
I also use the code from the correct answer for this question.
Yes, my OPTIONS request has the "Access-Control-Allow-Origin" header.
Update
Following dannymac's answer below. I got these:
I added console.log(event.requestContext); to my Lambda function (written in Node.js).
There are logs for Lambda when I test the function.
2019-06-27T20:07:03.118Z 462b93b2-9d4b-4ed3-bc04-f966fcd034cf Debug CORS issue. Request ID:
2019-06-27T20:07:03.118Z 462b93b2-9d4b-4ed3-bc04-f966fcd034cf undefined
It looks like there is no event.requestContext.
I selected Enable CloudWatch Logs-INFO and Enable Detailed CloudWatch Metrics with CloudWatch log role ARN*:arn:aws:iam::<ID>:role/ApiGatewayCloudWatchLogsRole (it's a role created by AWS) in the API Gateway settings.
However, there is no CloudWatch log for the API Gateway. There's a default log in CloudWatch - Log Groups: /aws/apigateway/welcome
Time (UTC +00:00)
2019-06-27
19:50:55
Cloudwatch logs enabled for API Gateway
It looks like the CloudWatch log didn't pick up the test from API Gateway.
This is what I got from testing the GET method in my API Gateway:
Response Body
{
"message": "Internal server error"
}
Response Headers
{}
Logs
Execution log for request 10d90173-9919-11e9-82e1-dd33dda3b9df
Thu Jun 27 20:20:54 UTC 2019 : Starting execution for request: 10d90173-9919-11e9-82e1-dd33dda3b9df
Thu Jun 27 20:20:54 UTC 2019 : HTTP Method: GET, Resource Path: /notes
Thu Jun 27 20:20:54 UTC 2019 : Method request path: {}
Thu Jun 27 20:20:54 UTC 2019 : Method request query string: {userid=<ID>}
Thu Jun 27 20:20:54 UTC 2019 : Method request headers: {}
Thu Jun 27 20:20:54 UTC 2019 : Method request body before transformations:
Thu Jun 27 20:20:54 UTC 2019 : Endpoint request URI: https://lambda.us-east-1.amazonaws.com/2015-03-31/functions/arn:aws:lambda:us-east-1:770402430649:function:test-api-gateway-2-LambdaFunction-1XDONAN3QIY9I/invocations
Thu Jun 27 20:20:54 UTC 2019 : Endpoint request headers: {x-amzn-lambda-integration-tag=... [TRUNCATED]
Thu Jun 27 20:20:54 UTC 2019 : Endpoint request body after transformations: {"resource":"/notes","path":"/notes","httpMethod":"GET","headers":null,"multiValueHeaders":null,"queryStringParameters":{"userid":"<USERID>"},"multiValueQueryStringParameters":{"userid":["<USERID>"]},"pathParameters":null,"stageVariables":null,"requestContext":{"path":"/notes","accountId":"<ID>"...,"identity":{"cognitoIdentityPoolId":null,"cognitoIdentityId":null,"apiKey":"test-invoke-api-key","principalOrgId":null,"cognitoAuthenticationType":null,"userArn":"<ARN>","apiKeyId":"test-invoke-api-key-id","userAgent":..."test [TRUNCATED]
Thu Jun 27 20:20:54 UTC 2019 : Sending request to https://lambda.us-east-1.amazonaws.com/2015-03-31/functions/arn:aws:lambda:us-east-1:<ID>:function:test-api-gateway-2-LambdaFunction-<STRING>/invocations
Thu Jun 27 20:20:54 UTC 2019 : Received response. Status: 403, Integration latency: 6 ms
Thu Jun 27 20:20:54 UTC 2019 : Endpoint response headers: {Date=Thu, 27 Jun 2019 20:20:54 GMT, Content-Length=130, Connection=keep-alive, x-amzn-RequestId=<ID>}
Thu Jun 27 20:20:54 UTC 2019 : Endpoint response body before transformations: <AccessDeniedException>
<Message>Unable to determine service/operation name to be authorized</Message>
</AccessDeniedException>
Thu Jun 27 20:20:54 UTC 2019 : Lambda invocation failed with status: 403. Lambda request id: feb22917-0dea-4f91-a274-fb6b85a69121
Thu Jun 27 20:20:54 UTC 2019 : Execution failed due to configuration error:
Thu Jun 27 20:20:54 UTC 2019 : Method completed with status: 500
I've also exported both the working and not working API Gateway in Swagger 2. The only difference is:
// working one:
"x-amazon-apigateway-any-method": {
"produces": [
"application/json"
],
"parameters": [
{
"name": "noteid",
"in": "path",
"required": true,
"type": "string"
}
],
"responses": {
"200": {
"description": "200 response",
"schema": {
"$ref": "#/definitions/Empty"
}
}
},
"security": [
{
"mobile-notes-api-authorizer": []
}
]
}
// not working one:
"x-amazon-apigateway-any-method": {
"produces": [
"application/json"
],
"responses": {
"200": {
"description": "200 response",
"schema": {
"$ref": "#/definitions/Empty"
}
}
},
"security": [
{
"test-api-gateway-2-authorizer": []
}
]
}
They both have:
"headers": {
"Access-Control-Allow-Origin": {
"type": "string"
},
"Access-Control-Allow-Methods": {
"type": "string"
},
"Access-Control-Allow-Headers": {
"type": "string"
}
}
I've tried to use the Swagger template in the Body of my API Gateway before but unable to solve the invalid authorizer issue.
I've figured out the issue. There are 2 main things:
The IntegrationHttpMethod for Lambda must be POST. I found the answer here.
The template didn't have AWS::Lambda::Permission that allows API Gateway to invoke Lambda function.
With the template, when you use AWS::Lambda::Permission, it will show the API as a trigger of your Lambda function.
However, if you manually create the API Gateway and link it with your Lambda function, it won't show API Gateway as a trigger but it still works.
So for the template I posted above, I needed to add these for it to work:
"LambdaPermission": {
"Type": "AWS::Lambda::Permission",
"Description": "Permission for API GateWay to invoke Lambda.",
"Properties": {
"Action": "lambda:invokeFunction",
"FunctionName": {
"Fn::GetAtt": [
"LambdaFunction",
"Arn"
]
},
"Principal": "apigateway.amazonaws.com",
"SourceArn": {
"Fn::Join": [
"",
[
"arn:aws:execute-api:",
{
"Ref": "AWS::Region"
},
":",
{
"Ref": "AWS::AccountId"
},
":",
{
"Ref": "ApiGateway"
},
"/*"
]
]
}
}
},
And edit method ANY to look like this
"methodNotesANY": {
"Type": "AWS::ApiGateway::Method",
"DependsOn": "LambdaPermission",
"Properties": {
"AuthorizationType": "COGNITO_USER_POOLS",
"AuthorizerId": {
"Ref": "GatewayAuthorizer"
},
"RestApiId": {
"Ref": "ApiGateway"
},
"ResourceId": {
"Ref": "resourceNotes"
},
"HttpMethod": "ANY",
"Integration": {
"Type": "AWS_PROXY",
"IntegrationHttpMethod": "POST",
"Uri": {
"Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${LambdaFunction.Arn}/invocations"
},
"IntegrationResponses": [{
"StatusCode": "200"
}]
},
"MethodResponses": [{
"ResponseModels": {
"application/json": "Empty"
},
"StatusCode": "200"
}]
}
},
My Best Guess: The POST to your ANY lambda function is failing during execution, and not setting the Access-Control-Allow-Origin header to * (or your domain). Anytime I get a 5XX error and a CORS error at the same time from a non-OPTIONS request, this is almost always the case for me.
Recommended Next Steps: Reproduce the error situation after adding debug logging to your Lambda source code, and turning on CloudWatch Logs in your API Gateway Rest API. You can do this by going to the API Gateway console, clicking on Stages > Prod > Logs/Tracing, then checking these two: Enable CloudWatch Logs (Log level: INFO), and Enable Detailed CloudWatch Metrics. Then you must "deploy" the changes in order for them to take effect. Do this by clicking the Actions button from your Rest API's Resources menu, and choosing Deploy API. I also recommend logging the extendedRequestId (an event property passed to your handler) from your Lambda function in order to tie the Lambda request to the API Gateway request: event.requestContext.extendedRequestId.
Example API Gateway logs:
(b66b3876-984b-11e9-95eb-dd93c7e40ca0) Extended Request Id: b5zpBGS3IAMFvqw=
(b66b3876-984b-11e9-95eb-dd93c7e40ca0) Verifying Usage Plan for request: b66b3876-984b-11e9-95eb-dd93c7e40ca0. API Key: API Stage: 1234567890/Prod
(b66b3876-984b-11e9-95eb-dd93c7e40ca0) API Key authorized because method 'ANY /forms' does not require API Key. Request will not contribute to throttle or quota limits
(b66b3876-984b-11e9-95eb-dd93c7e40ca0) Usage Plan check succeeded for API Key and API Stage 1234567890/Prod
(b66b3876-984b-11e9-95eb-dd93c7e40ca0) Starting execution for request: b66b3876-984b-11e9-95eb-dd93c7e40ca0
(b66b3876-984b-11e9-95eb-dd93c7e40ca0) HTTP Method: GET, Resource Path: /forms
(b66b3876-984b-11e9-95eb-dd93c7e40ca0) Lambda execution failed with status 200 due to customer function error: select count(*) AS `count(*)` from (select `user`.* from `user` where (id IN ('some_id_123'))) as `temp` - Cannot enqueue Query after fatal error.. Lambda request id: 1ae2bb06-5347-4775-9277-caccc42f18f2
(b66b3876-984b-11e9-95eb-dd93c7e40ca0) Method completed with status: 502
(b66b3876-984b-11e9-95eb-dd93c7e40ca0) AWS Integration Endpoint RequestId : 1ae2bb06-5347-4775-9277-caccc42f18f2
(b66b3876-984b-11e9-95eb-dd93c7e40ca0) X-ray Tracing ID : 1-5d13cca0-3be96a1ab93a877edc70577c
Example correlated Lambda execution logs:
START RequestId: 1ae2bb06-5347-4775-9277-caccc42f18f2 Version: $LATEST
2019-06-26T19:50:56.391Z 1ae2bb06-5347-4775-9277-caccc42f18f2 { "extendedRequestId": "b5zpBGS3IAMFvqw=", ... }
2019-06-26T19:50:57.853Z 1ae2bb06-5347-4775-9277-caccc42f18f2 { "errorMessage": "select count(*) AS `count(*)` from (select `user`.* from `user` where (id IN ('some_id_123'))) as `temp` - Cannot enqueue Query after fatal error.", ... }
END RequestId: 1ae2bb06-5347-4775-9277-caccc42f18f2
REPORT RequestId: 1ae2bb06-5347-4775-9277-caccc42f18f2 Duration: 1660.45 ms Billed Duration: 1700 ms Memory Size: 256 MB Max Memory Used: 57 MB
Other Thoughts: Export the Swagger definitions of both the broken API and the working API. Compare and see what is different. Do this from the console by going to Stages > Prod > Export > Export as Swagger + API Gateway Extensions. It may not be exactly the same as the CloudFormation template, but it's pretty close.
At the time of this post, Lambda Proxy Integration (AWS_PROXY) and CORS (Access-Control-Allow-Origin) don't work very well together. My approach -inspired on this explanation- was to use AWS instead of AWS_PROXY and manually provide Mapping templates for both request and response as follows:
MyApiGateway:
Type: AWS::Serverless::Api
Properties:
StageName: !Ref Stage
Cors:
AllowMethods: "'POST,OPTIONS'"
AllowHeaders: "'Access-Control-Allow-Origin,Content-Type,X-Amz-Date,Authorization,X-Api-Key,x-requested-with,x-requested-for'"
AllowOrigin: "'*'"
DefinitionBody:
swagger: 2.0
info:
version: 1.1
title: !Ref AWS::StackName
paths:
/mypath:
get:
responses:
"200":
schema:
$ref: "#/definitions/Empty"
headers:
Access-Control-Allow-Origin:
type: string
x-amazon-apigateway-integration:
httpMethod: POST # must be POST even for GET
type: AWS # must be AWS to allow cors headers
uri: !Sub arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MyLambda.Arn}/invocations
requestTemplates:
application/json: |
{
#set($params = $input.params().querystring)
"queryStringParameters" : {
#foreach($param in $params.keySet())
"$param" : "$util.escapeJavaScript($params.get($param))" #if($foreach.hasNext),#end
#end
},
#set($params = $input.params().path)
"pathParameters" : {
#foreach($param in $params.keySet())
"$param" : "$util.escapeJavaScript($params.get($param))" #if($foreach.hasNext),#end
#end
}
}
responses:
default:
statusCode: 200
responseParameters:
method.response.header.Access-Control-Allow-Origin: "'*'"
responseTemplates:
application/json: |
#set($payload = $util.parseJson($input.json('$')))
#set($context.responseOverride.status = $payload.statusCode)
$payload.body
Related
All I want is deploying a github page which launches a cross site post request with payload {"statement":"select * from t"} to aws lambda API url like https://xxxxxxx.execute-api.ap-northeast-1.amazonaws.com/default/foo. So I create a aws lambda, write some code and create a API Gateway trigger.
When I test my page I found the OPTIONS request is successful but the header is like
apigw-requestid: WjJcajPTtjMEPTw=
content-length: 0
date: Mon, 08 Aug 2022 14:50:30 GMT
xxxx: aaaa
As you can see, the xxxx header is OK but the Access-Control-Allow-Origin header not. This caused the POST request failed.
the aws lambda code:
exports.handler = async (event,context, callback) => {
// I add this because the browser make a OPTIONS request first before axios.post()
if (event.httpMethod == 'OPTIONS') {
return {
headers: {
"xxxx":"aaaa",
"Access-Control-Allow-Origin": "*",
},
statusCode: 200,
}
}
// handle POST case
}
the API Gateway trigger setting:
API type: HTTP
Authorization: NONE
CORS: Yes
Detailed metrics enabled: Yes
Method: ANY
Resource path: /foo
Service principal: apigateway.amazonaws.com
Stage: default
Statement ID: xxxxxxxxxxx
I added a Model to my API Gateway. When I send a POST request with the body containing a string (instead of valid JSON) the Model validates the request. I was expecting it to be rejected.
I've tried these two cases showing the problem:
The Model will validate "A_STRING" (incorrect behaviour)
The Model will validate "A_STRING"{} (incorrect behaviour)
The Model will fail {"A_STRING"} (correct behaviour)
I am testing the API Gateway directly from the AWS console (see attached screenshot), here are the logs for one example:
Execution log for request b2b825b4-4fb2-43e6-935b-0781264eb5df
Mon Aug 17 21:45:42 UTC 2020 : Starting execution for request: b2b825b4-4fb2-43e6-935b-0781264eb5df
Mon Aug 17 21:45:42 UTC 2020 : HTTP Method: POST, Resource Path: /TrainingNotif
Mon Aug 17 21:45:42 UTC 2020 : Method request path: {}
Mon Aug 17 21:45:42 UTC 2020 : Method request query string: {}
Mon Aug 17 21:45:42 UTC 2020 : Method request headers: {}
Mon Aug 17 21:45:42 UTC 2020 : Method request body before transformations: "A_STRING"
Mon Aug 17 21:45:42 UTC 2020 : Request validation succeeded for content type application/json
If it helps here is my model:
{
"additionalProperties": false,
"$schema": "http://json-schema.org/draft-04/schema#",
"required":
[
"trainingSiteRequester",
"employeeTrainingList"
],
"properties":
{
"trainingSiteRequester":
{
"type": "string"
},
"employeeTrainingList":
{
"type": "array",
"items":
{
"additionalProperties": false,
"type": "object",
"required":
[
"id",
"trainingURL",
"dueDate"
],
"properties":
{
"trainingURL":
{
"type": "string"
},
"dueDate":
{
"type": "string"
},
"id":
{
"type": "integer"
}
}
}
}
}
}
And to be complete here is my CloudFormation code to attach the model to the API Gateay:
PostMethod:
Type: AWS::ApiGateway::Method
Properties:
AuthorizationType: AWS_IAM
HttpMethod: POST
RequestValidatorId: !Ref TrainingNotificationRequestValidator
RequestModels:
application/json: !Ref TrainingNotificationRequestModel
Integration:
Credentials: !GetAtt TrainingNotificationsAPIGatewayRole.Arn
IntegrationHttpMethod: POST
IntegrationResponses:
- SelectionPattern: 200
StatusCode: 200
- SelectionPattern: 429
StatusCode: 429
PassthroughBehavior: NEVER
RequestParameters:
integration.request.querystring.Action: "'SendMessage'"
integration.request.querystring.TopicArn: !Sub "'${ReceivedRequestsSNS}'"
integration.request.querystring.Message: "method.request.body"
RequestTemplates:
application/json: "{\"statusCode\": 200}"
Type: AWS
Uri:
Fn::Join:
- ""
- - "arn:aws:apigateway:"
- Ref: AWS::Region
- ":sns:action/Publish"
MethodResponses:
- StatusCode: 200
- StatusCode: 429
- StatusCode: 500
ResourceId: !Ref TrainingNotificationsAPIGatewayResources
RestApiId: !Ref TrainingNotificationsAPIGateway
# Request Model
TrainingNotificationRequestModel:
Type: AWS::ApiGateway::Model
Properties:
RestApiId: !Ref TrainingNotificationsAPIGateway
ContentType: application/json
Name: TrainingNotificationRequestModel
Schema:
$schema: 'http://json-schema.org/draft-04/schema#'
additionalProperties: false
required:
- trainingSiteRequester
- employeeTrainingList
properties:
trainingSiteRequester:
type: string
employeeTrainingList:
type: array
items:
type: object
additionalProperties: false
properties:
id:
type: integer
trainingURL:
type: string
dueDate:
type: string
required:
- id
- trainingURL
- dueDate
TrainingNotificationRequestValidator:
Type: AWS::ApiGateway::RequestValidator
Properties:
RestApiId: !Ref TrainingNotificationsAPIGateway
Name: TrainingNotificationRequestValidator
ValidateRequestBody: true
Found the answer, the Model's schema didn't have a Type property.
Adding type : object makes the model reject anything that isn't json (as expected) and makes the model run validation against anything that is JSON.
To be honest I found this through trial and error, so I'm not 100% sure as to the why of my answer, but it works.
I'm attempting to to have an API Gateway act as a proxy to DynamoDB and I'm currently testing with a simple POST call to append the $context.requestId and $context.requestTime to my table. I am getting the following error message:
{
"__type": "com.amazon.coral.validate#ValidationException",
"message": "One or more parameter values were invalid: An AttributeValue may not contain an empty string"
}
and what is sent is:
Mon Apr 15 19:10:24 UTC 2019 : Endpoint request body after transformations: {
"TableName": "BurgerOrders",
"Item": {
"OrderId": {
"S": "1f54a90b-5fb2-11e9-8b31-c9003bb71ec2"
},
"RequestTime": {
"S": ""
}
}
}
The mapping template within Integration Request that I have is:
{
"TableName": "BurgerOrders",
"Item": {
"OrderId": {
"S": "$context.requestId"
},
"RequestTime": {
"S": "$context.requestTime"
},
}
}
I've tried to change $context.requestTime to $context.requestTimeEpoch and I get the same error.
I know this was posted a while back, but according to an AWS dev:
"At this time, [$context.requestTime] is only available when the API has been deployed and API call was invoked to deployed stage."
Source: https://forums.aws.amazon.com/thread.jspa?messageID=697652
I have built a custom authorizer for AWS in .net core. When testing it from API Gateway Console I am receiving ResponseCode 500 with this error.
Execution log for request 0566bf99-cfb5-11e8-b203-65db1a667292
Sun Oct 14 13:28:22 UTC 2018 : Starting authorizer: i07xnl for request: 0566bf99-cfb5-11e8-b203-65db1a667292
Sun Oct 14 13:28:22 UTC 2018 : Incoming identity: **ds
Sun Oct 14 13:28:22 UTC 2018 : Endpoint request URI: https://lambda.us-west-2.amazonaws.com/2015-03-31/functions/arn:aws:lambda:us-west-2:278483347755:function:GetPolicy/invocations
Sun Oct 14 13:28:22 UTC 2018 : Endpoint request headers: {x-amzn-lambda-integration-tag=0566bf99-cfb5-11e8-b203-65db1a667292, Authorization=************************************************************************************************************************************************************************************************************************************************************************************************************************4e3e8c, X-Amz-Date=20181014T132822Z, x-amzn-apigateway-api-id=k8ate5przg, X-Amz-Source-Arn=arn:aws:execute-api:us-west-2:278483347755:k8ate5przg/authorizers/i07xnl, Accept=application/json, User-Agent=AmazonAPIGateway_k8ate5przg, X-Amz-Security-Token=FQoGZXIvYXdzEA0aDBDj/T/Y58E+lkgRcyK3A5EXzDygzB0DzIFN36D/LMM0uCMn70NDKnpualhTEKEe8Zj/a6/nSFVwDSmQty8r2b/ezWcJoQCQztPHDiTFFu7I/4vvoGuH6P3REduQn8knZGVkBAOFTi/EIcnLNBoWjWQXrO8BszGKdoykJ3BrTIq+2dbyfOUdIcmCwGGyC/UzGn5B+fkNcSJT94yfemVcfEiuncnx6snRekDYzRZWXW1+ZzxPoMINpykNTYbKCnG5pNzPF7j2xxH7zyfYtmsVaMaq5zBGqT3eGzUonM4k/7FIRwOB6SxRUIHrO/fboa3QW+z7+iQEtqWg7DDO [TRUNCATED]
Sun Oct 14 13:28:22 UTC 2018 : Endpoint request body after transformations: {"type":"TOKEN","methodArn":"arn:aws:execute-api:us-west-2:278483347755:k8ate5przg/ESTestInvoke-stage/GET/","authorizationToken":"sdds"}
Sun Oct 14 13:28:22 UTC 2018 : Sending request to https://lambda.us-west-2.amazonaws.com/2015-03-31/functions/arn:aws:lambda:us-west-2:278483347755:function:GetPolicy/invocations
Sun Oct 14 13:28:24 UTC 2018 : Authorizer result body before parsing: {"Version":"10/14/18","Statement":[{"Effect":"Allow","Action":["apigateway: POST"],"Resource":["arn:aws:lambda:us-west-2:278483347755:function:GetPolicy"]}]}
Sun Oct 14 13:28:24 UTC 2018 : Execution failed due to configuration error: Invalid JSON in response: {"Version":"10/14/18","Statement":[{"Effect":"Allow","Action":["apigateway: POST"],"Resource":["arn:aws:lambda:us-west-2:278483347755:function:GetPolicy"]}]}
Sun Oct 14 13:28:24 UTC 2018 : AuthorizerConfigurationException
The Invalid Json is this:
{
"Version": "10/14/18",
"Statement": [{
"Effect": "Allow",
"Action": ["apigateway: POST"],
"Resource": ["arn:aws:lambda:us-west-2:278483347755:function:GetPolicy"]
}]
}
To me this seems Okay. Here, Action value is taken from AWS documentation and Resource is the ARN for my custom authorizer lambda method.
The response isn't correct for a custom authorisation lambda.
You can see the full details here (https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-lambda-authorizer-output.html), however for your example you need to return:
{
"principalId": "user",
"policyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Action": "execute-api:Invoke",
"Effect": "Allow",
"Resource": "arn:aws:execute-api:us-west-2:278483347755:k8ate5przg/ESTestInvoke-stage/GET/"
}
]
}
}
Specifically you need to nest your policy in a policyDocument key, and the permission you're granting is not to be able to POST to API Gateway, but to be allowed to invoke the function behind the gateway.
I have configured my AWS APIGateway to validate the requests according to a JSON schema.
E.g. the path /vehicle, which has the following schema attached:
{
"type":"object",
"properties":{
"licensePlate":{
"pattern":"[A-Za-z]{1,3} [A-Za-z]{1,2} \\d{1,4}",
"type":"string"
},
"vehicleType":{
"type":"string",
"enum":[
"Truck",
"Trailer"
]
}
},
"required":[
"licensePlate",
"vehicleType"
]
}
This works fine. If I submit an invalid request the API responds with a 400 {"message": "Invalid request body"}. I would like to customize this message, e.g. to
{
"entity": "vehicleType",
"message": "missing"
}
If I take a look at the logs from the Gateway it seems that a similar message is logged (object has missing required properties (["vehicleType"])). Can I use that one? How can I access it?
Logs:
Execution log for request test-request
Thu Feb 01 13:12:18 UTC 2018 : Starting execution for request: test-invoke-request
Thu Feb 01 13:12:18 UTC 2018 : HTTP Method: POST, Resource Path: /vehicle
Thu Feb 01 13:12:18 UTC 2018 : Method request path: {}
Thu Feb 01 13:12:18 UTC 2018 : Method request query string: {}
Thu Feb 01 13:12:18 UTC 2018 : Method request headers: {}
Thu Feb 01 13:12:18 UTC 2018 : Method request body before transformations: {
"licensePlate": "HH AB 123"
}
Thu Feb 01 13:12:18 UTC 2018 : Request body does not match model schema for content type application/json: [object has missing required properties (["vehicleType"])]
Thu Feb 01 13:12:18 UTC 2018 : Method completed with status: 400
Is this possible with the API Gateway?
Yeah what you want is the $context.error.validationErrorString
Like #Bajwa said -- you need to customize the GatewayReponse template. If you're using cloud formation that looks like this:
"GatewayResponse": {
"Type": "AWS::ApiGateway::GatewayResponse",
"Properties": {
"ResponseParameters": {
"gatewayresponse.header.Access-Control-Allow-Origin": "'*'",
"gatewayresponse.header.Access-Control-Allow-Headers": "'*'"
},
"ResponseTemplates": {
"application/json": "{\"error\":{\"message\":\"$context.error.messageString\",\"errors\":\"$context.error.validationErrorString\"}"
},
"ResponseType": "BAD_REQUEST_BODY",
"RestApiId": {
"Ref": "YouRestApiResource"
},
"StatusCode": "400"
}
}
If you violate the request body validator you'll see something like this:
{
"error": {
"message":" "Invalid request body"",
"errors":"[string \"1\" is too short (length: 1, required minimum: 10)]"
}
It's not perfect -- some of the messaging is vague, would be nice if anyone knows how to for example add the property name that caused the violation.
If the request payload is invalid, you will see the same message:
{
"message": "Invalid request body"
}
API Gateway includes more detail, if the request payload format is valid and parameters format is invalid.
See more detail from below link, especially the bottom part.
Test Basic Request Validation in API Gateway
But you may do some customization in the response message by customizing Gateway responses.
You may customize Bad Request Body 400.
{
"message":"$context.error.messageString",
"Hint":"Check required number of parameters or parameters allowed type and length."
}
Then you will see message in below format.
{
"message": "Invalid request body",
"Hint": "Check required number of parameters or parameters allowed type and length."
}
See hot to update Body Mapping Template from attached screenshot. (APIGateway/Gateway Responses).