Có cách nào để thay đổi mã trạng thái http do Amazon API Gateway trả về không?


95

Ví dụ: nếu tôi muốn trả về lỗi 400 cụ thể cho các tham số không hợp lệ hoặc có lẽ là 201 khi lệnh gọi hàm lambda dẫn đến tạo.

Tôi muốn có các mã trạng thái http khác nhau nhưng có vẻ như api gateway luôn trả về mã trạng thái 200 ngay cả khi hàm lambda trả về lỗi.


2
vì vậy, có vẻ như vấn đề tôi gặp phải là tôi đã trả về một loại lỗi tùy chỉnh - điều này khiến regex errorMessage không hoạt động chính xác. Trả lại một chuỗi tiêu chuẩn trong phản hồi thất bại từ lambda sẽ làm cho giải pháp bên dưới hoạt động - tuy nhiên, trả về đối tượng lỗi tùy chỉnh của riêng bạn thì không.
MonkeyBonkey

giải pháp của tôi là chuyển từ phiên bản Serveless 0.5 sang 1.0. Ngoài ra, tôi đang sử dụng phản hồi từ tài liệu Serveless, chỉ định Mã trạng thái trong đối tượng phản hồi như một thuộc tính. Hy vọng nó giúp
Relu Mesaros

Câu trả lời:


79

Cập nhật ngày 20-9-2016

Amazon cuối cùng đã thực hiện điều này dễ dàng bằng cách sử dụng tích hợp Lambda Proxy . Điều này cho phép hàm Lambda của bạn trả về các mã và tiêu đề HTTP thích hợp:

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

context.succeed(response);

Tạm biệt ánh xạ yêu cầu / phản hồi trong API Gateway!

Lựa chọn 2

Tích hợp một ứng dụng Express hiện có với Lambda / API Gateway bằng aws-serverless-express .


Tôi không thể tích hợp nó, ý tôi là, tôi nhận được trạng thái 200 và phản hồi đã tạo (lỗi đã tạo). Tui bỏ lỡ điều gì vậy ? "S-function.json" trông như thế nào?
Relu Mesaros

Để có ví dụ đơn giản nhất, hãy xem bản thiết kế Lambda của chính AWS được gọi là microservice-http-endpoint (trong bảng điều khiển AWS Lambda). Bạn đề cập đến "s-function.json", có vẻ như bạn đang sử dụng Serverless framework ( serverless.com ). Đây hoàn toàn là một con thú khác và không nên nhầm lẫn với aws-serverless-express hoặc Lambda / API Gateway 'thô'. Câu trả lời của tôi không mô tả cách thực hiện việc này bằng cách sử dụng khuôn khổ Serverless.
Eric Eijkelenboom,

7
Đối với bất kỳ ai thắc mắc, điều này cũng có thể đạt được bằng cách sử dụng callbackphong cách mới . Chỉ cần làm callback(null, {statusCode: 200, body: 'whatever'}).
Widdershin

1
@Sushil vâng, bạn chỉ cần trả về JSON giống như trong biến phản hồi ở trên.
ô uế

8
@Sushil Tôi đã giải quyết điều này bằng Python với LambdaProxyIntegration và trở vềreturn { "isBase64Encoded": True, "statusCode": 200, "headers": { }, "body": "" }
Jithu R Jacob

74

Đây là cách nhanh nhất để trả về Mã trạng thái HTTP tùy chỉnh và tùy chỉnh errorMessage:

Trong bảng điều khiển API Gateway, hãy làm như sau:

  1. Trong phương thức cho tài nguyên của bạn , hãy nhấp vào phản hồi phương thức
  2. Trong bảng Trạng thái HTTP , hãy nhấp vào thêm phản hồi và thêm vào từng Mã trạng thái HTTP mà bạn muốn sử dụng.
  3. Trong phương pháp cho tài nguyên của bạn , hãy nhấp vào phản hồi tích hợp
  4. Thêm phản hồi tích hợp cho từng Mã trạng thái HTTP bạn đã tạo trước đó. Đảm bảo rằng thông tin đầu vào được kiểm tra. Sử dụng lambda error regex để xác định mã trạng thái nào nên được sử dụng khi bạn trả về thông báo lỗi từ hàm lambda của mình. Ví dụ:

    // 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. Tuyến API Gateway của bạn sẽ trả về:

    HTTP Status Code: 400
    JSON Error Response: 
        {
            errorMessage: "Bad Request: You submitted invalid input"
        }
    
  6. Tôi thấy không có cách nào để sao chép các cài đặt này và sử dụng lại nó cho các phương pháp khác nhau, vì vậy chúng tôi có nhiều thao tác nhập thủ công thừa gây phiền nhiễu phải làm!

