Amazon web services 有没有办法更改AmazonAPI网关返回的http状态码?

Amazon web services 有没有办法更改AmazonAPI网关返回的http状态码?,amazon-web-services,aws-lambda,aws-api-gateway,Amazon Web Services,Aws Lambda,Aws Api Gateway,例如,如果我想为无效参数返回一个特定的400错误,或者当lambda函数调用导致创建错误时返回一个201错误 我希望有不同的http状态代码,但api网关似乎总是返回200状态代码,即使lambda函数返回错误。以下是返回自定义http状态代码和自定义错误消息的最快方法: 在API网关仪表板中,执行以下操作: 在资源的方法中,单击方法响应 在HTTP状态表中,单击AddResponse,然后添加要使用的每个HTTP状态代码 在资源的方法中,单击集成响应 为前面创建的每个HTTP状态代码添加集成响

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


我希望有不同的http状态代码,但api网关似乎总是返回200状态代码,即使lambda函数返回错误。

以下是返回自定义http状态代码和自定义
错误消息的最快方法:

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

  • 在资源的方法中,单击方法响应
  • 在HTTP状态表中,单击AddResponse,然后添加要使用的每个HTTP状态代码
  • 在资源的方法中,单击集成响应
  • 为前面创建的每个HTTP状态代码添加集成响应。确保已选中输入传递。使用lambda error regex确定从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"
        }
    
  • 我认为没有办法复制这些设置并将其用于不同的方法,所以我们有很多烦人的冗余手动输入要做

  • 我的集成响应如下所示:


    对于那些尝试了所有方法解决这个问题但无法解决这个问题的人(像我一样),请查看这篇帖子上的thedevkit评论(拯救了我的一天):

    完全复制如下:

    我自己也有过一些问题,我相信新线 人物是罪魁祸首

    foo.*将匹配后跟任何字符的“foo” 除了新线。通常通过添加“/s”标志来解决此问题,即。 “foo.*/s”,但Lambda错误regex似乎不尊重这一点

    作为替代方案,您可以使用以下内容:foo(.|\n)*


    为了能够将自定义错误对象作为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网关的响应流程:

    根据2016年9月20日更新

    Amazon最终通过使用。这允许Lambda函数返回正确的HTTP代码和标头:

    let response = {
        statusCode: '400',
        body: JSON.stringify({ error: 'you messed up!' }),
        headers: {
            'Content-Type': 'application/json',
        }
    };
    
    context.succeed(response);
    
    告别API网关中的请求/响应映射

    选项2


    使用将现有Express应用程序与Lambda/API网关集成。

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

    如果使用API网关,AWS Compute博客上建议这样做。检查集成是否与直接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的错误是正确的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方法,可以说GET of/res1/service1:

    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')  
    
    现在,publish/res1/service1,点击已发布的URL,该URL连接到上面的lambda

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

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

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

    2)用两种方法更改lambda代码

    • 适当地处理传入的
      事件
      (第一个函数参数)。它不再只是简单的负载,它代表整个HTTP请求,包括头、查询字符串和正文。下面是示例。关键点是JSON主体将是需要显式
      JSON.parse(event.body)
      call的字符串(不要忘记
      try/catch
      )。下面是一个例子
    • 通过使用null调用回调进行响应,然后调用一个响应对象,该对象提供HTTP详细信息,包括
      statusCode
      body
      、和
      • body
        应该是一个字符串,也可以根据需要使用
        JSON.stringify(有效负载)
      • statusCode
        可以是数字
      • 标题
        是标题名称到值的对象
    代理集成的Lambda事件参数示例 示例回调响应形状
    注释
    -我相信
    context
    上的方法,例如
    context.succeed()
    已经被弃用了。它们不再被记录,尽管它们似乎仍然有效。我认为编写回调API是正确的做法。

    最简单的方法是。使用这种方法,您不需要在API网关管道中设置任何特殊的转换

    您的返回对象必须类似于下面的代码段:

    module.exports.lambdaHandler=(事件、上下文、完成)=>{
    // ...
    让r
    
    {
        "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'
      }
    })
    
    #set($context.responseOverride.status =  $input.path('$.statusCode'))
    
    export const dummyFunction = async (event, context, callback) => 
    {
     // ... logic
       return {
       statusCode: 400,
       body: JSON.stringify({...data}),
       }
    };