AWS API GATEWAY - empty response body - amazon-web-services

I'am using node serverless and python to deploy service on AWS.
Services used: S3, API-Gateway, Cloudformation, Lambda.
The problem is... I am getting empty response body:
{
"statusCode": 200,
"isBase64Encoded": false,
"headers": {
"Content-Type": "application/json"
},
"body": "{}"
}
The field "body" is empty. It is same when I test with POSTMAN.
but when I test on lambda it works fine:
{
"statusCode": 200,
"isBase64Encoded": false,
"headers": {
"Content-Type": "application/json"
},
"body": "{\"55b7badc-75af-41c0-9877-af308264cb33\":\"0.4666666666666667\",\"4694e172-322e-4a51-930e-d3b9bfd3c2e6\":\"0.36363636363636365\",\"c5447cc5-936d-4aa6-97c4-3f51a7e7c283\":\"0.3\",\"6abf0893-5d32-4a43-942f-aaef4395d91d\":\"0.2727272727272727\",\"c0bf1214-fb41-48eb-b07d-f81b71ba0061\":\"0.25\"}"
}
Here is the yml file:
service: collaborative
provider:
name: aws
runtime: python3.6
region: eu-west-1
defaults:
stage: dev1
region: eu-west-1
package:
include:
- collaborative
exclude:
- .git
- .idea
- .col_ser.txt
custom:
integration: lambda
functions:
collaborative:
name: lambda-collaborative
handler: handler.lambda_handler
events:
- http:
path: recommend_user
method: post
integration: lambda
cors: true
request:
template:
text/xhtml: '{ "stage" : "$context.stage" }'
application/json: '{ "httpMethod" : "$context.httpMethod" }'
response:
headers:
Access-Control-Allow-Origin: "'*'"
statusCodes:
400:
pattern: '.*wrong.*'
template:
application/json: >
#set ($errorMessageObj = $input.path('$.errorMessage'))
$errorMessageObj
Resources:
ApiGatewayMethodRecommenduserPost:
Type: AWS::ApiGateway::Method
Properties:
Integration:
IntegrationHttpMethod: POST
Type: lambda

If you have invoked your lambda function asynchronously from API Gateway then it's normal to receive an empty response because in this case, API Gateway does not wait for lambda to response back and simply return an empty response. However, you can create your own integration response template from API gateway depending upon the status code.
If you have invoked synchronously and still haven't received the response then you can also invoke your lambda function from API gateway and observe if your lambda function is working properly or not.

Related

How to allow API Gateway to return a 405 response

Hello AWS Cloud Gurus,
I am trying to allow my REST API to return a 405 when an unsupported HTTP verb is used on any resource.
I see there are ways to define GatewayResponses.
However, I don't see any obvious approach to return a 405 (other than to define it as the DEFAULT_4XX which seems incorrect)
ExampleApi:
Type: AWS::Serverless::Api
Properties:
StageName: Prod
OpenApiVersion: '3.0.1'
GatewayResponses:
DEFAULT_4XX:
StatusCode: 405
ResponseTemplates:
"application/*": '{ "message": "Method Not Allowed" }'
Does anyone know how to do this?
One solution is to create a lambda function, attached to the API, to handle a specific endpoint which needs to indicate 405
ExampleApi:
Type: AWS::Serverless::Api
Properties:
StageName: Prod
OpenApiVersion: '3.0.1'
MethodNotAllowedResponse:
Type: AWS::Serverless::Function
Properties:
Runtime: nodejs14.x
Handler: index.handler
InlineCode: |
let response;
exports.handler = (event, context, callback) => {
response = {
"statusCode": 405,
"headers": {
"Content-Type": "application/problem+json"
},
"body": JSON.stringify({
"type": "https://tools.ietf.org/html/rfc7231#section-6",
"status": 405,
"title": "Method Not Allowed",
"detail": `Method ${event.httpMethod} is not allowed on ${event.path}`
})
}
callback(null, response);
}
Events:
Televisions:
Type: Api
Properties:
Auth:
Authorizer: NONE
RestApiId: !Ref ExampleApi
Path: '/not/allowed/path'
Method: patch
This can be implemented as a mock integration that you then use for any methods that are not implemented/supported.
Integration request mapping
{
"statusCode": 405,
"message": "The invoked method is not supported on the API resource."
}

How to use AWS SAM CLI Local HttpAPI with JWT Bearer token Auth offline?

