How can an aws lambda know what endpoint called it from API Gateway? - amazon-web-services

If two different endpoints use one lambda, how can the lambda know about the parts of the URL path?
How can one lambda know it was called from /zips vs /zip?zip_code=02140 ?
I can use event["queryStringParameters"]['zip_Code'] to get the URLs query string - /zip?zip_code=02140 - from within the lambda,
but how can I know if I am called from the /zips endpoint?
I tried using event["pathStringParameters"]['zips'] which I created a test event for but that didn't work, not recognized.
I can use one lambda per specific resource but I'd like to also know other approaches and how those that use the same endpoint can have their path revealed.

If I am following what you're asking for, namely that you have one Lambda function servicing two API Gateway endpoints, then I think you have two options:
Use the path parameter
Set a custom header and check that in headers
From the AWS documentation:
In Lambda proxy integration, API Gateway maps the entire client request to the input event parameter of the backend Lambda function as follows:
So given this HTTP request:
POST /testStage/hello/world?name=me HTTP/1.1
Host: gy415nuibc.execute-api.us-east-1.amazonaws.com
Content-Type: application/json
headerName: headerValue
{
"a": 1
}
You'll have available:
"message": "Hello me!",
"input": {
"resource": "/{proxy+}",
"path": "/hello/world",
"httpMethod": "POST",
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
...
Here both path and headers will serve your need.
Personally, I would recommend setting a custom header. That way, no matter if your API routing changes, your Lambda will still pick it up.

You can get the path that invoked your Lambda in the event object, under event["requestContext"]["path"]
You can see more details on what the event object contains in the documentation for using AWS Lambda with API Gateway

Related

Specify HTTP Request Headers in x-amazon-apigateway-integration?

In API Gateway, you can specify, under a method's Method Request setting, HTTP Request Headers to forward along. I currently set a required name of Authorization as part of the Swagger specification document I import via put_rest_api API Gateway client. This gets imported into API Gateway just fine, as shown here.
parameters: [{
"in": "header",
"name": "Authorization",
"type": "string",
"required": true
}]
But I also need to set it a HTTP Header mapping via the method's Integration Request
Is there a way to specify this when using the x-amazon-apigateway-integration object of a Swagger specification document? I've tried something like the following but I get an error message.
requestParameters: {
"integration.request.header.authorization" => "method.request.header.authorization"
}
errors : [Invalid mapping expression parameter specified: method.request.header.authorization]
Looks like the name is case sensitive. Changing it to match the name of the parameters object allows it to import and be set within API Gateway.
"integration.request.header.Authorization": "method.request.header.Authorization"

AWS API Gateway integration request Http headers not being passed to lambda

This is similar to this post API-Gateway Integration Request HTTP Header not mapping query string to header but I can't see any answer for it and all of the answers are not related to what the question is intended.
I am trying to add the integration request parameters in API Gateway, but whenever I set 'Http Headers' as shown here and 'Mapping template' as passthrough, I could not see that header when I log inside the lambda.
Also in the integration response I cannot reference it inside the integration header response parameters. The "integration.response.header.cokers" will be returned blank when I invoke the API. This is how I configured the integration response
Ultimately the solution here is to implement a lambda proxy integration.
A proxy integration in API Gateway tells API Gateway to simply forward all headers to the integration for processing, which means you will see all of those values in your lambda function.
NOTE: Lambda has to return a specific response format back to API Gateway if it's a proxy integration. Ultimately, the response should be something like:
{
'statusCode': 200,
'headers': {
'echo-proxy': True
},
'body': 'some payload'
}
What you are trying to do right now is map everything manually, which is a deprecated approach and usually you don't want to do that unless you absolutely have to because it's kind of a pain.
If you have to map the headers manually start by mapping them in the the method request so it can carry on to the next step and so on. Basically like this:
Method Request -> Maps variable to Integration Request -> Maps variable to Body Mapping Template -> Maps variable to actual request header
What you have in your screenshot for the Integration Request -> HTTP Headers is:
Name: cokers
Mapped from: 'blah'
However, "mapped from" should look something like "method.request.header.coker" which is a standardized path (meaning to get the value from the Method Request Header field with name "coker").
Once you have added the coker header to the Method Request, and the Integration Request HTTP Headers are mapped correctly, you have to implement a mapping template. Set the content-type to application/json with passthrough set to "When there are no templates defined(recommended)" and a simple mapping template:
{
"headers": {
"coker" : "$input.params('coker')"
}
}
That is the way my API is setup and it returns the following to me because I had my lambda function return the event as a json object back to API GW:
{"body": "{\"headers\": {\"coker\": \"mapped\"}}", "statusCode": 200}
NOTE: the value of my header "coker" in the request on the client side is "mapped"
UPDATED ANSWER
To map the original "coker" header to "coker2" (or any other name you want to give it) you simply set the name of the header in your mapping template like so:
{
"headers": {
"coker2" : "$input.params('coker')"
}
}
Then edit your lambda function to return "coker2" header and you should get a response like this:
{"body": "{\"headers\": {\"coker2\": \"mapped\"}}", "statusCode": 200}

How to add CORS header to AWS API Gateway response with lambda proxy integration activate

I use lambda as backend for AWS API Gateway with lambda proxy integration and want to add CORS into response header.
According to documentation:
http://docs.aws.amazon.com/apigateway/latest/developerguide/how-to-cors.html
However, you must rely on the back end to return the Access-Control-Allow-Origin headers because the integration response is disabled for the proxy integration.
How can I program it in my lambda function with Python.
To create OPTIONS method you can enable it from the Gateway
Navigate to your Gateaway, Select Resources from left side
Select endpoint, on top there will a button "Action", there you will need to select "Enable CORS", save the settings.
Deploy the Gateway.
It will create a method OPTIONS on the resource(endpoint)
for GET/POST other HTTP Verbs you will need to manage it from your code, in case of python
return {
'statusCode': "200",
'body': json.dumps({"test" : "123"}),
'headers': {
"Content-Type" : "application/json",
"Access-Control-Allow-Origin" : "*",
"Allow" : "GET, OPTIONS, POST",
"Access-Control-Allow-Methods" : "GET, OPTIONS, POST",
"Access-Control-Allow-Headers" : "*"
}
}
for other unhandled cases like IntegrationTimeout (504) or Error in your code (502), you can configure default response headers at API Gateway Level. refer Default Response Headers: AWS API Gateway w/ Proxy Integration
you need to add a method "options" to your api gateway and using a proxy lambda... return
result.headers = { "Access-Control-Allow-Origin": "domain.com" }
so when the browser will first call options to your server it will return the CORS headers.
the thing is that, by default your lambda method will be called for "any" method, so you need to change the default one to get,post or whatever you need
note: you could also use the same method, like any or options,get,post and if it is a options call, only return status 200 and the cors header. it depends if you are using or not an auth method for get,post,etc
there is an option in Lambda console "Enable CORS" if you are just using lambda with nothing strange

Using AWS API Gateway to POST email via AWS SES

I'm setting up an API from an App which allows the App user to contact someone for follow-up, but hides the email address from the sender (and allows it to be changed without a new release of the App).
I've managed to setup an AWS API Gateway GET method which sends email directly via SES
https://...aws.com/em/send/en?body=The+email+is+here
Using a path override of
Action=SendEmail
&Source=source%40mydomain.org
&Destination.ToAddresses.member.1=follow.up%40anydomain.com
&Message.Subject.Data=Request+For+Followup
&Message.Body.Text.Data={body}
I would much prefer to use a POST method - but am really struggling to work out what should go in the Action parameter and how to create the mapping template - or even if it is possible to create an application/x-www-form-urlencoded request without having to resort to a lambda function - although the AWS documentation does include a $util.urlEncode() function.
http://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-mapping-template-reference.html
Edit:
I am trying to use a POST method to AWS Gateway and a POST to SES with the contents in the request body, but it is just not clear to me how to create the mapping template and I can't find any examples for SES.
Testing it with the mapping template (as per the example in the documentation)
Source=source%40mydomain.org
&Destination.ToAddresses.member.1=follow.up%40anydomain.com
&Message.Subject.Data=Request+For+Followup
&Message.Body.Text.Data=The+message+goes+here
And a content type application/x-www-form-urlencoded AWS gateway gives the error:
{
"message": "Unsupported Media Type"
}
If I use a mapping template
{
"Action":"SendEmail",
"Source":"source#mydomain.org",
"Destination":{
"ToAddresses":{
"member":["follow.up#anydomain.com"]
}
},
"Message":{
"Subject": {
"Data":"Request For Followup"
},
"Body":{
"Text":{
"Data":"The message goes here"
}
}
}
}
and a content type of application/json AWS gateway gives the error
{
"Output": {
"__type": "com.amazon.coral.service#UnknownOperationException",
"message": null
},
"Version": "1.0"
}
Your mapping template configuration must be in a POST format, not in the JSON, since SES service doesn't support json.
For example, this config has worked out for me:
Action=SendEmail&
Message.Body.Text.Data=Some+text&
Message.Subject.Data=New+message&
ReturnPath=some#mail.io
&Destination.ToAddresses.member.1=some#destination.com
&Source=your#sender.email
Also, your path override must be SendEmail only for the case above

Is it possible to add an HTTP header from AWS Custom Auth on API gateway?

I am using Custom Auth on AWS API Gateway, but I would like to add an extra HTTP header depending on the result. Does anyone know if this is possible, or how to do it. If it is not, is there an idea of if or when this will be possible?
Many thanks.
We recently added support for this. Docs should be up soon.
Now you can return an object like this from the authorizer function:
{
"principalId": "xxxxxxxx", // The principal user identification associated with the token send by the client.
"policyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Action": "execute-api:Invoke",
"Effect": "Allow|Deny",
"Resource": "arn:aws:execute-api:<regionId>:<accountId>:<appId>/<stage>/<httpVerb>/[<resource>/<httpVerb>/[...]]"
}
]
},
"context" : {
"key" : "value",
"numKey" : 1,
"boolKey" : true
}
}
Arrays and objects aren't allowed, only string/number/boolean as valid JSON. The root key must be named context.
You can access those values in the request $context like so:
$context.authorizer.key -> value
$context.authorizer.numKey -> 1
$context.authorizer.boolKey -> true
So to answer your question, you wont' be able to conditionally add the header, but you could set the header value to $context.authorizer.yourKey and if yourKey isn't set in the authorizer response, the header value would be blank (but the header would still be sent).
Edit:
Docs are live http://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-lambda-authorizer-output.html
I was able to get this working after a day of pulling my hair out and hopefully I can save someone from that. It adds a bit more to Jacks' response.
Basically, you can dynamically add headers by using Body Mapping Templates
Create and Authorizer Lambda (you can use the authorizer blueprint lambda to get started), do your business logic to create the AuthPolicy and populate the context object with the key/values.
On the API Gateway, select the resource, click on Method Request and set the Auth to your Authorizer lambda
Open Method Execution, select the Integration type and make sure to unselect Use Lambda Proxy integration (if your request points to a lambda)
Add a Body Mapping Template - create one from the template and this is where you have access to $context.authorizer.key
Add the following to your template (just under "body-json" : $input.json('$')," is fine)
"headers": {
"key-header" : "$util.escapeJavaScript($context.authorizer.key)",
#foreach($param in $input.params().header.keySet())
"$param": "$util.escapeJavaScript($input.params().header.get($param))"
#if($foreach.hasNext),#end
#end },
This will add a new header called "key-header" and also forward all the original headers allowing you to append additional information like user_id, user_role, etc to the upstream service.
I tried Emanuel Canha's answer above, but that no longer seems to work. The way I got it to work yesterday (5 June 2019) is to
Create the lambda
Add the lambda under Method Request as your authorizer
Open Integration Request
Open HTTP Headers
Add a header with the name you want (key-header in the above example)
Use context.authorizer.yourKey as the mapped from entry. (Note that you do not use the $ in this field.)
You can only get PrincipalId from authorizer result, in your integration request, you can map a header value using context.authorizer.principalId
These answers seem to be specific to HTTP API Gateways. We're trying to user RestAPI API gateway which would work with both Lambdas (that take pricnipalID as part of the event object forwarded by Lambda Authorizer) but also, we would like to Authorize non-Lambda endpoints (deployed in private VPC and accessed via Network Load Balancer) so we want to put the principalId into HTTP headers the endpoint would parse.