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

Posted

技术标签:

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

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

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

【问题讨论】:

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

    在您的资源方法中,点击方法响应HTTP 状态 表中,单击 添加响应 并添加您想要使用的每个 HTTP 状态代码。 在您的资源方法中,点击集成响应

    为您之前创建的每个 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: .*
    

    您的 API 网关路由应返回:

    HTTP Status Code: 400
    JSON Error Response: 
        
            errorMessage: "Bad Request: You submitted invalid input"
        
    

    我认为无法复制这些设置并将其重新用于不同的方法,因此我们有很多烦人的冗余手动输入要做!

我的集成响应如下所示:

【讨论】:

所以看起来我的问题是正则表达式触发器从未工作过,因为我在 fail 方法中从 lambda 返回了一个错误对象,而不仅仅是一个字符串。例如return context.fail(new Error('bad one')) @kalisjoshua 我最近发表了一篇关于 API Gateway/Lambda 错误处理的相当详细的帖子:jayway.com/2015/11/07/… Python Lambda 的 context.fail 等价物是什么? 对于 python:引发异常。见docs.aws.amazon.com/lambda/latest/dg/python-exceptions.html 有没有办法改变非错误响应中的状态码?如果我想连同创建的对象一起发送“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 详细信息,包括 statusCodebodyheadersbody 应该是一个字符串,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 状态正则表达式中,它就可以正常工作。确保您的应用程序正确处理错误。

【讨论】:

以上是关于有没有办法更改 Amazon API Gateway 返回的 http 状态代码?的主要内容,如果未能解决你的问题,请参考以下文章

Amazon Web服务Route 53忽略域名

github下载速度太慢解决办法

Amazon RDS:将快照还原到现有实例

有没有办法 touch() Amazon S3 中的文件?

有没有办法将多个静态 IP 分配给 Amazon Lightsail 实例?

在没有 lxml 的 Google Appengine 上使用 python-amazon-product-api [重复]