I want add an api validation to the serverless aws-nodes template and nothing I have tested until now has worked very well.
My current approach is to overwrite the existing api-gateway, which is generated by the serverless framework, with a yml/json swagger definition that contains my models for the validation. This works for me when I test it in the API-Gateway UI, but on external requests the api don't validate the request for the lambda-proxy.
When I use normal lambda the api gateway also passthrough the request body without validation or transformation.
My current swagger api definition with validation:
swagger: "2.0"
info:
title: feedback
version: '1.0'
schemes:
- https
produces:
- application/json
x-amazon-apigateway-api-key-source : HEADER
x-amazon-apigateway-request-validators:
full:
validateRequestBody: true
validateRequestParameters: true
body-only:
validateRequestBody: true
validateRequestParameters: false
x-amazon-apigateway-request-validator: full
# Custom 400 response with validation feedback
x-amazon-apigateway-gateway-responses:
BAD_REQUEST_BODY:
statusCode: 400
type:
application/json:
responseTemplates:
application/json:
|-
{
"message": $context.error.messageString,
"validation": "$context.error.validationErrorString",
"statusCode": "'400'"
}
# request structure
paths:
/feedback:
post:
# validation definition
x-amazon-apigateway-request-validator: body-only
parameters:
- in: body
name: Create ...
required: true
schema:
"$ref": "#/definitions/Model"
responses:
'200':
description: validation succeeded
'400':
description: validation failed
x-amazon-apigateway-integration:
uri: "arn:aws:apigateway:{api-region}:lambda:path/2015-03-31/functions/arn:aws:lambda:{lambda-region}:{konto-id}:function:{function-name}/invocations"
passthroughBehavior: when_no_match
httpMethod: POST
requestTemplates:
application/json: '{"statusCode": 200}'
type: aws
get:
responses:
'201':
description: list all Data
content:
application/json:
schema:
type: array
items:
feedback:
$ref: "#/definitions/Model"
'401':
$ref: "#/definitions/UnauthorizedError"
x-amazon-apigateway-integration:
uri: "arn:aws:apigateway:{api-region}:lambda:path/2015-03-31/functions/arn:aws:lambda:{lambda-region}:{konto-id}:function:{function-name}/invocations"
passthroughBehavior: never
httpMethod: POST
type: aws_proxy
# definition of the request/respons model with validation
definitions:
Model:
type: object
properties:
topic:
$ref: "#/definitions/Topic"
text:
type: string
minLength: 1
maxLength: 250
required:
- topic
- text
Topic:
type: string
enum:
- xyz
My api definition from my serverless.yml
functions:
create:
handler: feedback/create.create
events:
- http:
path: feedback
method: post
list:
handler: feedback/list.list
events:
- http:
path: feedback
method: get
the lambda functions only read/write feedback from/to an DynamoDB
Has someone an idea how I can add some kind of api validation to my serverless project without using small plugins (serverless-reqvalidator-plugin) or how to solve the problem with the data transformation ?
Ok the solution for the problem that the validation works with the internal test but not with an external request was very obvious. I forgot to deploy the new api-definition.
aws apigateway create-deployment --rest-api-id {api-id} --stage-name dev
Also I have changed my API-definition. I now inegrate to my Post request an normal lambda. This is the only way, I can make sure that only json content gets validated and afterwards get passed through to the lamda function. Because I use no lambda-proxy the request event gets transformed from the api-gateway, so I have to define an request template, that put the whole request body in an new request.
requestTemplates:
application/json: '{"statusCode": 202, "body": $input.body}'
With this way I also transformed the lambda response in an predefined api response with Cors headers.
I the end my solution is:
1: Write a swagger api definition:
swagger: "2.0"
info:
title: xxxxxx
version: '0.0.0'
schemes:
- https
produces:
- application/json
x-amazon-apigateway-api-key-source : HEADER
# Define which parts of the request should be validated
x-amazon-apigateway-request-validators:
full:
validateRequestBody: true
validateRequestParameters: true
body-only:
validateRequestBody: true
validateRequestParameters: false
# Custom response model from the api-gateway that return validation error string
x-amazon-apigateway-gateway-responses:
BAD_REQUEST_BODY:
statusCode: 400
type:
application/json:
responseParameters: # CORS Headers
gatewayresponse.header.Access-Control-Allow-Credentials : "'true'"
gatewayresponse.header.Access-Control-Allow-Origin : "'*'"
responseTemplates:
application/json: #must be an json string because otherwiese there are some transformation issues
|-
{
"message": $context.error.messageString,
"validation": "$context.error.validationErrorString",
"statusCode": "400"
}
paths:
/feedback:
options:
description:
Enable CORS by returning correct headers
tags:
- CORS
x-amazon-apigateway-integration:
type: mock
requestTemplates:
application/json: |
{
"statusCode" : 200
}
responses:
"default":
statusCode: "200"
responseParameters: # CORS Headers
method.response.header.Access-Control-Allow-Headers : "'Content-Type,X-Amz-Date,Authorization,X-Api-Key'"
method.response.header.Access-Control-Allow-Methods : "'*'"
method.response.header.Access-Control-Allow-Origin : "'*'"
responseTemplates:
application/json: |
{}
responses:
200:
description: Default response for CORS method
headers:
Access-Control-Allow-Headers:
type: "string"
Access-Control-Allow-Methods:
type: "string"
Access-Control-Allow-Origin:
type: "string"
post:
# validation definition
x-amazon-apigateway-request-validator: body-only
parameters:
- in: body
name: requestBody
required: true
content:
application/json:
schema: # validation model
"$ref": "#/definitions/Model"
responses: # response documentation
'200':
description: Create ......
headers: # Header format for the CORS headers
Access-Control-Allow-Credentials:
type: "string"
Access-Control-Allow-Origin:
type: "string"
x-amazon-apigateway-integration:
responses:
default:
statusCode: "200"
responseParameters: # CORS Header
method.response.header.Access-Control-Allow-Credentials : "'true'"
method.response.header.Access-Control-Allow-Origin : "'*'"
uri: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/arn:aws:lambda:${AWS::Region}:${AWS::account-id}:function:{AWS::lambda-function-name}/invocations
requestTemplates:
application/json: '{"statusCode": 202, "body": $input.body}'
passthroughBehavior: never # only accept Json Data
httpMethod: POST
type: aws
get:
security: # X-API-Key
- authorizer: []
responses:
'200':
description: ......
x-amazon-apigateway-integration:
uri: "arn:aws:apigateway:xxx:lambda:path/2015-03-31/functions/arn:aws:lambda:xxx:xxxxxxx:function:function-name/invocations"
httpMethod: POST
type: aws_proxy
definitions:
Model:
# Swagger Model with validation
securityDefinitions:
authorizer :
type : apiKey
name : x-api-key
in : header
2: Overwrite the existing serverless api:
aws apigateway put-rest-api --rest-api-id {api-id} --mode overwrite --body file://xxxx/api.yml
3: Don't forget to deploy the new api:
aws apigateway create-deployment --rest-api-id {api-id} --region eu-central-1 --stage-name ...