I'm trying to add a POST HTTP method to my AWS API Gateway. I'm using SAM framework with Python.
I find that there is a difference in the "body" of the response when it is generated from my desktop (curl or postman) and the AWS API Gateway 'TEST'
Right now, the "POST" command only prints the 'event' object received by the lambda_handler. (I'm using an object to store the event as you can see below)
def add(self):
response = {
"statusCode": 200,
"body": json.dumps(self._event)
}
return response
When I'm using the 'TEST' option of the API Gateway console, with the input:
{"username":"xyz","password":"xyz"}
I receive the following output:
{
"body": "{\"username\":\"xyz\",\"password\":\"xyz\"}",
<the rest of the response>
}
However, when I'm sending the curl (or postman) request:
curl --header "Content-Type: application/json" --request POST --data '{"username":"xyz","password":"xyz"}' <aws api gateway link>
I get the following response:
{
"body": "eyJ1c2VybmFtZSI6Inh5eiIsInBhc3N3b3JkIjoieHl6In0="
<the rest of the response>
}
Why do you think there is a difference between the two tests?
Curl and Postman seem to be automatically Base64 encoding your Authentication credentials.
The responses are the same. The latter response is a Base64-encoded token of the first response.
Related
I have switched from the REST API to HTTP API for it's cost efficiency. I am a noob to AWS so please be patient with me.
LAMBDA
I have a simple Lambda function that returns the same value it's given. It works fine when tested from the actual Lambda console.
exports.handler = async (event) => {
// TODO implement
const response = {
statusCode: 200,
body: "hello" + event["key1"]
};
return response;
};
When the function is tested from the Lambda console with JSON input:
{ "key1":"value1" }, it returns "hellovalue1".
This is what I want my GET/POST request to return.
API GATEWAY (HTTP API)
I created a simple HTTP API which leads to the lambda function above.
https://39lzilxqm2.execute-api.eu-central-1.amazonaws.com/MS_APITest
When the link above is called from the browser, it returns helloundefined.
I would assume the way of passing an argument would be [THE LINK ABOVE]?key1=value1, but that also returns helloundefined
When using other online tools to send JSON data to the link above through GET/POST requests, the result is, again, helloundefined
Honorable mention: When sending a request through postman, it displays an error:
CORS Error: The request has been blocked because of the CORS policy
How do I pass an argument to AWS Lambda using HTTP API?
Thank you in advance.
I tried to replicate the issue using HTTP API. I noticed that the event has the following form.
{
version: '2.0',
routeKey: 'ANY /MS_APITest',
rawPath: '/MS_APITest',
rawQueryString: 'key1=value1',
headers: {
accept: '*/*',
'content-length': '0',
host: 'xxxxx.execute-api.us-east-1.amazonaws.com',
'user-agent': 'curl/7.72.0',
'x-amzn-trace-id': 'Root=1-5f5afd55-332693f425fbcc7a032809da',
'x-forwarded-for': 'xxxxxxxxx',
'x-forwarded-port': '443',
'x-forwarded-proto': 'https'
},
queryStringParameters: { key1: 'value1' },
requestContext: {
accountId: '820872329501',
apiId: 'sg5mhha5ic',
domainName: 'xxxx.execute-api.us-east-1.amazonaws.com',
domainPrefix: 'sg5mhha5ic',
http: {
method: 'GET',
path: '/MS_APITest',
protocol: 'HTTP/1.1',
sourceIp: 'xxxxxx',
userAgent: 'curl/7.72.0'
},
requestId: 'SryFYjtUIAMEV5w=',
routeKey: 'ANY /MS_APITest',
stage: '$default',
time: '11/Sep/2020:04:30:13 +0000',
timeEpoch: 1599798613551
},
isBase64Encoded: false
}
As can be seen the ?key1=value1 is passed as
queryStringParameters: { key1: 'value1' },
Therefore, the lambda function should be:
exports.handler = async (event) => {
// TODO implement
console.log(event)
const response = {
statusCode: 200,
body: "hello" + event['queryStringParameters']["key1"]
};
return response;
};
I verified that it works using:
curl https://xxxx.execute-api.us-east-1.amazonaws.com/MS_APITest?key1=value1
which resulted in:
hellovalue1
it would be up to your api gateway configuration to properly map something - query parameters, POST or PUT body (note GET request bodies are disregarded), form data - to Json input for your lambda. Look closely at https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-create-api-as-simple-proxy-for-lambda.html - note how
the entire request is sent to the backend Lambda function as-is, via a catch-all ANY method that represents any HTTP method. The actual HTTP method is specified by the client at run time. The ANY method allows you to use a single API method setup for all of the supported HTTP methods: DELETE, GET, HEAD, OPTIONS, PATCH, POST, and PUT.
Later they show how
curl -v -X POST "https://r275xc9bmd.execute-api.us-east-1.amazonaws.com/test/helloworld?name=John&city=Seattle" -H "content-type: application/json" -H "day: Thursday" -d "{ \"time\": \"evening\" }"
or, for a GET request,
curl -X GET \
'https://r275xc9bmd.execute-api.us-east-1.amazonaws.com/test/helloworld?name=John&city=Seattle' \
-H 'content-type: application/json' \
-H 'day: Thursday'
all are captured in the catch-all configuration provided there.
However, if that integration isn't quite right, the data won't be passed properly, so look closely.
I am quite new to the WSO2 tools. I recently started using the WSO2 API Manager(ver. 3.1.0).
I created an API gateway by importing the httpbin swagger specs: https://github.com/Azure/api-management-samples/blob/master/apis/httpbin.swagger.json. I published the API, subscribed to it, generated the API keys and started testing.
I imported the spec in Postman, configured the API key for authorization, changed the server to the local gateway http://localhost:8280/Api_Base/1.0
All the resources defined with GET method were accessible, but the POST, PUT and PATCH resources
were not reachable via the gateway. I received the following error response "<faultstring>unknown" for these resources. I tried with cURL as well but got the same results. When I tried POST for httpbin directly it was working just fine:
curl --location --request POST 'http://httpbin.org/post'
{
"args": {},
"data": "",
"files": {},
"form": {},
"headers": {
"Accept": "*/*",
"Host": "httpbin.org",
"User-Agent": "curl/7.58.0",
"X-Amzn-Trace-Id": "Root=1-5e8e0d39-ddf21f1055008f60707cf150"
},
"json": null,
"origin": "95.103.xxx.xxx",
"url": "http://httpbin.org/post"
}
and via my API gateway(with API key as well):
curl --location --request POST 'http://localhost:8280/HTTP_Bin_Mock/1.0/post'
<faultstring>unknown</faultstring>
What could have gone wrong?
please try below CURL command
curl --location --request POST 'http://localhost:8280/HTTP_Bin_Mock/1.0/post' --data '{}' --header 'Content-Type: Application/JSON'
I use POSTMAN to test my Cloudformation created APIs
POST https://6pppnxxxh.execute-api.eu-central-1.amazonaws.com/Prod/users
I got
{
"message": "Missing Authentication Token"
}
My prod stage
I doublechecked PROD Invoke URL.
How to solve this problem?
I tried with curl
curl --header "Content-Type: application/json" --request POST --data '{ "emailaddress" : "acj#rambler.ru,"first name" : "Aca","last name" : "Ljubascikic", "password" : "bbbac_96"}' https://6pppnxxxh.execute-api.eu-central-1.amazonaws.com/Prod/users
The same issue
{"message":"Missing Authentication Token"}
How to test from CLI?
According to your screenshot /Prod/users is a PUT method and you are using POST in your command. I would confirm that first.
Hope this helps.
I am trying to send a gzipped json as POST request body to AWS Application Load Balancer, which calls AWS Lambda.
When I set the content type request header as application/json, I get 502 Bad Gateway error as response and AWS Lambda does not get invoked.
I am using following curl command.
curl -v -s --data-binary #samples/small-batch.json.gz -H "Content-Encoding: gzip" -H "Content-Type: application/json" -X POST https://sub.domain.com/batch
Am I sending invalid request headers?
My AWS Lambda code:
import json
def lambda_handler(event, context):
print("event = ", event)
return {
'statusCode': 200,
'body': json.dumps({ 'success': True }),
'headers': {
'Content-Type': 'application/json'
}
}
Update
If I place request with empty content type, then Lambda gets called successfully.
curl -v --data-binary #samples/small-batch.json.gz -H "Content-Type: " -H "Content-encoding: gzip" -X POST https://sub.domain.com/batch
If I make request with application/gzip content type then Lambda gets called successfully.
curl -v --data-binary #samples/small-batch.json.gz -H "Content-Type: application/gzip" -H "Content-encoding: gzip" -X POST https://sub.domain.com/batch
The 502 error is occurred only when I request with Content Encoding as gzip and Content Type as application/json. But as per my understanding these are valid headers.
Update 2
From the documentation I found at https://docs.aws.amazon.com/elasticloadbalancing/latest/application/lambda-functions.html
If the content type is one of the following types, the load balancer
sends the body to the Lambda function as is and sets isBase64Encoded
to false: text/*, application/json, application/javascript, and
application/xml. For all other types, the load balancer Base64 encodes
the body and sets isBase64Encoded to true.
I think because of this, header Content-Encoding: gzip can not be coupled with header Content-Type: application/json. I think something is going wrong in ALB while calling Lambda.
It's probably not a problem with header Content-Encoding: gzip & Content-Type: application/json but it could be a problem with the binary data size (which could be more than 1 MB) that you are sending.
According to AWS documenation, Lambda has limitation in request and response payload while using with ALB.
The maximum size of the request body that you can send to a Lambda function is 1 MB.
The maximum size of the response JSON that the Lambda function can send is 1 MB.
Please refere:
https://docs.aws.amazon.com/elasticloadbalancing/latest/application/lambda-functions.html
Custom Authorization
Lambda Execution Role : Full Access Api Gateway and Lambda
Token Source: method.request.header.Authorization
Token Validation: blank
Add this custom authorization to api method request . Authorizers test is succes but ı request to api on Postman then 401.
{
"message": "Unauthorized"
}
Custom Authorizer Lambda
console.log('Loading function');
exports.handler = function(event, context, callback) {
console.log("event:",JSON.stringify(event)); console.log("event:",JSON.stringify(context));
console.log('Client token: ' + event.authorizationToken);
console.log('Method ARN: ' + event.methodArn);
callback(null, {
"principalId": "18",
"policyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Stmt1459758003000",
"Effect": "Allow",
"Action": [
"execute-api:Invoke"
],
"Resource": [
"arn:aws:execute-api:*"
]
}
]
}
});
}
Postman Code:
curl --request GET \
--url {url} \
--header 'authorization: Test Token' \
--header 'cache-control: no-cache' \
--header 'content-type: application/json' \
--header 'postman-token: c9110a92-414e-e1aa-61fb-194758dace86'
Solution
Token Source: Authorization
API Gateway has changed, as of today Jan 18. Following needs to be know.
If the Method's Integration Request is not lambda proxied. While creating custom authorier (in Authorizer tab) , you should enter Token Source as "Authorization" and not "method.request.header.Authorization". Also , in the Method Request tab of the method ( eg GET) , you should set the HTTP Header Mapping , for 'Authorization'.
If the Method's Integration Request is proxied , no mapping is required. All the request body+header+parameter+AWSextraStuff is avaliable in the event[] object of lambda. Hence no mapping is required.
Few more pitfall be careful.
- Use standard string like 'Authorization' ( which is a standard) , is you use different string , change every where.
- The authorization token when passed to lambda, for no proxied integration request, as event['authorizationToken']and not event['Authorization']
- If you get error like Lambda Malform... , it is because you are using Lambda Proxy and it requires response in specific format, your not sending data in tha format.
- If your using Postman , switch to 'raw' against 'pretty' mode.
To call an API with the custom TOKEN authorizer
Open Postman, choose the GET method and paste the API's Invoke URL into the adjacent URL field.
Add the custom authorization token header and set the value to allow. Choose Send.
Worth read - http://docs.aws.amazon.com/apigateway/latest/developerguide/use-custom-authorizer.html#call-api-with-api-gateway-custom-authorization
You don't call the Custom Authorizer through Postman, that's API Gateways' job.
Every time that you call an endpoint protected by your custom authorizer the API Gateway will check if the value of the given Authorization Header exists in its Policies Cache. If the value does not exist, your Custom Authorizer will be called to authenticate the request.
A simple representation of the flow:
Lambda Handler (handles a protected endpoint GET /users/{id})
|
| ------------
| |
| Custom Authorizer
| /
| / (if the request is not authorized yet)
| /
Api Gateway
|
|
|
Request (with Authorization Header)
You just have to set the Authorization of your resource method to your custom authorizer and let the API Gateway do all the work.