【问题标题】:Is there a way to change the http status codes returned by Amazon API Gateway?有没有办法更改 Amazon API Gateway 返回的 http 状态代码?
【发布时间】:2015-09-28 12:53:52
【问题描述】:

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

我想要不同的 http 状态码,但看起来 api 网关总是返回 200 状态码,即使 lambda 函数返回错误。

【问题讨论】:

  • 所以看起来我遇到的问题是我返回了一个自定义错误类型 - 这使得 errorMessage 正则表达式无法正常工作。在 lambda 的失败响应中返回标准字符串将使以下解决方案起作用 - 但是,返回您自己的自定义错误对象不会。
  • 我的解决方案是从 Serveless 版本 0.5 切换到 1.0。此外,我正在使用来自无服务文档的响应,将响应对象中的 statusCode 指定为属性。希望对你有帮助

标签: amazon-web-services aws-lambda aws-api-gateway


【解决方案1】:

2016 年 9 月 20 日更新

亚马逊终于使用Lambda Proxy integration 让这一切变得简单。这允许您的 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”长什么样子?
  • 对于任何想知道的人,这也可以使用新的callback 样式来实现。只需callback(null, {statusCode: 200, body: 'whatever'})
  • @unclemeat 我也有同样的问题。你想清楚了吗?如何在 python 中做到这一点?
  • @Sushil 是的,您只需像上面的响应变量一样返回 JSON。
  • @Sushil 我已经在 Python 中使用 LambdaProxyIntegration 解决了这个问题并返回 return { "isBase64Encoded": True, "statusCode": 200, "headers": { }, "body": "" }
【解决方案2】:

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

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

  1. 在您的资源方法中,点击方法响应
  2. HTTP 状态 表中,单击 添加响应 并添加您想要使用的每个 HTTP 状态代码。
  3. 在您的资源方法中,点击集成响应
  4. 为您之前创建的每个 HTTP 状态代码添加一个集成响应。确保 input passthrough 被选中。当您从 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. 我认为无法复制这些设置并将其重新用于不同的方法,因此我们有很多烦人的冗余手动输入要做!

我的集成响应如下所示:

【讨论】:

  • 所以看起来我的问题是正则表达式触发器从未工作过,因为我在 fail 方法中从 lambda 返回了一个错误对象,而不仅仅是一个字符串。例如return context.fail(new Error('bad one'))
  • @kalisjoshua 我最近发表了一篇关于 API Gateway/Lambda 错误处理的相当详细的帖子:jayway.com/2015/11/07/…
  • Python Lambda 的 context.fail 等价物是什么?
  • 有没有办法改变非错误响应中的状态码?如果我想连同创建的对象一起发送“201 Created”怎么办?
【解决方案3】:

为了能够以 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:

.*"status":400.*

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

$input.path('$.errorMessage')

我写了一篇关于此的文章,更详细地解释了从 Lambda 到 API Gateway 的响应流: http://kennbrodhagen.net/2016/03/09/how-to-return-a-custom-error-object-and-status-code-from-api-gateway-with-lambda/

【讨论】:

  • @kennbrodhagen 你知道 API 网关和 Java Lambda 吗?我正在使用一种相同的reg exp,它对我不起作用。我使用 .*statusCode":422.*
  • @Perimosh 查看这篇解释如何使用 Java 异常执行此操作的文章:aws.amazon.com/blogs/compute/…
【解决方案4】:

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

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

  • 适当处理传入的event(第一个函数参数)。它不再只是裸露的负载,它代表了整个 HTTP 请求,包括标头、查询字符串和正文。示例如下。关键是 JSON 主体将是需要显式调用 JSON.parse(event.body) 的字符串(不要忘记 try/catch )。示例如下。
  • 通过使用 null 调用回调,然后是一个响应对象来响应,该对象提供 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 状态
  • 使用此设置的 4xx 和 5xx 的问题通常与 CORS 有关。 This is the way to fix it.
【解决方案5】:

我希望 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,连接到上面的 lambda

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

从 API 的“仪表板”中,在 API Gateway 中,我们可以看到如下 http 状态码:

【讨论】:

    【解决方案6】:

    最简单的方法是use LAMBDA_PROXY integration。使用此方法,您无需在 API Gateway 管道中设置任何特殊转换。

    您的返回对象必须类似于下面的 sn-p:

    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 端点;也就是说,如果您真的不打算在其他任何地方使用它,那也不是什么大问题。

    【讨论】:

      【解决方案7】:

      对于那些在这个问题上尝试了所有方法但无法完成这项工作的人(比如我),请查看这篇文章的 thedevkit 评论(节省了我的一天):

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

      在下面完全复制它:

      我自己也遇到过这个问题,我相信换行符 人物是罪魁祸首。

      foo.* 将匹配出现的 "foo" 后跟任何字符 除了换行符。通常这是通过添加'/s'标志来解决的,即 “foo.*/s”,但 Lambda 错误正则表达式似乎不尊重这一点。

      您可以使用类似的替代方法: foo(.|\n)*

      【讨论】:

      • 惊人的发现!它只是为我节省了几个小时的敲头时间!它远非显而易见。
      • Mirko,很高兴它对你有所帮助!
      【解决方案8】:

      如果使用 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。
      【解决方案9】:

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

      #set($context.responseOverride.status =  $input.path('$.statusCode'))
      

      【讨论】:

        【解决方案10】:

        我正在使用无服务器 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;
        

        【讨论】:

          【解决方案11】:

          有效期为 2021 年 2 月

          设置自定义 HTTP 状态代码的最简单方法是在 API Gateway 中设置 Lambda 代理集成。

          在 API Gateway > Resource > Actions Dropdown > Create Method > 勾选 Lambda Proxy Integration 并选择适当的 Lambda 函数。

          API 网关

          Lambda

          对于异步函数,只需返回带有statusCodebody 的对象。对于同步功能使用callback(null,obj);参考full documentation

          export const dummyFunction = async (event, context, callback) => 
          {
           // ... logic
             return {
             statusCode: 400,
             body: JSON.stringify({...data}),
             }
          };
          
          

          结果

          自定义状态码 400。

          【讨论】:

            【解决方案12】:

            我有一个快速应用程序,并在它前面使用了带有 http 集成的 api 网关。要从我的应用程序返回状态代码而不是 200 OK ,我只需将应用程序的错误处理程序返回的 http 状态代码添加到集成响应部分的 http 状态正则表达式中,它就可以正常工作。确保您的应用程序正确处理错误。

            【讨论】:

              猜你喜欢
              • 2017-01-28
              • 1970-01-01
              • 2016-01-27
              • 2019-07-15
              • 2014-02-08
              • 2017-06-27
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多