I would like to use AWS SAM JWT HttpApi Auth offline
Based on this AWS example, I decided to create the following YAML file.
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Resources:
HelloWorldFunction:
Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
Properties:
CodeUri: hello-world/
Handler: app.lambdaHandler
Runtime: nodejs10.x
Events:
ExplicitApi: # warning: creates a public endpoint
Type: HttpApi
Properties:
ApiId: !Ref HttpApi
Method: GET
Path: /path
TimeoutInMillis: 15000
PayloadFormatVersion: "2.0"
RouteSettings:
ThrottlingBurstLimit: 600
HttpApi:
Type: AWS::Serverless::HttpApi
Properties:
FailOnWarnings: True
Auth:
Authorizers:
MyOauthAuthorizer:
IdentitySource: $request.header.Authorization
JwtConfiguration:
audience:
- audience
issuer: issuer-url
DefaultAuthorizer: MyOauthAuthorizer
Using AWS::Serverless:HttpApi based on docs creates an Amazon API Gateway HTTP API which supports JWT based auth.
I start it with
sam local start-api
However, when I query it with Postman, with or without JWT Bearer token, the request succeeds.
And the AWS query does not contain a single authenticated user object.
Running it with Debug mode does not provide any useful additional information either.
let response;
exports.lambdaHandler = async (event, context) => {
try {
// const ret = await axios(url);
response = {
statusCode: 200,
body: JSON.stringify({
message: "hello world",
event,
context,
// location: ret.data.trim()
}),
};
} catch (err) {
console.log(err);
return err;
}
return response;
};
My expectation would be that AWS SAM CLI would convert the Bearer token based on the correctly provided Issuer URL into an identity value which I can use in later operations.
Does AWS SAM Local not support this while running locally?
SAM Local unfortunately doesn't support Authorizers. There is a feature request on AWS SAM's GitHub repository to add this feature, see https://github.com/aws/aws-sam-cli/issues/137

How to configure CORS for AWS API Gateway using OpenAPI?

I have an OpenAPI spec for an api which I am deploying via CDK. The spec looks like:
openapi: 3.0.1
info:
title: My API
description: My REST API with CORS enabled
version: 0.1.0
x-amazon-apigateway-cors:
allowOrigins:
- "*"
allowCredentials: true
exposeHeaders:
- "x-apigateway-header"
- "x-amz-date"
- "content-type"
maxAge: 3600
allowMethods:
- "*"
allowHeaders":
- "x-apigateway-header"
- "x-amz-date"
- "content-type"
- "Authorization"
components:
securitySchemes:
lambda:
type: "apiKey"
name: "Authorization"
in: "header"
x-amazon-apigateway-authtype: "custom"
x-amazon-apigateway-authorizer:
authorizerUri: "{{my-lambda-authorizer}}"
authorizerResultTtlInSeconds: 300
type: "token"
paths:
/user/{id}:
get:
summary: Get info of specified user.
parameters:
- in: path
name: id
required: true
schema:
type: string
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/User'
security:
- lambda: []
x-amazon-apigateway-integration:
uri: "{{my-lambda}}"
passthroughBehavior: "when_no_match"
httpMethod: "POST"
type: "aws_proxy"
When I try to access this via fetch(), I get an error Failed to load resource: Origin http://localhost:8000 is not allowed by Access-Control-Allow-Origin.
fetch('https://api.example.com/user/1')
.then(response => response.json())
.then((user: User) => {
// do something
})
.catch((err) => {
console.log("Error: " + err);
});
The API is accessible at api.example.com and I am running the website locally using Gatsby at localhost:8000.
The AWS docs seem to state that CORS is enabled when I put x-amazon-apigateway-cors at the root of the spec, but CORS doesn't seem to be enabled (or working) when I try to access the API. How do I enable CORS for my API without needing to configure it in the console?
Amazon API Gateway offers two types of APIs: REST APIs and HTTP APIs. REST APIs were the kind of APIs originally introduced with Amazon API Gateway, while HTTP APIs got announced at the end of 2019.
Based on the description of your OpenAPI specification I assume you're trying to deploy a REST API. The x-amazon-apigateway-cors OpenAPI extension however only works for HTTP APIs.
You have two choices now: You either switch to use a HTTP API or you configure CORS manually. As long as you don't need features only supported by REST APIs, I suggest you switch to use a HTTP API, as that's the more modern kind of API Amazon API Gateway offers.
If you want to use a REST API, enabling CORS requires more manual configuration. That's documented by AWS in Enabling CORS for a REST API resource. Essentially you have to ensure your integration returns proper CORS headers. For a NodeJS AWS Lambda function that could look like:
exports.handler = async (event) => {
const response = {
statusCode: 200,
headers: {
"Access-Control-Allow-Headers" : "Content-Type",
"Access-Control-Allow-Origin": "https://www.example.com",
"Access-Control-Allow-Methods": "OPTIONS,POST,GET"
},
body: JSON.stringify('Hello from Lambda!'),
};
return response;
};
For CORS pre-flight requests to work you'd also have to ensure that OPTIONS-requests also return the correct headers. The easiest way to achieve that is to add a mock-integration for the OPTIONS-request to your OpenAPI specification. That would look like:
options:
responses:
'200':
description: Default response
headers:
Access-Control-Allow-Headers:
schema:
type: string
Access-Control-Allow-Methods:
schema:
type: string
Access-Control-Allow-Origin:
schema:
type: string
x-amazon-apigateway-integration:
type: mock
requestTemplates:
application/json: |
{"statusCode" : 200}
responses:
default:
statusCode: 200
responseParameters:
method.response.header.Access-Control-Allow-Headers: "'*'"
method.response.header.Access-Control-Allow-Methods: "'OPTIONS,POST'"
method.response.header.Access-Control-Allow-Origin: "'https://example.com/'"
Please also mind that modern browsers don't support localhost as origin for CORS, so you might need to work around that as well.

