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.
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 have an application written in React JS, running on localhost, which makes API calls to API Gateway in AWS. API Gateway forwards requests to a lambda function, which returns a response. I have enabled CORS on AWS side.
At the moment whenever I click a 'request' button in the application I get a response from the gateway. Here is my current python code:
import json
def lambda_handler(event, context):
response = {}
response['result'] = "Success"
response['message'] = "Updated successfully!"
return {
'headers': {
'Access-Control-Allow-Headers': 'Content-Type',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'POST'
},
"body": json.dumps(response)
}
And here's the body of the request:
{
"ID": "1101",
"RequestDate": "2021-02-28"
}
This works fine. I get the 'message' value from this response and can display it without problems.
Next I want to display the information containing some data coming from the request. For example instead of Updated successfully I would like to get the RequestDate from the request and return Updated successfully on 2021-02-28.
I added these two lines:
def lambda_handler(event, context):
body = json.loads(event['body'])
request_date = body['RequestDate']
response = {}
response['result'] = "Success"
response['message'] = "Updated successfully!"
return {
'headers': {
'Access-Control-Allow-Headers': 'Content-Type',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'POST'
},
"body": json.dumps(response)
}
As soon as I make this change I get the following code in my application:
Access to fetch at url from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
This only happens when I add request_date = body['RequestDate']. I tried returning the body only and it was working fine as well.
In my react js application I add following headers as well:
async callAPI(url, method, data) {
let result = await fetch(url, {
method: method,
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*'
},
body: JSON.stringify({
data
})
})
return result.json().then(body => this.notify(body['message']));
}
I tried enabling CORS and deploying the resource again but to no avail. I have added Access-Control-Allow-Origin to allowed headers in AWS. As I mentioned it works fine with post method prior to adding that one line. What could be wrong here and how can I remedy it?
EDIT:
One more thing. I get this error only from my application running on localhost. Curling or using any REST client works fine.
EDIT2:
Added fetch code
Setting up CORS in Lambda is dependent on how you setup API Gateway. API Gateway has several modes [REST, HTTP, WebSocket]. In the case of REST, API Gateway does some pre-processing on the incoming request like parameter validation before passing to Lambda. HTTP Proxy is just that, a pass through to Lambda, and Websockets is not really for this discussion.
I am assuming it is because you are using API Gateway in standard configuration and you have not enabled CORS on API Gateway. The code you provided above will work for API Gateway Configured for HTTP.
If you are using the CDK, or CloudFormation then you must configure CORS there, else the easiest is to use the console.
Go to your API in the AWS Console, select resources, select your method or service, from the actions menu enable CORS. And then publish your updated API.
Here is a link to the AWS documentation that outlines how to do it.
Some advice, when testing endpoints via a browser it is best not to use Localhost or 127.0.0.1 it has unintended consequences. Edit your hosts file and give yourself a domain name, and use that domain name instead.
CORS is there for a reason to prevent Cross Site Scripting or Cross Site Origin Attacks. Try not to use *, testing sure, but production no. If you have code say on S3, and your REST services on say API Gateway you can front both with CloudFront using the same Origin, and let CloudFront know to forward based on URL (e.g /API) to APIGateway and the other to S3. Thus everything has the same domain.
This is what I have on my server and it works fine. Using fetch on the front-end.
Server Code:
import json
result = {"hello": "world"}
return {
'statusCode': 200,
'headers': {
'Content-Type': 'application/json',
"Access-Control-Allow-Origin": "*"
},
'body': json.dumps(result)
}
Front-end Code using ES6
url = "Your url"
path = "Your desired path"
data = {"hello": "world"}
const response = await fetch(url + path, {
method: "POST",
cache: "no-cache",
mode: "cors",
body: JSON.stringify(data),
});
Add statusCode inside response from lambda
return {
'statusCode': 200,
'headers': {
'Access-Control-Allow-Headers': 'Content-Type',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'POST'
},
'body': json.dumps(response)
}
In case there is any error in your lambda it will return default 5XX response without any CORS headers and in such cases browser will complain for cors headers not found.
You can add your current code in try block and in except block you can print the error and return some default response like below
return {
'statusCode': 500,
'headers': {
'Access-Control-Allow-Headers': 'Content-Type',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'POST'
},
'body': json.dumps({'message':'Unexpected error'})
}
Add some log statements in your lambda and check api gateway configuration for Lambda proxy setting.
Make sure you check your api gateway logs and lambda function logs for some more details regarding error.
I have a POST resource and I want to pass a JSON array as the request payload.
#http:ResourceConfig {
methods: ["POST"],
path: "/news-articles/validatetest",
cors: {
allowOrigins: ["*"],
allowHeaders: ["Authorization, Lang"]
},
produces: ["application/json"],
consumes: ["application/json"]
}
resource function validateArticlesTest(http:Caller caller, http:Request req) {
json[]|error jsonarray = <json[]>req.getJsonPayload();
io:println(jsonarray);
}
My request is as below.
curl -X POST http://localhost:9090/news-articles/validatetest -H "Content-Type: application/json" --data '[{"aaa":"amaval", "bbb":"bbbval"},{"ccc":"amaval", "ddd":"bbb val"}]'
But 'jsonarray' gets always null when I run this and make the above curl request.
I guess I am not doing this correct. What is the correct approach to achieve this?
Edit: (Adding the version)
Ballerina version: jBallerina 1.1.3
I have a react application linked to a Django backend on two separate servers. I am using DRF for django and I allowed cors using django-cors-headers. For some reason when I curl POST the backend, I am able to get the request out. However when I use axios POST the backend, I get and error. The status of the POST request from axios is failed. The request and takes more than 10 seconds to complete. My code was working locally (both react and django codes), but when I deployed to AWS ec2 ubuntu, the axios requests stopped working.
Console error logs
OPTIONS http://10.0.3.98:8000/token-auth/ net::ERR_CONNECTION_TIMED_OUT
{
"config": {
"transformRequest": {},
"transformResponse": {},
"timeout": 0,
"xsrfCookieName": "XSRF-TOKEN",
"xsrfHeaderName": "X-XSRF-TOKEN",
"maxContentLength": -1,
"headers": {
"Accept": "application/json, text/plain, */*",
"Content-Type": "application/json;charset=UTF-8",
"Access-Control-Allow-Origin": "*"
},
"method": "post",
"url": "http://10.0.3.98:8000/token-auth/",
"data": "{\"username\":\"testaccount\",\"password\":\"testpassword\"}"
},
"request": {}
}
Here is my request code
axios.post('http://10.0.3.98:8000/token-auth/',
JSON.stringify(data),
{
mode: 'no-cors',
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin' : '*'
},
},
).then( res => (
console.log(JSON.stringify(res)),
)
).catch( err => (
console.log(JSON.stringify(err))
)
);
my curl code that worked
curl -d '{"username":"testaccount", "password":"testpassword"}' -H "Content-Type: application/json" -X POST http://10.0.3.98:8000/token-auth/
UPDATE 1
on firefox i am getting the warning
Cross-Origin Request Blocked: The Same Origin Policy disallows reading
the remote resource at http://10.0.3.98:8000/token-auth/. (Reason:
CORS request did not succeed).[Learn More]
UPDATE 2
Perhaps it has something to do with my AWS VPC and subnets? My django server is in a private subnet while my react app is in a public subnet.
UPDATE 3 - my idea of what the problem is
I think the reason why my requests from axios aren't working is because the requests i'm making is setting the origin of the request header to http://18.207.204.70:3000 - the public/external ip address - instead of the private/internal ip address which is http://10.0.2.219:3000 - i search online that the origin is a forbidden field so it can't be changed. How can i set the origin then? Do I have to use a proxy - how can I do that.
try this http request instead of axios, it's called superagent (https://www.npmjs.com/package/superagent) , just install it to your react app via npm,
npm i superagent
and use this instead of axios.
import request from 'superagent'
const payload ={
"1": this.state.number,
"2": this.state.message
}
request.post('LINK HERE')
.set('Content-Type', 'application/x-www-form-urlencoded')
.send(payload)
.end(function(err, res){
if (res.text==='success'){
this.setState({
msgAlert: 'Message Sent!',
})
}
else{
console.log('message failed/error')
}
});
The issue here is that the request is being made on the client browser. You need to either use a reverse proxy or request directly to the api server. You cannot do a local ssh forwarding either.
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.