Just learning my way through AWS - I have an APIGateway REST API setup with Lambda proxy integration. The API has a model defined, and request validation setup on the body using this model.
Say the model is
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"propertyA": {
"type": "string"
},
"propertyB": {
"type": "string"
},
"propertyC": {
"type": "string"
},
"propertyD": {
"type": "string"
}
},
"required": ["propertyA", "propertyB", "propertyC", "propertyD"]
}
Now, if I test the API via APIGateway console, and purposely give an invalid input (omitting a required property propertyD):
{
"propertyA": "valueA",
"propertyB": "valueB",
"propertyC": "valueC"
}
the request fails with the error(400): Sun Jul 11 13:07:07 UTC 2021 : Request body does not match model schema for content type application/json: [object has missing required properties (["propertyD"])]
But when I invoke the same API(and stage) with the same invalid input from Postman, the validation seems to be not happening, and request is proxied to Lambda, which even returns a 200 OK as long as I comment out the parts of code that depend on propertyD.
What's the difference here? Should I be passing in any request header from the client side? I couldn't find anything from the AWS documentations
Answering my question-
Issue was with the headers used in the request - Postman defaulted the JSON as a Content-Type of text/plain, I had to switch to JSON using the dropdown in Body tab to make PostMan set the Content-Type to application/json
Following this post seems to have fixed the problem: https://itnext.io/how-to-validate-http-requests-before-they-reach-lambda-2fff68bfe93b, although it doesn't explain how
Apparently the magic lies with the config adding Content-Type Header under HTTP Request Headers section, even though the header is set correctly as application/json in PostMan.
Related
I am trying to follow StartContactChat & CreateParticipantConnection to initiate the chat from third party applications e.g. Postman.
i want to route the chat to agent on talk to agent message from API, the chat should be routed to the agent in amazon connect.
StartChatContact's Request Syntax is given like this:
PUT /contact/chat HTTP/1.1
Content-type: application/json
{
"Attributes": {
"string" : "string"
},
"ChatDurationInMinutes": number,
"ClientToken": "string",
"ContactFlowId": "string",
"InitialMessage": {
"Content": "string",
"ContentType": "string"
},
"InstanceId": "string",
"ParticipantDetails": {
"DisplayName": "string"
}
}
I have done with it using URL: PUT https://connect.us-east-1.amazonaws.com/contact/chat and got the ParticipantToken and now trying to create the participant connect using CreateParticipantConnection - https://connect.us-east-1.amazonaws.com/participant/connection but keep facing the error:
{
"message": "Unable to determine service/operation name to be authorized"
}
i have added the participant token generated by StartContactChat in Authorization --> AWS Signature --> Session Token as well as in Header but still the still getting the AccessDeniedExcetion.
The CreateParticipantConnection API does not belong to the same service as the StartChatContact API. As a result, https://connect.us-east-1.amazonaws.com/participant/connection is the wrong endpoint for the latter API. Instead, it should be https://participant.connect.us-east-1.amazonaws.com/participant/connection
I'm trying to get my "queryStringParameters" in the event into the "body" key of the event so I can parse them in the same way that I parse post requests. Is this possible with a HTTP api? I tried using parameter mappings in the integration settings but this only allows me to append the body to the queryString.
The way that you get the values from a POST request using body and with query parameters are different. So both ca not be in the body property as you want.
The queryStringParameters is passed when you add a URL Query String Parameter in your Api gateway resource.
When you send a request to the event in your API the following event will be send to the Lamba function
{
"resource": "/{proxy+}",
"path": "/path/to/resource",
"httpMethod": "POST",
"isBase64Encoded": true,
"queryStringParameters": {
"foo": "bar"
},
"multiValueQueryStringParameters": {
"foo": [
"bar"
]
},
"pathParameters": {
"proxy": "/path/to/resource"
},
"stageVariables": {
"baz": "qux"
},
"body":"{ \"time\": \"evening\" }",
"headers": {
...
},
...
}
You be able to get the query parameters of event in your Lambda using the properties:
event.queryStringParameters && event.queryStringParameters.foo
And the body we need to get with the body property:
if (event.body) {
let body = JSON.parse(event.body)
if (body.time)
time = body.time;
}
In this way in your Lambda you need to parse them in different ways.
For more information of how to work with API Gateway and Lambda take a look here: Tutorial: Build a Hello World REST API with Lambda proxy integration
I need to build a function that takes 5 particular HTTP headers + the request params, aggregate, order, encode, and then hash them in order to validate/authenticate the overall request. However, I am unable to get the header 'Content-Length' to come through to the lambda.
I used Terraform to create the API Gateway (aws_api_gateway_domain_name) and then Serverless to create the endpoints:
functions:
alerts:
handler: src/event.handler
role: arn:aws:iam::${env:AWS_ACCOUNT_ID}:role/alerts_lambda
environment:
API_TRANS_KEY: ${env:API_TRANS_KEY}
REGION: ${self:custom.region}
SNS_ARN: arn:aws:sns:us-east-1:${env:AWS_ACCOUNT_ID}:Transactions
STAGE: ${self:custom.deploymentStage}
events:
- http:
path: /alerts/AccountEvent
method: post
cors: true
integration: lambda-proxy
however the headers I get are :
"headers": {
"Accept": "*/*",
"accept-encoding": "gzip, deflate",
"Cache-Control": "no-cache",
"CloudFront-Forwarded-Proto": "https",
"CloudFront-Is-Desktop-Viewer": "true",
"CloudFront-Is-Mobile-Viewer": "false",
"CloudFront-Is-SmartTV-Viewer": "false",
"CloudFront-Is-Tablet-Viewer": "false",
"CloudFront-Viewer-Country": "US",
"Content-Type": "application/x-www-form-urlencoded",
"Date": "20170504:141752UTC",
"Encryption-Type": "HMAC-SHA256",
"Host": "events.dev.myapi.com",
"Postman-Token": "84bd0cc3-f339-4b2a-8017-31ec9174c37e",
"User-Agent": "PostmanRuntime/7.11.0",
"User-ID": "galileo",
"Via": "1.1 50c3c79d5d7adbc8948ea11709b61d17.cloudfront.net (CloudFront)",
"X-Amz-Cf-Id": "1OE1aGP_3Q-CkXFuJbRwvkGAR2ZaHAPuozckZ6747EP64zZcmXjphw==",
"X-Amzn-Trace-Id": "Root=1-5d0bf01b-8afdb9628f42a9357dbb5c68",
"X-Forwarded-For": "73.72.58.46, 70.132.57.87",
"X-Forwarded-Port": "443",
"X-Forwarded-Proto": "https"
},
Do I need to use a mapping template at this point? Is CloudFront/Api Gateway stripping this header out for some reason (note, I don't set up a CloudFront distribution but API Gateway is creating one due to 'edge' type, but I could change it to 'regional' if that would solve this)?
Based on my tests, the Content-Length header can be passed through when making an API request. However, it won't appear in the event payload for a Lambda proxy integration.
Two alternatives that you could use to receive the Content-Length header:
Use an HTTP API with Lambda proxy integration instead of a REST API in API Gateway.
Use a Lambda non-proxy Integration for the REST API and configure it as follows:
In the Mapping Templates for Integration Request:
Set application/json as the Content-Type
Change the default 'Method Passthrough Template' template by adding the statement - "X-Content-Length" : $input.body.length()
In the Lambda function, the variable event ['X-Content-Length'] will contain the Content-Length.
In the Integration Request stage of the API Gateway for a given endpoint, you can map header values to the body. You could also use the Integration Request step to move the lost headers into the body first. You may also be able to directly re-map it to the headers passed to the Lambda function as well.
More information on header/body mapping can be found here:
https://docs.aws.amazon.com/apigateway/latest/developerguide/request-response-data-mappings.html
I am trying to validate an incoming XML payload via API gateway. To be specific, I actually don't even care about the schema, I just want to make sure that the body is not empty (and maybe that it is valid XML if I can get that functionality). I see a variety of posts from years ago stating that XML input validation is not yet supported in API Gateway.
Can somebody confirm if this is still the case? To provide a specific example, I have a model like this:
{
"$schema" : "http://json-schema.org/draft-04/schema#",
"title" : "Test Schema",
"type" : "object",
"minProperties": 1,
"properties": {
"account_id": {
"type": "string",
"pattern": "[A-Za-z]{6}[0-9]{6}"
}
},
"required": ["account_id"]
}
If I add request body validation using this model for content type "application/json" all is well, but if I do the same for content type "application/xml" no validation is performed.
Yes APIGW only supports json payload validation.
Is it possible to send request with: Content-Type: multipart/form-data to API Gateway?
In my case, I try to send form-data like below via Postman:
user[email]:extest829#ex.com
user[password]:password
user[password_confirmation]:password
user[username]:testUser
But It seems that API Gateway loses the content.
Everything works fine when I send it as: application/x-www-form-urlencoded or application/json.
Using mulipart/form-data is not fully supported by AWS API Gateway, especially when we try to send file via mulipart/form-data.
To send image along with other data from form, probably the best solution would be send it as JSON, where image is encoded with base64.
For example, when someone want to send:
Username (string)
Avatar (image)
Avatar should be encoded with base64. It gives an image as a text, like:
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAyA...
The content of POST message would be:
{
"user_account": {
"avatar": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAyA...",
"username": "my name"
}
}
API Gateway
In API Gateway, under your API, open Models and create new model, for example UserAccount:
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "UserAccountUpdate",
"type": "object",
"properties": {
"user": {
"type": "object",
"properties": {
"avatar": { "type": "string" },
"username": { "type": "string" }
}
}
}
}
In Method Request -> HTTP Request Headers set: Content-Type.
In Method Request -> Request Body set: application/json and as model use created UserAccount model.
In Integration Request -> Content Handling set as: Passthrough.
In Integration Request -> Body Mapping Templates choose: When no template matches the reuqest Content-Type header. (You can also use two other options here and set additional mapping templates).
Backend
Backend receives an image as a text encoded with base64. So probably before it uses futher, it needs to decode it from text to image.
Encoding/decoding images with base64 is quite popular, so you should find some appropriate tools / libs in your frameworks.
When you are sending request with Content-Type: multipart/form-data to API Gateway, you need to pass through your original Content-Type header to your integration endpoint.
aws apigateway update-integration \
--rest-api-id a1b2c3d4e5 \
--resource-id a1b2c3 \
--http-method POST \
--patch-operations op='replace',path='/requestParameters/integration.request.header.Content-Type',value='method.request.header.Content-Type'
Code example for managing binary data in AWS Gateway and Proxy+ Lambda. Or to upload and retrieve files from servers using AWS Gateway and Proxy Lambda+
Click here