Các phản hồi tích hợp của tôi trông giống như sau:

xử lý lỗi phản hồi lỗi cổng kết nối aws api lambda


3
vì vậy, có vẻ như vấn đề của tôi là trình kích hoạt regex không bao giờ hoạt động vì tôi trả về một đối tượng lỗi từ lambda trong phương thức fail, thay vì chỉ là một chuỗi. ví dụreturn context.fail(new Error('bad one'))
MonkeyBonkey

7
@kalisjoshua Gần đây, tôi đã xuất bản một bài đăng khá chi tiết về việc xử lý lỗi với API Gateway / Lambda: jayway.com/2015/11/07/…
Carl

9
Tương đương với context.fail cho Python Lambda's là gì?
routeburn

1
Đối với python: nâng một Ngoại lệ. Xem docs.aws.amazon.com/lambda/latest/dg/python-exceptions.html
devxoul

1
Không có cách nào để thay đổi mã trạng thái trong các phản hồi không lỗi? Điều gì sẽ xảy ra nếu tôi muốn gửi "201 Đã tạo" cùng với đối tượng đã tạo?
Ben Davis

18

Để có thể trả về một đối tượng lỗi tùy chỉnh dưới dạng JSON, bạn phải thực hiện một vài bước.

Đầu tiên, bạn phải làm hỏng Lambda và chuyển cho nó một đối tượng JSON được xâu chuỗi:

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));
};

Tiếp theo, bạn thiết lập ánh xạ regex cho từng mã trạng thái mà bạn muốn trả về. Sử dụng đối tượng tôi đã xác định ở trên, bạn sẽ thiết lập regex này cho 400:

. * "trạng thái": 400. *

Cuối cùng, bạn thiết lập Mẫu ánh xạ để trích xuất phản hồi JSON từ thuộc tính errorMessage do Lambda trả về. Mẫu ánh xạ trông giống như sau:

$ input.path ('$. errorMessage')

Tôi đã viết một bài báo về vấn đề này chi tiết hơn và giải thích luồng phản hồi từ Lambda đến API Gateway tại đây: http://kennbrodhagen.net/2016/03/09/how-to-return-a-custom-error-object -and-status-code-from-api-gateway-with-lambda /


@kennbrodhagen bạn có biết về API Gateway và Java Lambdas không? Tôi đang sử dụng một loại reg exp giống nhau và nó không hoạt động với tôi. Tôi sử dụng. * StatusCode ": 422. *
Perimosh

@Perimosh kiểm tra bài viết này giải thích làm thế nào để làm điều này với Exceptions Java: aws.amazon.com/blogs/compute/...
kennbrodhagen

10

1) Định cấu hình tài nguyên API Gateway của bạn để sử dụng Lambda Proxy Integration bằng cách chọn hộp kiểm có nhãn "Sử dụng Lambda Proxy tích hợp" trên màn hình "Integration Request" của định nghĩa tài nguyên API Gateway. (Hoặc xác định nó trong cấu hình cloudformation / terraform / serverless / etc của bạn)

2) Thay đổi mã lambda của bạn theo 2 cách

  • Xử lý đối số đến event(đối số chức năng thứ nhất) một cách thích hợp. Nó không còn chỉ là tải trọng trống, nó đại diện cho toàn bộ yêu cầu HTTP bao gồm tiêu đề, chuỗi truy vấn và nội dung. Mẫu bên dưới. Điểm mấu chốt là các phần thân JSON sẽ là các chuỗi yêu cầu JSON.parse(event.body)lệnh gọi rõ ràng (đừng quên try/catchđiều đó). Ví dụ dưới đây.
  • Phản ứng bằng cách gọi callback với null thì một đối tượng phản ứng cung cấp các chi tiết HTTP bao gồm statusCode, bodyheaders.
    • bodynên là một chuỗi, vì vậy hãy làm JSON.stringify(payload)khi cần thiết
    • statusCode có thể là một số
    • headers là một đối tượng của tên tiêu đề cho các giá trị

Đối số sự kiện Lambda mẫu để tích hợp proxy

{
    "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
}

