Hapi-Swagger failing with header value - node.js

I am using hapi-swagger in our application where one of API trying to use custom header but when I ivoke that API with custom header getting below error
{
"statusCode": 400,
"error": "Bad Request",
"message": "Invalid request headers input"
}
Below the API where I am using headers with validator.
{
method: 'POST',
path: '/v1/testapi',
config: {
description: 'Greet user',
notes: ['Use to greet a user'],
tags: ['api'],
handler: function ( request, h ) {
console.log('sending response...');
return h.response('OK');
},
validate: {
headers: {
name: Joi.string().required()
}
}
}
}
Below are the versions we are using.
"hapi": "17.2.2",
"hapi-swagger": "9.1.1",
"joi": "13.1.2",

I ran into this recently. You need to use the allowUnknown validation option to allow unknown headers (https://github.com/hapijs/hapi/issues/2407#issuecomment-74218465).
validate: {
headers: Joi.object({
name: Joi.string().required()
}).options({ allowUnknown: true })
}
Also note that hapi 17 changed the default behavior for reporting validation errors. If you want to log or return the actual error indicating which headers are failing validation rather than a generic "Bad Request" you can add a custom failAction hander (https://github.com/hapijs/hapi/issues/3706).

Related

Missing credentials while calling YouTube insert API

I want to use API key instead of OAuth token to call insert API from YouTube v3 lib. Code snippet is like below:
await google
.youtube("v3")
.videos.insert({
key: "my-youtube-api-key",
part: "id,snippet,status",
notifySubscribers: false,
requestBody: {
snippet: {
title: "Test video title",
description: "Test video description",
},
status: {
privacyStatus: "public",
},
},
media: {
body: fs.createReadStream(filePath),
},
})
.catch((err) => {
console.log("Upload to YouTube failed", err);
return null;
});
However, I am hitting error code 401, message is:
code: 401,
errors: [
{
message: 'Login Required.',
domain: 'global',
reason: 'required',
location: 'Authorization',
debugInfo: 'Authentication error: missing credentials.',
locationType: 'header'
}
]
How can I fix this issue? Isn't API key not supported? Thanks!
As per the docs, it's insufficient to use an API key on Videos.insert endpoint; you'll have to be properly authorized to call this endpoint:
Authorization
This request requires authorization with at least one of the following scopes (read more about authentication and authorization).
Scope
https://www.googleapis.com/auth/youtube.upload
https://www.googleapis.com/auth/youtube
https://www.googleapis.com/auth/youtubepartner
https://www.googleapis.com/auth/youtube.force-ssl

Handling errors in aws lambda in serverless

I'm trying to return errors from my lambda functions but for all of the errors it just returns status 502 with message Internal server error. Previously it was just returning cors error for all types of returned errors. After adding 'Access-Control-Allow-Origin' : '*' in api gateway responses, i'm getting 502 error. I've logged thrown errors in catch block & i can see the specific errors in CloudWatch. I've seen this question but that didn't help anyway. Please note that instead of using callback i'm using async await. Also i've tried with & without lambda-proxy integration but the response is same. Do i need to configure something else in case of lambda-proxy?
const test = async (event, context) => {
try {
context.callbackWaitsForEmptyEventLoop = false;
const error = { status: 400, message: 'my custom error' };
throw error;
} catch(error) {
console.log('==== error ====', error);
return createErrorResponse(error.status || 500, error.errors || error.message);
}
createErrorResponse
const createErrorResponse = (statusCode, message, stack={}) => ({
statusCode,
headers: {
'Access-Control-Allow-Origin' : '*',
'Access-Control-Allow-Credentials': true,
},
body: JSON.stringify({
error: message
}),
stack: JSON.stringify({ stack })
});
export default createErrorResponse;
serverless.yml
test:
handler: api/index.test
timeout: 360
events:
- http:
path: test-lambda-api
method: post
cors: true
When using Lambda proxy integration with API Gateway, the response from Lambda is expected in a certain format:
var response = {
"statusCode": 200,
"headers": {
"my_header": "my_value"
},
"body": JSON.stringify(responseBody),
"isBase64Encoded": false
};
Include your stack JSON key inside the body itself. You can also refer to this post for more info: https://aws.amazon.com/premiumsupport/knowledge-center/malformed-502-api-gateway/
Also, you should enable debug logs for the API Gateway stage which will give a better understanding of what is sent and received from the API GW integration.

Axios Post Request in NodeJS

I have an API call in POSTMAN, which I am trying to replicate in nodeJS project using Axios, but the result is not the same that of a POSTMAN.
The call looks like this in POSTMAN:
Inside the body element I have: models and values properties and Authorization is of type Bearer .
I get a response result as an array.
Now, I try to do the same using axios, but I get error:
Code
axios.defaults.baseURL = 'http://XXXXXXXXXXXXXXX:8069/api';
axios({
method: 'POST',
url: '/create/res.users',
data: {
models: 'res.users',
values: "{ 'login': 'john#gmail.com', 'name':'john', 'email':'john#gmail.com', 'password': '123123123' }"
},
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': 'Bearer ' + accessToken
},
})
.then(function (response) {
console.log("Register", response);
res.status(200).send({
message: response.data
});
})
.catch(function (error) {
console.log("Error", error.response.data);
res.status(error.response.status).send({
message: error.response.data
});
});
Error
{
"message": {
"name": "odoo.exceptions.RedirectWarning",
"message": "You cannot create a new user from here.\n To create new user please go to configuration panel.\n74\nGo to the configuration panel",
"arguments": [
"You cannot create a new user from here.\n To create new user please go to configuration panel.",
74,
"Go to the configuration panel"
],
"exception_type": "error",
"code": 500,
"description": "Restful API Error"
}
}
By default, axios serializes JavaScript objects to JSON. To send data in the application/x-www-form-urlencoded format instead, This document may help you:
https://github.com/axios/axios#using-applicationx-www-form-urlencoded-format

Body is missing from request

I'm building a express app, and I'm using express-validator (https://github.com/ctavan/express-validator).
I'm using it as a middleware:
export default function verifyLogin(req, res, next) {
req.checkBody({
'email': {
notEmpty: true,
isEmail: {
errorMessage: 'Invalid Email'
},
errorMessage: "Empty"
},
'password': {
notEmpty: true,
errorMessage: 'Empty',
"isLength": {
options: [{min: 5, max: 20}],
errorMessage: "Password must be between 5 and 20 chars long"
}
}
});
req.getValidationResult().then(result => {
if(!result.isEmpty()) {
res.send(result.array());
console.log('In here, wrong params');
} else {
next();
}
});
}
But if I change res.send(result.array()); to res.status(422).send(result.array()); the body of the request is missing if I log it at the first line after the function (before I use req.CheckBody).
I'm total clueless of the behaviour. Any clues?
Added:
I get a empty body if I send a post request from angular and using res.status().send, but not from postman.
If I use res.send() - both postman and angular works.
The problem was that content-type was not specified in the angular2 app.
For some reason, when using res.status(), the content-type needed to be specified, but not when res.send() was used.

AWS lambda api gateway error "Malformed Lambda proxy response"

I am trying to set up a hello world example with AWS lambda and serving it through api gateway. I clicked the "Create a Lambda Function", which set up the api gatway and selected the Blank Function option. I added the lambda function found on AWS gateway getting started guide:
exports.handler = function(event, context, callback) {
callback(null, {"Hello":"World"}); // SUCCESS with message
};
The issue is that when I make a GET request to it, it's returning back a 502 response { "message": "Internal server error" }. And the logs say "Execution failed due to configuration error: Malformed Lambda proxy response".
Usually, when you see Malformed Lambda proxy response, it means your response from your Lambda function doesn't match the format API Gateway is expecting, like this
{
"isBase64Encoded": true|false,
"statusCode": httpStatusCode,
"headers": { "headerName": "headerValue", ... },
"body": "..."
}
If you are not using Lambda proxy integration, you can login to API Gateway console and uncheck the Lambda proxy integration checkbox.
Also, if you are seeing intermittent Malformed Lambda proxy response, it might mean the request to your Lambda function has been throttled by Lambda, and you need to request a concurrent execution limit increase on the Lambda function.
If lambda is used as a proxy then the response format should be
{
"isBase64Encoded": true|false,
"statusCode": httpStatusCode,
"headers": { "headerName": "headerValue", ... },
"body": "..."
}
Note : The body should be stringified
Yeah so I think this is because you're not actually returning a proper http response there which is why you're getting the error.
personally I use a set of functions like so:
module.exports = {
success: (result) => {
return {
statusCode: 200,
headers: {
"Access-Control-Allow-Origin" : "*", // Required for CORS support to work
"Access-Control-Allow-Credentials" : true // Required for cookies, authorization headers with HTTPS
},
body: JSON.stringify(result),
}
},
internalServerError: (msg) => {
return {
statusCode: 500,
headers: {
"Access-Control-Allow-Origin" : "*", // Required for CORS support to work
"Access-Control-Allow-Credentials" : true // Required for cookies, authorization headers with HTTPS
},
body: JSON.stringify({
statusCode: 500,
error: 'Internal Server Error',
internalError: JSON.stringify(msg),
}),
}
}
} // add more responses here.
Then you simply do:
var responder = require('responder')
// some code
callback(null, responder.success({ message: 'hello world'}))
For Python3:
import json
def lambda_handler(event, context):
return {
'statusCode': 200,
'headers': {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*'
},
'body': json.dumps({
'success': True
}),
"isBase64Encoded": False
}
Note the body isn't required to be set, it can just be empty:
'body': ''
I had this issue, which originated from an invalid handler code which looks completely fine:
exports.handler = (event, context) => {
return {
isBase64Encoded: false,
body: JSON.stringify({ foo: "bar" }),
headers: {
'Access-Control-Allow-Origin': '*',
},
statusCode: 200,
};
}
I got the hint from examining the somewhat confusing API Gateway response logs:
> Endpoint response body before transformations: null
The way to fix it would be to either
Add the async keyword (async function implicitly returns a Promise):
exports.handler = async (event, context) => {
return {
isBase64Encoded: false,
body: JSON.stringify({ foo: "bar" }),
headers: {
'Access-Control-Allow-Origin': '*',
},
statusCode: 200,
};
}
Return a Promise:
exports.handler = (event, context) => {
return new Promise((resolve) => resolve({
isBase64Encoded: false,
body: JSON.stringify({ foo: "bar" }),
headers: {
'Access-Control-Allow-Origin': '*',
},
statusCode: 200,
}));
}
Use the callback:
exports.handler = (event, context, callback) => {
callback({
isBase64Encoded: false,
body: JSON.stringify({ foo: "bar" }),
headers: {
'Access-Control-Allow-Origin': '*',
},
statusCode: 200,
});
}
My handler was previously declared async without ever using await, so I removed the async keyword to reduce complexity of the code, without realizing that Lambda expects either using async/await/Promise or callback return method.
From the AWS docs
In a Lambda function in Node.js, To return a successful response, call
callback(null, {"statusCode": 200, "body": "results"}). To throw an
exception, call callback(new Error('internal server error')). For a
client-side error, e.g., a required parameter is missing, you can call
callback(null, {"statusCode": 400, "body": "Missing parameters of
..."}) to return the error without throwing an exception.
Just a piece of code for .net core and C# :
using Amazon.Lambda.APIGatewayEvents;
...
var response = new APIGatewayProxyResponse
{
StatusCode = (int)HttpStatusCode.OK,
Body = JsonConvert.SerializeObject(new { msg = "Welcome to Belarus! :)" }),
Headers = new Dictionary<string, string> { { "Content-Type", "application/json" } }
};
return response;
Response from lambda will be :
{"statusCode":200,"headers":{"Content-Type":"application/json"},"multiValueHeaders":null,"body":"{\"msg\":\"Welcome to Belarus! :)\"}","isBase64Encoded":false}
Response from api gateway will be :
{"msg":"Welcome to Belarus! :)"}
I've tried all of above suggestion but it doesn't work while body value is not String
return {
statusCode: 200,
headers: {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*"
},
body: JSON.stringify({
success: true
}),
isBase64Encoded: false
};
A very very special case, if you pass the headers directly there is a chance you have this header:
"set-cookie": [ "........" ]
But Amazon needs this:
"set-cookie": "[ \\"........\\" ]"
For anyone else who struggles when the response appears valid. This does not work:
callback(null,JSON.stringify( {
isBase64Encoded: false,
statusCode: 200,
headers: { 'headerName': 'headerValue' },
body: 'hello world'
})
but this does:
callback(null,JSON.stringify( {
'isBase64Encoded': false,
'statusCode': 200,
'headers': { 'headerName': 'headerValue' },
'body': 'hello world'
})
Also, it appears that no extra keys are allowed to be present on the response object.
If you're using Go with https://github.com/aws/aws-lambda-go, you have to use events.APIGatewayProxyResponse.
func hello(ctx context.Context, event ImageEditorEvent) (events.APIGatewayProxyResponse, error) {
return events.APIGatewayProxyResponse{
IsBase64Encoded: false,
StatusCode: 200,
Headers: headers,
Body: body,
}, nil
}
I had this error because I accidentally removed the variable ServerlessExpressLambdaFunctionName from the CloudFormation AWS::Serverless::Api resource. The context here is https://github.com/awslabs/aws-serverless-express "Run serverless applications and REST APIs using your existing Node.js application framework, on top of AWS Lambda and Amazon API Gateway"
Most likely your returning body is in JSON format, but only STRING format is allowed for Lambda proxy integration with API Gateway.
So wrap your old response body with JSON.stringify().
In case the above doesn't work for anyone, I ran into this error despite setting the response variable correctly.
I was making a call to an RDS database in my function. It turned out that what was causing the problem was the security group rules (inbound) on that database.
You'll probably want to restrict the IP addresses that can access the API, but if you want to get it working quick / dirty to test out if that change fixes it you can set it to accept all like so (you can also set the range on the ports to accept all ports too, but I didn't do that in this example):
A common cause of the "Malformed Lambda proxy response" error is headers that are not {String: String, ...} key/values pairs.
Since set-cookie headers can and do appear in multiples, they are represented
in http.request.callback.response as the set-cookie key having an Array of
Strings value instead of a single String. While this works for developers, AWS
API Gateway doesn't understand it and throws a "Malformed Lambda proxy response"
error.
My solution is to do something like this:
function createHeaders(headers) {
const singleValueHeaders = {}
const multiValueHeaders = {}
Object.entries(headers).forEach(([key, value]) => {
const targetHeaders = Array.isArray(value) ? multiValueHeaders : singleValueHeaders
Object.assign(targetHeaders, { [key]: value })
})
return {
headers: singleValueHeaders,
multiValueHeaders,
}
}
var output = {
...{
"statusCode": response.statusCode,
"body": responseString
},
...createHeaders(response.headers)
}
Note that the ... above does not mean Yada Yada Yada. It's the ES6 spread operator.
Here's another approach. Configure the mapping template in your API gateway integration request and response. Go to IntegrationRequest -> MappingTemplate -> select "When there are no templates defined" -> type application/json for content-type. Then you don't have to explicitly send a json. Even the response you get at your client can be a plain string.
The format of your function response is the source of this error. For API Gateway to handle a Lambda function's response, the response must be JSON in this format:
{
"isBase64Encoded": true|false,
"statusCode": httpStatusCode,
"headers": { "headerName": "headerValue", ... },
"body": "..."
}
Here's an example function in Node.js with the response correctly formatted:
exports.handler = (event, context, callback) => {
var responseBody = {
"key3": "value3",
"key2": "value2",
"key1": "value1"
};
var response = {
"statusCode": 200,
"headers": {
"my_header": "my_value"
},
"body": JSON.stringify(responseBody),
"isBase64Encoded": false
};
callback(null, response);
};
Ref: https://aws.amazon.com/premiumsupport/knowledge-center/malformed-502-api-gateway/
Python 3.7
Before
{
"isBase64Encoded": False,
"statusCode": response.status_code,
"headers": {
"Content-Type": "application/json",
},
"body": response.json()
}
After
{
"isBase64Encoded": False,
"statusCode": response.status_code,
"headers": {
"Content-Type": "application/json",
},
"body": str(response.json()) //body must be of string type
}
If you're just new to AWS and just want your URL working,
If you haven't created a trigger for your Lambda Function, navigate to the function in Lambda Functions app and create trigger choosing API Gateway.
Navigate to API Gateway App -> Choose your Particular Lambda's API Gateway (Method execution) -> Click on INTEGRATION Request -> Uncheck "Use Lambda Proxy integration" (check box).
Then click on "<-Method Execution" & click on Test Client section. Provide the options and click test button. You should see a success response.
If you are still unable to get a success response, create an alias for the correct version (if you have multiple versions in the Lambda Function)
Pick the URL from the logs and use your POST/GET Tool (Postman) and choose authentication as AWS Signature - provide your authentication keys(AccessKey & SecretKey) in the postman request with AWS Region & Service Name as lambda.
P.S : This may only help beginners and may be irrelevant to others.

Resources