有没有办法更改 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 详细信息,包括 statusCode
、body
和 headers
。
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
对于异步函数,只需返回带有statusCode
和body
的对象。对于同步功能使用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 状态代码?的主要内容,如果未能解决你的问题,请参考以下文章
有没有办法将多个静态 IP 分配给 Amazon Lightsail 实例?
在没有 lxml 的 Google Appengine 上使用 python-amazon-product-api [重复]