Hình dạng phản hồi cuộc gọi lại mẫu

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

Lưu ý - Tôi tin rằng các phương pháp trên contextnhư context.succeed()không được dùng nữa. Chúng không còn được ghi lại mặc dù chúng dường như vẫn hoạt động. Tôi nghĩ rằng mã hóa cho API gọi lại là điều chính xác trong tương lai.


Điều này không hoạt động. Tôi vẫn nhận được 200 trạng thái được trả về với toàn bộ đầu ra phản hồi này. Không thể đặt api để thực sự trả lại trạng thái 409
Andy N

7

Tôi muốn một lỗi từ Lambda là lỗi 500 thích hợp, sau khi thực hiện rất nhiều nghiên cứu, đã đưa ra kết quả bên dưới, nó hoạt động:

Trên LAMBDA

Để có phản hồi tốt, tôi sẽ quay lại như sau:

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);
}

Đối với phản hồi không tốt, hãy trả lại như bên dưới

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)));
}

Trên API Gateway

Đối với phương pháp GET, hãy nói GET / res1 / service1:

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

Sau đó,

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')  

Bây giờ, xuất bản / res1 / dịch vụ1, nhấn vào URL đã xuất bản, được kết nối với lambda ở trên

Đã sử dụng plugin chrome client Advanced REST (hoặc Postman), bạn sẽ thấy mã http thích hợp như lỗi máy chủ (500) hoặc 400, thay vì 200 mã phản hồi http cho tất cả các yêu cầu được đưa ra trong "httpStatusCode".

Từ 'Trang tổng quan' của API, trong API Gateway, chúng ta có thể thấy các mã trạng thái http như bên dưới:

400 & 500 lỗi


7

Cách dễ nhất để làm điều này là sử dụng tích hợp LAMBDA_PROXY . Sử dụng phương pháp này, bạn không cần bất kỳ biến đổi đặc biệt nào để được đặt thành đường ống API Gateway.

Đối tượng trả lại của bạn sẽ phải tương tự như đoạn mã dưới đây:

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
};

Nó có một số nhược điểm: như phải đặc biệt cẩn thận trong việc xử lý lỗi và ghép hàm lambda của bạn với điểm cuối API Gateway; điều đó nói rằng, nếu bạn không thực sự định sử dụng nó ở bất kỳ nơi nào khác, thì đó không phải là vấn đề quá lớn.


6

Đối với những người đã thử mọi cách đặt câu hỏi này và không thể làm cho nó thành công (như tôi), hãy kiểm tra nhận xét của thedevkit trên bài đăng này (đã lưu ngày của tôi):

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

Sao lại toàn bộ bên dưới:

Bản thân tôi cũng gặp vấn đề với điều này và tôi tin rằng các ký tự dòng mới là thủ phạm.

foo. * sẽ khớp với các lần xuất hiện của "foo" theo sau bởi bất kỳ ký tự nào NGOẠI TRỪ dòng mới. Thông thường điều này được giải quyết bằng cách thêm cờ '/ s', tức là "foo. * / S", nhưng lỗi Lambda regex dường như không tôn trọng điều này.

Để thay thế, bạn có thể sử dụng một cái gì đó như: foo (. | \ N) *


tìm thấy tuyệt vời! Nó chỉ giúp tôi tiết kiệm hàng giờ đập đầu! Và nó không rõ ràng.
Mirko Vukušić

Mirko, tôi rất vui vì nó đã giúp bạn!
Carlos Ballock

2

Đây là cách được đề xuất trên Blog tính toán AWS nếu sử dụng API Gateway. Kiểm tra xem tích hợp có hoạt động với lệnh gọi Lambda trực tiếp hay không.

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

Đối với các lệnh gọi Lambda trực tiếp, đây dường như là giải pháp tốt nhất để phân tích cú pháp ở phía máy khách.


điều gì sẽ xảy ra nếu ví dụ là một cuộc gọi lambda tới lambda. đây vẫn là những gì được gọi là lambda sẽ trả lại? và làm cách nào tôi có thể đọc httpStatus đó trên lambda đang gọi.
Rod

1

Tôi đang sử dụng serverless 0.5. Đây là cách nó hoạt động, đối với trường hợp của tôi

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

Nếu bạn không muốn sử dụng proxy, bạn có thể sử dụng mẫu này:

#set($context.responseOverride.status =  $input.path('$.statusCode'))
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.