I would like to download a PDF file using a nodejs lambda function deployed in AWS. Please let me know the configurations to be provided in serverless settings.yaml file.
I am able to download PDF by making below configuration changes from console.
1) Add Content-Type as application/pdf 2) Map the response model for application/pdf=>Empty 3) Change the content handling in integration response from passthrough (default) to Convert to Binary. I am looking for options where these can be provided in serverless configuration file
I am looking for options where content handling and response model can be set using serverless
Below is the snippet from serverless.yml
events:
- http:
path: /test
method: get
integration: lambda
response:
statusCodes:
200:
pattern: '' # Default response method
headers:
Content-Type: "'application/pdf'"
In your lambda function, you have to return a json object like that:
{
statusCode: 200,
headers: { 'Content-Type': 'application/pdf' },
body: YOUR_PDF_base64_encoded_string,
isBase64Encoded: true, // important
};
then, you can use serverless-apigw-binary plugin to config APIGateway Binary Support or you can do it by manualy: Change APIGateway setting
use application/pdf instead of my image mime types.
Related
I'm working on AWS Lambda using serverless framework and I need to specify two methods in functions.yml for each of the API. For example, if I have to create an endpoint for getting books http://basic-url.com/api/books/all. I have to add two methods for it in functions.yml as follows.
get_books:
handler: books/handler.get_books
tags:
Name: get-books
events:
- httpApi:
method: GET
path: /api/books/all
get_books_preflight:
handler: default/handler.get_preflight
tags:
Name: get-preflight
events:
- httpApi:
method: OPTIONS
path: /api/books/all
I have to specify preflight for all of endpoints that I want to create in functions.yml. Is there any way to manage these preflight endpoints for each endpoint automatically?
There is no need to create a function for each preflight endpoint.
When a browser receives a non-simple HTTP request, the CORS protocol requires the browser to send a preflight request to the server and wait for approval (or a request for credentials) from the server before sending the actual request. The preflight request appears to your API as an HTTP request that:
Includes an Origin header.
Uses the OPTIONS method.
Includes the following headers:
Access-Control-Request-Method
Access-Control-Request-Headers
To support CORS, therefore, a REST API resource needs to implement an OPTIONS method that can respond to the OPTIONS preflight request with at least the following response headers mandated by the Fetch standard:
Access-Control-Allow-Methods
Access-Control-Allow-Headers
Access-Control-Allow-Origin
With Serverless Framework you can do this in two easy steps:
Add the cors: true flag to each HTTP endpoint in your serverless.yml:
getBooks:
handler: books/handler.getBooks
tags:
Name: get-books
events:
- http:
path: /api/books/all
method: GET
cors: true
Add the following headers to your response:
module.exports.getBooks = (event, context, callback) => {
// Do work to retrieve a Book
const book = retrieveBook(event);
const response = {
statusCode: 200,
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Credentials': true,
'Access-Control-Allow-Headers': 'Authorization',
},
body: JSON.stringify({
book: book
}),
};
callback(null, response);
};
Eventually, if you use Javascript, take a look to Middy middleware engine for use with Lambda. It has a lot of nice middlewares and one is the cors middleware, which automatically adds CORS headers to your functions.
My application is in serverless framework and I am using vtl template as a lambda resolver. My app stack is AppSync, Lambda on Node JS runtime, Serverless framework and vtl templates.
I am trying to figure how I can add custom response headers from my lambda to client and really appreciate any input on the same. Please find my code below so far:
Lambda
const securityHeaders = {
"content-type": "application/json; charset=utf-8",
"x-content-type-options": "nosniff",
"cache-control": "no-store, no-cache, must-revalidate, proxy-revalidate",
};
callback(null, {
statusCode: 200,
headers: securityHeaders,
body: JSON.stringify({
data,
})
});
return;
serverless yml
functions:
getData:
handler: src/handler.getData
events:
- http:
path: getData
method: post
custom:
configValidationMode: off
appSync:
schema: ['graphql-schemas/data.graphql']
authenticationType: AMAZON_COGNITO_USER_POOLS
mappingTemplates:
- dataSource: GetData
type: Query
field: getData
request: "data-request.vtl"
response: "data-response.vtl"
data-response.vtl
## return the body
#if($ctx.result.statusCode == 200)
##if response is 200
$ctx.result.body
#else
##if response is not 200, append the response to error block.
$utils.appendError($ctx.result.body, "$ctx.result.statusCode")
#end
The above code giving me the result in the postman but I am not able to see my custom headers in the response section. I think I am missing on how to include headers in the response vtl.
It's late but still I also need to add same response headers in Every GraphQL operation. Please refer https://aws.amazon.com/about-aws/whats-new/2022/02/aws-appsync-support-custom-response-headers/ and https://docs.amazonaws.cn/appsync/latest/devguide/http-helpers-in-utils-http.html.
I created VTL template and attached to each resolver in responseMappingTemplate. It works for me.
I'm trying to create a function with CORS enabled.
The function works as expected using Postman (cors headers present), but I get CORS error when trying from a browser (no cors header).
const getTicket = async event => {
var ticketNumber = Date.now();
return {
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Methods': '*',
'Access-Control-Allow-Origin': '*',
},
statusCode: 200,
body: JSON.stringify({"ticketNumber": ticketNumber})
};
};
functions:
getTicket:
handler: code/common/ticket.getTicket
runtime: nodejs12.x
events:
- http:
method: get
path: getTicket
cors: true
authorizer:
arn: ${self:custom.auth0.authorizer_arn}
I also tried few more ways of writing the serverless.yaml file, but none of them worked, the only difference between them being the created methods in my API Gateway. Sometimes I got GET+OPTIONS methods, sometimes only GET, but never worked with CORS.
I get the error:
"Access to XMLHttpRequest at 'https://...amazonaws.com/prod/getTicket' from origin 'http://localhost:8080' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource."
What you are likely seeing is that the server is returning an error before it reaches your return statements. In other words, API Gateway is returning a 502 or 403 (as these are the most common errors), and those specific returns have no CORS headers present by default in API Gateway. I would recommend using CloudWatch to inspect the specific Lambda invocations to determine if your Lambda function itself is erroring out and correct that.
You can also force your API Gateway to return the CORS headers for all responses. In the resources section of the serverless.yml file you can do the following and add any additional entries for other status codes:
resources:
Resources:
GatewayResponseDefault4XX:
Type: 'AWS::ApiGateway::GatewayResponse'
Properties:
ResponseParameters:
gatewayresponse.header.Access-Control-Allow-Origin: "'*'"
gatewayresponse.header.Access-Control-Allow-Headers: "'*'"
ResponseType: DEFAULT_4XX
RestApiId:
Ref: 'ApiGatewayRestApi'
Questions:
IntegrationResponse to Sam template ? Is it possible without OpenApi?
Way to add headers to GatewayResponses in SAM template?
What im trying to achive:
Define gateway responses and integration responses inside a SAM
CloudFront template.
What i checked so far:
SAM github link
SAM issue
I was checking SAM github but for me it wasnt clear how to do it based on the above link.
Also i didnt found any answer on stackoverflow which would explain why the headers are bad in my gatewayresponse snippet
Every Help is appreciated
Some examples:
Globals:
Api:
GatewayResponses:
MISSING_AUTHENTICATION_TOKEN:
ResponseParameters:
# gatewayresponse.header.Cache-Control: "no-cache"
ResponseTemplates:
"application/json" : '{"errors": [{errorKey: "error Value"}]}'
StatusCode: '404'
#defaultResponse: true
Expected function level integrationResponse:
Function:
Type: AWS::Serverless::Function
Properties:
Handler: Function.handler
Timeout: 20
CodeUri: "src/Function"
Role: .......
Events:
FunctionGet:
Type: Api
Properties:
Path: /Function
Method: get
IntegrationResponse:
SOME_ERROR_CODE
ResponseTemplates
integrationresponse.header
So long story short half of my question is stupid. in proxy integration API GW is by defult returning the response from server to client no need further declaration in SAM template.
As for the headers the following way is the correct:
Globals:
Api:
GatewayResponses:
MISSING_AUTHENTICATION_TOKEN:
ResponseParameters:
Headers:
Access-Control-Allow-Origin: "'*'"
Access-Control-Allow-Headers: "'*'"
Cache-Control: "'no-cache'"
Content-Type: "'application/json'"
ResponseTemplates:
"application/json" : '{"errors": [{errorKey: "error Value"}]}'
StatusCode: '404'
#defaultResponse: true
I am attempting to use a node based lambda function to return jpeg images from s3, using API Gateway.
My Lambda function reads as:
s3.getObject(params).promise().then((result) => {
let resp = {
statusCode: 200,
headers: {
'Content-Type': 'image/jpeg'
},
body: result.Body.toString('base64'),
isBase64Encoded: true
};
callback(null, resp);
});
I have also modified the integration response in API gateway to "Convert to binary (if needed)". When I try testing this function I receive the error "Execution failed due to configuration error: Unable to base64 decode the body.".
Is there a step I am missing to allow me to retrieve base64 encoded files?
I'm not sure about it, but have you tried to use this instead of the toString called directly on your object?
Buffer.from(result.Body).toString('base64')
Sounds like you're using AWS integration type of API Gateway instead of LAMBDA integration and in that case API Gateway would expect entire message to be base64 encoded, not just the body. For your use case you probably should use LAMBDA integration and return json with statusCode, body, headers, and Content-Type as you currently do.