有没有办法更改Amazon API Gateway返回的http状态代码?


95

例如,当lambda函数调用导致创建时,如果我想为无效参数返回特定的400错误,或者可能返回201。

我想使用不同的http状态代码,但是即使lambda函数返回错误,api网关也总是返回200状态代码。


2
所以看起来我遇到的问题是我正在返回自定义错误类型-这使errorMessage正则表达式无法正常工作。从lambda的失败响应中返回标准字符串将使以下解决方案起作用-但是,返回自己的自定义错误对象将不会。
MonkeyBonkey

我的解决方案是从Serveless版本0.5切换到1.0。另外,我使用来自Serveless文档的响应,在响应对象中将statusCode指定为属性。希望对您
有所

Answers:


79

每20-9-2016更新

亚马逊最终使用Lambda Proxy集成简化了这一过程。这使您的Lambda函数可以返回正确的HTTP代码和标头:

let response = {
    statusCode: '400',
    body: JSON.stringify({ error: 'you messed up!' }),
    headers: {
        'Content-Type': 'application/json',
    }
};

context.succeed(response);

在API网关中说再见请求/响应映射!

选项2

使用aws-serverless-express将现有的Express应用程序与Lambda / API网关集成。


我无法集成它,我的意思是,我得到200状态和创建的响应(创建的错误)。我想念什么吗?“ s-function.json”的外观如何?
Relu Mesaros

对于最简单的示例,请查看AWS自己的Lambda蓝图,该蓝图称为microservice-http-endpoint(在AWS Lambda控制台中)。您提到“ s-function.json”,这听起来像您正在使用Serverless框架(serverless.com)。这完全是另一种野兽,不应与aws-serverless-express或“原始” Lambda / API网关相混淆。我的答案没有描述如何使用无服务器框架来完成这项工作。
Eric Eijkelenboom 2016年

7
对于任何想知道的人,也可以使用新callback样式来实现。做吧callback(null, {statusCode: 200, body: 'whatever'})
Widdershin

1
@Sushil是的,您只需像上面的响应变量一样返回JSON。
取消挑战'17

8
@Sushil我已经使用LambdaProxyIntegration在Python中解决了这个问题,并返回了它return { "isBase64Encoded": True, "statusCode": 200, "headers": { }, "body": "" }
Jithu R Jacob

74

这是返回自定义HTTP状态代码和自定义的最快方法errorMessage

在API Gateway仪表板中,执行以下操作:

  1. 在您资源方法中,单击方法响应
  2. 在“ HTTP状态”表中,单击“ 添加响应”,然后添加要使用的每个HTTP状态代码。
  3. 资源方法中,单击集成响应
  4. 为您先前创建的每个HTTP状态代码添加一个集成响应。确保检查输入直通。使用lambda错误正则表达式标识从lambda函数返回错误消息时应使用哪个状态代码。例如:

    // Return An Error Message String In Your Lambda Function
    
    return context.fail('Bad Request: You submitted invalid input');
    
    // Here is what a Lambda Error Regex should look like.
    // Be sure to include the period and the asterisk so any text
    // after your regex is mapped to that specific HTTP Status Code
    
    Bad Request: .*
    
  5. 您的API网关路线应返回以下内容:

    HTTP Status Code: 400
    JSON Error Response: 
        {
            errorMessage: "Bad Request: You submitted invalid input"
        }
    
  6. 我看不到可以复制这些设置并将其重新用于其他方法的方法,因此我们有很多烦人的多余手动输入要做!

我的集成回复如下所示:

AWS API Gateway Lambda错误响应处理


3
所以看来我的问题是,正则表达式触发器永远无法正常工作,因为我在fail方法中从lambda返回了一个错误对象,而不仅仅是一个字符串。例如return context.fail(new Error('bad one'))
MonkeyBonkey

7
@kalisjoshua我最近在API Gateway / Lambda上发表了有关错误处理的相当详细的文章:jayway.com/2015/11/07/…–
Carl

9
Python Lambda的context.fail等于什么?
routeburn


1
有没有办法在非错误响应中更改状态代码?如果我想将“ 201 Created”与创建的对象一起发送怎么办?
本戴维斯

18

为了能够将自定义错误对象作为JSON返回,您必须跳过几个步骤。

首先,您必须使Lambda失败,并将其传递给字符串化的JSON对象:

exports.handler = function(event, context) {
    var response = {
        status: 400,
        errors: [
            {
              code:   "123",
              source: "/data/attributes/first-name",
              message:  "Value is too short",
              detail: "First name must contain at least three characters."
            },
            {
              code:   "225",
              source: "/data/attributes/password",
              message: "Passwords must contain a letter, number, and punctuation character.",
              detail: "The password provided is missing a punctuation character."
            },
            {
              code:   "226",
              source: "/data/attributes/password",
              message: "Password and password confirmation do not match."
            }
        ]
    }

    context.fail(JSON.stringify(response));
};