How to change API Gateway response

I have an aws ApiGateway which verify my token and pass request to lambda.
When I have an error from lambda, APIGateway response is
{
"statusCode": 500,
"error": "Internal Server Error",
"message": "..."
}
But if I don't pass my token, then APIGateway will return me
{
"message": "Unauthorized"
}
And in postman I have statusCode: 401.
How I want it to be:
{
"statusCode": 401,
"error": "Unauthorized"
}
I use serverless.yml to deploy:
functions:
index:
handler: dist/index.handler
events:
- http:
cors: true
path: '/'
method: any
authorizer:
type: COGNITO_USER_POOLS
authorizerId:
Ref: ApiGatewayAuthorizer
Please, tell me how I have to change my serverless.yml to change the 'Unauthorized' error to be as at third code example.
try to implement this:
https://github.com/SeptiyanAndika/serverless-custom-authorizer:
Allows to get reponses like:
{
"success":false,
"message":"Custom Deny Message"
}
Adding to #svladimirrc, it will work even if you don't have a custom authorizer in place, just make sure you have the proper name of your API Gateway configured to link to:
resources:
Resources:
ApiGatewayRestApi:
Type: AWS::ApiGateway::RestApi
Properties:
Name: ${self:provider.stage}-${self:service}
InvalidApiKeyGatewayResponse:
Type: 'AWS::ApiGateway::GatewayResponse'
Properties:
RestApiId:
Ref: 'ApiGatewayRestApi'
ResponseType: INVALID_API_KEY
ResponseTemplates:
application/json: "{\"success\":false,\"message\":\"Invalid API key\"}"
StatusCode: '401'
You can achieve this by modifying Gateway Responses.
Go to API Gateway in AWS Management Console.
Select your API.
Click "Gateway Responses" which can be seen on the left side.
Select "Unauthorized" in the list of Gateway Responses.
Select "application/json" in Response Templates and click "Edit".
Update the response template body based on your requirements.
Click "Save".
Re-deploy your API.

Customizing API when integrating Lambda with API through AWS SAM

Problem
I'm trying to run simple integration between Lambda and API Gateway with AWS SAM. I want to customize the input for lambda - apply some requestTemplates. But those seem to be ignored.
Steps to reproduce
Running with:
sam local start-api
curl -v -XPOST -H "Content-type: application/json" -d '{"jarmil":"prdel"}' http://localhost:3000/
The output is:
START RequestId: 43594e8a-c3af-4f47-9d85-6c605131f02a Version: $LATEST
Processing event {'httpMethod': 'POST', 'body': '{"jarmil":"prdel"}', 'resource': '/', 'requestContext': {'resourcePath': '/', 'httpMethod': 'POST', 'stage': 'prod', 'identity': {'sourceIp': '127.0.0.1:53210'}}, 'queryStringParameters': {}, 'headers': {'Accept': '*/*', 'Content-Length': '18', 'Content-Type': 'application/json', 'User-Agent': 'curl/7.43.0'}, 'pathParameters': None, 'stageVariables': None, 'path': '/'}
END RequestId: 43594e8a-c3af-4f47-9d85-6c605131f02a
It seems to work as if the default API was created. I get the event printed. But it seems my API definition is completely ignored. I always get back the original event without requestTemplates being applied. Symptoms:
Whatever I put in my swagger definition has no effect. Changing produces, putting there any malformed swagger
When I use invalid RestApiId (non-existing reference) - no change
When I use non-existing type instead of AWS::Serverless::Api - no effect
Environment
Sam version: 0.2.4
OS: X
Code
My template.yml:
AWSTemplateFormatVersion : '2010-09-09'
Transform: 'AWS::Serverless-2016-10-31'
Description: POC
Resources:
ScheduleFunction:
Type: 'AWS::Serverless::Function'
Properties:
Runtime: python3.6
Handler: lambda_function2.lambda_handler
Events:
ApiRoot:
Type: Api
Properties:
RestApiId: !Ref ScheduleApi
Path: /
Method: ANY
ScheduleApi:
Type: 'AWS::Serverless::Api'
Properties:
StageName: dev
DefinitionUri: swagger.yml
My swagger.yml:
swagger: 2.0
info:
title: "Scheduling API"
consumes:
- application/json
produces:
- application/json
paths:
/:
post:
x-amazon-apigateway-integration:
httpMethod: post
type: aws
requestTemplates:
application/json: "#input x"
And the lambda_function2.py:
def lambda_handler(event, context):
print("Processing event", event)
return event
Further resources
This example from sam repo looks pretty much the same as what I want to achieve. Doesn't work either.