serverless-plugin-simulate icon indicating copy to clipboard operation
serverless-plugin-simulate copied to clipboard

fix request body handling in lambda-proxy

Open enolan opened this issue 6 years ago • 1 comments

What did you implement:

Prior to this, the API Gateway simulator used various content type middlewares for Express to parse request bodies, and then called JSON.stringify on the parsed bodies before invoking the lambda. AWS just passes the body through unchanged as a string in the event JSON. This commit matches AWS' behavior.

How did you implement it:

Used the text middleware from body-parser to handle every content type and stopped converting bodies to JSON.

How can we verify it:

  • Set up a new project with this serverless.yml:
service: form-urlencoded-test
provider:
  name: aws
  runtime: nodejs6.10
functions:
  hello:
    handler: handler.hello
    events:
      - http:
          method: post
          path: hello
plugins:
  - serverless-plugin-simulate

and this handler.js

'use strict';

module.exports.hello = (event, context, callback) => {
  const response = {
    statusCode: 200,
    body: JSON.stringify(event)
    }

  callback(null, response);

  // Use this code if you don't use the http event with the LAMBDA-PROXY integration
  // callback(null, { message: 'Go Serverless v1.0! Your function executed successfully!', event });
};
  • Install serverless-plugin-simulate
  • sls deploy
  • curl -X POST -d 'Foo=1&Bar=2' -H "Content-Type: application/x-www-form-urlencoded" https://YOUR_DOMAIN_HERE.amazonaws.com/dev/hello
  • You'll see something like this: {"resource":"/hello","path":"/hello","httpMethod":"POST","headers":{"Accept":"*/*","CloudFront-Forwarded-Proto":"https","CloudFront-Is-Desktop-Viewer":"true","CloudFront-Is-Mobile-Viewer":"false","CloudFront-Is-SmartTV-Viewer":"false","CloudFront-Is-Tablet-Viewer":"false","CloudFront-Viewer-Country":"US","Content-Type":"application/x-www-form-urlencoded","Host":"uyfbhveuki.execute-api.us-east-1.amazonaws.com","User-Agent":"curl/7.55.1","Via":"1.1 af602aaeaac93f5e274192569f593f59.cloudfront.net (CloudFront)","X-Amz-Cf-Id":"pS3ZMRFfozf-0BbnMyv9eTbR5-24Y_soHB1qHAYJ--PWHsdQVGrRRA==","X-Amzn-Trace-Id":"Root=1-5a987d62-6101c83f8c3803b09bcc5a33","X-Forwarded-For":"24.20.238.167, 52.46.16.78","X-Forwarded-Port":"443","X-Forwarded-Proto":"https"},"queryStringParameters":null,"pathParameters":null,"stageVariables":null,"requestContext":{"requestTime":"01/Mar/2018:22:23:31 +0000","path":"/dev/hello","accountId":"299718914046","protocol":"HTTP/1.1","resourceId":"uer9jx","stage":"dev","requestTimeEpoch":1519943011000,"requestId":"2bfff394-1d9f-11e8-83b7-f796fce58c28","identity":{"cognitoIdentityPoolId":null,"accountId":null,"cognitoIdentityId":null,"caller":null,"sourceIp":"24.20.238.167","accessKey":null,"cognitoAuthenticationType":null,"cognitoAuthenticationProvider":null,"userArn":null,"userAgent":"curl/7.55.1","user":null},"resourcePath":"/hello","httpMethod":"POST","apiId":"uyfbhveuki"},"body":"Foo=1&Bar=2","isBase64Encoded":false}
  • Note that body in there is the original string we POSTed.
  • sls simulate apigateway
  • same curl command: curl -X POST -d 'Foo=1&Bar=2' -H "Content-Type: application/x-www-form-urlencoded" http://localhost:3000/hello
  • Prior to this commit, you'll get {"path":"/hello","headers":{"host":"localhost:3000","user-agent":"curl/7.55.1","accept":"*/*","content-type":"application/x-www-form-urlencoded","content-length":"11"},"pathParameters":{},"requestContext":{"accountId":"localContext_accountId","resourceId":"localContext_resourceId","stage":"dev","requestId":"localContext_requestId_24394999491022662","identity":{"cognitoIdentityPoolId":"localContext_cognitoIdentityPoolId","accountId":"localContext_accountId","cognitoIdentityId":"localContext_cognitoIdentityId","caller":"localContext_caller","apiKey":"localContext_apiKey","sourceIp":"::ffff:127.0.0.1","cognitoAuthenticationType":"localContext_cognitoAuthenticationType","cognitoAuthenticationProvider":"localContext_cognitoAuthenticationProvider","userArn":"localContext_userArn","userAgent":"curl/7.55.1","user":"localContext_user"}},"resource":"localContext_resource","httpMethod":"POST","queryStringParameters":{},"body":"{\"Foo\":\"1\",\"Bar\":\"2\"}","stageVariables":{}} . After it, body will match Amazon's output.

Todos:

  • [x] Write tests - It'd be nice if there were an easy way to test the Express routing automatically, but I added a test for the lambda-proxy integration at least
  • [x] Write documentation - bugfix
  • [x] Fix linting errors
  • [x] Provide verification config/commands/resources
  • [x] Change ready for review message below

Is this ready for review?: YES

enolan avatar Mar 01 '18 23:03 enolan

Huh, somehow I pushed the wrong code. Should work now.

enolan avatar Mar 02 '18 17:03 enolan