接下来,为您要返回的每个状态代码设置正则表达式映射。使用上面定义的对象,您可以将此正则表达式设置为400:

。*“状态”:400. *

最后,您设置一个映射模板,以从Lambda返回的errorMessage属性中提取JSON响应。映射模板如下所示:

$ input.path('$。errorMessage')

我为此撰写了一篇文章,其中有更详细的说明,并在此处解释了从Lambda到API网关的响应流:http : //kennbrodhagen.net/2016/03/09/how-to-re-return-a-custom-error-object和带有lambda的API网关的状态代码/


@kennbrodhagen您知道API网关和Java Lambdas吗?我正在使用一种相同的reg exp,它对我不起作用。我使用。* statusCode“:422. *
Perimosh '17

@Perimosh查看这篇文章,该文章解释了如何使用Java异常来做到这一点: aws.amazon.com/blogs/compute/…–
kennbrodhagen

10

1)通过选中API网关资源定义的“集成请求”屏幕上标有“使用Lambda代理集成”的复选框,将API网关资源配置为使用Lambda代理集成。(或在您的cloudformation / terraform / serverless / etc配置中定义它)

2)通过2种方式更改您的lambda代码

  • event适当处理传入的(第一个函数参数)。它不再只是裸负载,它代表了整个HTTP请求,包括标头,查询字符串和正文。下面的示例。关键是JSON正文将是需要显式JSON.parse(event.body)调用的字符串(不要忘记try/catch了)。示例如下。
  • 通过调用回调与空值,则提供该HTTP的细节,包括一个响应对象响应statusCodebodyheaders
    • body应该是一个字符串,所以JSON.stringify(payload)需要做
    • statusCode 可以是一个数字
    • headers 是标题名称到值的对象

用于代理集成的示例Lambda事件参数

{
    "resource": "/example-path",
    "path": "/example-path",
    "httpMethod": "POST",
    "headers": {
        "Accept": "*/*",
        "Accept-Encoding": "gzip, deflate",
        "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/json",
        "Host": "exampleapiid.execute-api.us-west-2.amazonaws.com",
        "User-Agent": "insomnia/4.0.12",
        "Via": "1.1 9438b4fa578cbce283b48cf092373802.cloudfront.net (CloudFront)",
        "X-Amz-Cf-Id": "oCflC0BzaPQpTF9qVddpN_-v0X57Dnu6oXTbzObgV-uU-PKP5egkFQ==",
        "X-Forwarded-For": "73.217.16.234, 216.137.42.129",
        "X-Forwarded-Port": "443",
        "X-Forwarded-Proto": "https"
    },
    "queryStringParameters": {
        "bar": "BarValue",
        "foo": "FooValue"
    },
    "pathParameters": null,
    "stageVariables": null,
    "requestContext": {
        "accountId": "666",
        "resourceId": "xyz",
        "stage": "dev",
        "requestId": "5944789f-ce00-11e6-b2a2-dfdbdba4a4ee",
        "identity": {
            "cognitoIdentityPoolId": null,
            "accountId": null,
            "cognitoIdentityId": null,
            "caller": null,
            "apiKey": null,
            "sourceIp": "73.217.16.234",
            "accessKey": null,
            "cognitoAuthenticationType": null,
            "cognitoAuthenticationProvider": null,
            "userArn": null,
            "userAgent": "insomnia/4.0.12",
            "user": null
        },
        "resourcePath": "/example-path",
        "httpMethod": "POST",
        "apiId": "exampleapiid"
    },
    "body": "{\n  \"foo\": \"FOO\",\n  \"bar\": \"BAR\",\n  \"baz\": \"BAZ\"\n}\n",
    "isBase64Encoded": false
}

样本回调响应形状

callback(null, {
  statusCode: 409,
  body: JSON.stringify(bodyObject),
  headers: {
    'Content-Type': 'application/json'
  }
})

注释 -我相信,在方法上context,如context.succeed()已被弃用。尽管它们似乎仍然有效,但不再记录它们。我认为编写回调API是正确的做法。


这是行不通的。整个响应输出仍然返回200状态。无法将api设置为实际返回409状态
Andy N

7

经过大量研究后,我希望Lambda给出的错误是正确的500错误,提出了以下可行的方法:

在LAMBDA上

为了获得良好的响应,我将返回以下内容:

exports.handler = (event, context, callback) => {
    // ..

    var someData1 =  {
        data: {
            httpStatusCode: 200,
            details: [
                {
                    prodId: "123",
                    prodName: "Product 1"
                },
                {
                    "more": "213",
                    "moreDetails": "Product 2"
                }
            ]
        }
    };
    return callback(null, someData1);
}

