是否有办法更改由 AmazonAPI 网关返回的 http 状态代码?

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

I'd like to have different http status codes but it looks like api gateway always returns a 200 status code even if the lambda function is returning an error.

102231 次浏览

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

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

  1. 在您的 resource方法中,单击 方法响应方法响应
  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. Your API Gateway route should return this:

    HTTP Status Code: 400
    JSON Error Response:
    {
    errorMessage: "Bad Request: You submitted invalid input"
    }
    
  6. I see no way to copy these settings and re-use it for different methods, so we have much annoying redundant manual inputting to do!

My Integration Responses look like this:

aws api gateway lambda error response handling

For those who tried everything put on this question and couldn't make this work (like me), check the thedevkit comment on this post (saved my day):

Https://forums.aws.amazon.com/thread.jspa?threadid=192918

完全复制如下:

我自己也有过这样的问题,我相信这句新台词 角色是罪魁祸首。

* 将匹配出现在“ foo”后面的任何字符 通常可以通过添加’/s’标志来解决这个问题,即。 "foo.*/s", but the Lambda error regex doesn't seem to respect this.

作为一种替代方法,您可以使用诸如: 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设置这个 regex:

* “ status”: 400. *

Finally, you setup a Mapping Template to extract the JSON response from the errorMessage property returned by Lambda. The Mapping Template looks like this:

$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/

每20-9-2016年更新

Amazon 最终使用 Lambda 代理整合简化了这一过程,这使得你的 Lambda 函数可以返回正确的 HTTP 代码和头:

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


context.succeed(response);

和 API 网关中的请求/响应映射说再见吧!

选择二

使用 Aws-无服务器-快递将现有 Express 应用程序与 Lambda/API Gateway 集成。

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

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 计算博客上就是这样推荐的。检查集成是否与直接 Lambda 调用一起工作。

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

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

I wanted an error from Lambda to be proper 500 error, after doing a lot of research, came up with the below, that works:

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

For a bad response, returning as below

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

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

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

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

400 & 500 errors

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

2) Change your lambda code in 2 ways

  • 适当处理传入的 event(第一个函数参数)。它不再仅仅是有效负载,它代表整个 HTTP 请求,包括头、查询字符串和正文。下面是样本。关键点是 JSON 主体将是需要显式 JSON.parse(event.body)调用的字符串(不要忘了 try/catch)。下面是一个例子。
  • 通过使用 null 调用回调来响应,然后使用一个响应对象提供包括 statusCodebodyheaders在内的 HTTP 详细信息。
    • body应该是一个字符串,JSON.stringify(payload)也应该如此
    • statusCode可以是一个数字
    • headers is an object of header names to values

代理集成的示例 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'
}
})

笔记 - I believe the methods on context such as context.succeed() are deprecated. They are no longer documented although they do still seem to work. I think coding to the callback API is the correct thing going forward.

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

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

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

Valid as of Feb 2021

The easiest way to set custom HTTP status code is to setup a Lambda Proxy Integration in API Gateway.

在 API 网关 > 资源 > 操作下拉 > 创建方法 > 勾选 Lambda Proxy Integration并选择适当的 Lambda 函数。

API 网关 enter image description here

enter image description here

Lambda

对于异步函数,返回一个带有 statusCodebody的对象。对于同步函数,使用 callback(null,obj); 参考 完整的文件

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


结果

自定义状态码400。

enter image description here

我有一个快速应用程序,并使用 api 网关前面的 http 集成。要从我的应用程序返回状态代码而不是200确定,我只需将应用程序的错误处理程序返回的 HTTP状态码添加到集成响应部分的 http status regex 中,它就可以正常工作了。确保应用程序正确处理错误。 enter image description here