如果响应不好,则返回以下内容

exports.handler = (event, context, callback) => {
    // ..

    var someError1 = {
        error: {
            httpStatusCode: 500,
            details: [
                {
                    code: "ProductNotFound",
                    message: "Product not found in Cart",
                    description: "Product should be present after checkout, but not found in Cart",
                    source: "/data/attributes/product"
                },
                {
                    code: "PasswordConfirmPasswordDoesntMatch",
                    message: "Password and password confirmation do not match.",
                    description: "Password and password confirmation must match for registration to succeed.",
                    source: "/data/attributes/password",
                }
            ]
        }
    };

    return callback(new Error(JSON.stringify(someError1)));
}

在API网关上

对于GET方法,请说/ res1 / service1的GET:

Through Method Response > Add Response, added 3 responses:
- 200
- 300
- 400

然后,

Through 'Integration Response' > 'Add integration response', create a Regex for 400 errors (client error):

Lambda Error Regex    .*"httpStatusCode":.*4.*

'Body Mapping Templates' > Add mapping template as:  
    Content-Type                 application/json  
    Template text box*           $input.path('$.errorMessage')  


Similarly, create a Regex for 500 errors (server error):

Lambda Error Regex    .*"httpStatusCode":.*5.*

'Body Mapping Templates' > Add mapping template as:  
    Content-Type                 application/json  
    Template text box*           $input.path('$.errorMessage')  

现在,发布/ res1 / service1,点击发布的URL,该URL连接到上述lambda

使用高级REST客户端(或Postman)chrome插件,您会看到正确的http代码,例如服务器错误(500)或400,而不是“ httpStatusCode”中给出的所有请求的200 http响应代码。

在API的“控制台”的API网关中,我们可以看到如下的http状态代码:

400和500错误


7

最简单的方法是使用LAMBDA_PROXY集成。使用此方法,无需在API网关管道中设置任何特殊的转换。

您的返回对象必须与以下代码段相似:

module.exports.lambdaHandler = (event, context, done) => {
    // ...
    let response = {
        statusCode: 200, // or any other HTTP code
        headers: {       // optional
             "any-http-header" : "my custom header value"
        },
        body: JSON.stringify(payload) // data returned by the API Gateway endpoint
    };
    done(null, response); // always return as a success
};

它确实有一些缺点:必须特别小心错误处理,并将lambda函数耦合到API Gateway端点;就是说,如果您真的不打算在其他任何地方使用它,那不是什么大问题。


6

对于那些尝试过此问题的所有内容而无法完成此工作的人(像我一样),请查看此帖子上的thedevkit评论(保存了我的一天):

https://forums.aws.amazon.com/thread.jspa?threadID=192918

完全在下面复制:

我自己对此有疑问,我相信换行符是罪魁祸首。

foo。*将匹配出现的“ foo”,后跟除换行符以外的任何字符。通常,这可以通过添加“ / s”标志(即“ foo。* / s”)来解决,但是Lambda错误正则表达式似乎并不遵守这一点。

或者,您可以使用类似以下内容的命令:foo(。| \ n)*


惊人的发现!它节省了我几个小时的敲打头!其远非显而易见。
MirkoVukušić'17

Mirko,很高兴它对您有所帮助!
卡洛斯·巴洛克

2

如果使用API​​ Gateway,这是在AWS Compute Blog上推荐的方式。检查集成是否适用于直接Lambda调用。

var myErrorObj = {
    errorType : "InternalServerError",
    httpStatus : 500,
    requestId : context.awsRequestId,
    message : "An unknown error has occurred. Please try again."
}
callback(JSON.stringify(myErrorObj));

对于直接Lambda调用,这似乎是客户端解析的最佳解决方案。


如果示例是lambda到lambda调用该怎么办。这仍然是所谓的lambda会返回的内容吗?以及如何在调用lambda上读取该httpStatus。
Rod

1

我正在使用无服务器0.5。就我而言,这就是它的工作方式

s-function.json:

{
  "name": "temp-err-test",
  "description": "Deployed",
  "runtime": "nodejs4.3",
  "handler": "path/to/handler.handler",
  "timeout": 6,
  "memorySize": 1024,
  "endpoints": [
    {
      "path": "test-error-handling",
      "method": "GET",
      "type": "AWS_PROXY",
      "responses": {
        "default": {
          "statusCode": "200"
        }
      }
    }
  ]
}

handler.js:

'use strict';
function serveRequest(event, context, cb) {
  let response = {
    statusCode: '400',
    body: JSON.stringify({ event, context }),
    headers: {
      'Content-Type': 'application/json',
    }
  };
  cb(null, response);
}
module.exports.handler = serveRequest;

1

如果您不想使用代理,则可以使用以下模板:

#set($context.responseOverride.status =  $input.path('$.statusCode'))
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.