我在非代理模式下从API网关调用基于python的AWS Lambda方法。我应该如何正确地处理异常,以便使用异常的部分设置适当的HTTP状态码和JSON体。
作为一个例子,我有以下处理程序:def my_handler(event, context):
try:
s3conn.head_object(Bucket='my_bucket', Key='my_filename')
except botocore.exceptions.ClientError as e:
if e.response['Error']['Code'] == "404":
raise ClientException("Key '{}' not found".format(filename))
# or: return "Key '{}' not found".format(filename) ?
class ClientException(Exception):
pass
应该抛出异常还是返回字符串?那么我应该如何配置集成响应?显然我有RTFM,但FM是FU
br
- 如果你想要一个非200的响应,你的Lambda处理程序必须抛出一个异常。捕获处理程序方法中的所有异常。将捕获的异常消息格式化为JSON,并作为自定义异常类型抛出。
- 使用集成响应来正则化在Lambda响应的errorMessage字段中找到的自定义异常。
API网关+ AWS Lambda异常处理
关于Lambda、API Gateway以及它们是如何协同工作的,你需要知道很多事情。
λ异常当一个异常从你的处理程序/函数/方法抛出时,异常被序列化成JSON消息。在您的示例代码中,对于来自S3的404,您的代码将抛出:
{
"stackTrace": [
[
"/var/task/mycode.py",
118,
"my_handler",
"raise ClientException("Key '{}' not found ".format(filename))"
]
],
"errorType": "ClientException",
"errorMessage": "Key 'my_filename' not found"
}
API网关集成响应
概述"Integration Responses"将Lambda的响应映射到HTTP代码。它们还允许在传递时更改消息体。
默认情况下,为您配置了一个"200"集成响应,它将所有来自Lambda的响应原样传递回客户端,包括序列化的JSON异常,作为HTTP 200 (OK)响应。
对于好的消息,您可能希望使用"200"集成响应将JSON有效负载映射到您定义的模型之一。
捕获异常
对于异常,您将需要设置适当的HTTP状态码,并可能删除堆栈跟踪以隐藏代码的内部。
对于您希望返回的每个HTTP状态码,您将需要添加一个"集成响应"条目。集成响应配置了一个与errorMessage字段匹配的regex匹配(使用java.util.regex.Matcher.matches()
而不是.find()
)。一旦匹配完成,你就可以配置一个Body Mapping Template,来选择性地格式化一个合适的异常体。
由于regex只匹配异常中的errorMessage字段,因此您需要确保异常包含足够的信息,以允许不同的集成响应匹配并相应地设置错误。(你不能使用.*
来匹配所有的异常,因为这似乎匹配所有的响应,包括非异常!)
含义
的异常要在其消息中创建具有足够详细信息的异常,error-handling-patterns-in-amazon-api-gateway-and-aws-lambda博客建议您在处理程序中创建一个异常处理程序,将异常的详细信息填充到一个JSON字符串中,以便在异常消息中使用。
我更喜欢的方法是创建一个新的top方法作为处理程序,它处理响应API网关。这个方法要么返回所需的有效负载,要么抛出一个异常,其中包含一个编码为JSON字符串的原始异常,作为异常消息。
def my_handler_core(event, context):
try:
s3conn.head_object(Bucket='my_bucket', Key='my_filename')
...
return something
except botocore.exceptions.ClientError as e:
if e.response['Error']['Code'] == "404":
raise ClientException("Key '{}' not found".format(filename))
def my_handler(event=None, context=None):
try:
token = my_handler_core(event, context)
response = {
"response": token
}
# This is the happy path
return response
except Exception as e:
exception_type = e.__class__.__name__
exception_message = str(e)
api_exception_obj = {
"isError": True,
"type": exception_type,
"message": exception_message
}
# Create a JSON string
api_exception_json = json.dumps(api_exception_obj)
raise LambdaException(api_exception_json)
# Simple exception wrappers
class ClientException(Exception):
pass
class LambdaException(Exception):
pass
在异常情况下,Lambda现在将返回:
{
"stackTrace": [
[
"/var/task/mycode.py",
42,
"my_handler",
"raise LambdaException(api_exception_json)"
]
],
"errorType": "LambdaException",
"errorMessage": "{"message": "Key 'my_filename' not found", "type": "ClientException", "isError": true}"
}
映射异常现在您已经在errorMessage中拥有了所有的详细信息,您可以开始映射状态码并创建格式良好的错误有效负载。API Gateway解析并取消errorMessage字段的转义,因此使用的正则表达式不需要处理转义。
例子要捕获此ClientException为400错误并将有效负载映射到干净的错误模型,您可以执行以下操作:
创建新的错误模型:
{ "type": "object", "title": "MyErrorModel", "properties": { "isError": { "type": "boolean" }, "message": { "type": "string" }, "type": { "type": "string" } }, "required": [ "token", "isError", "type" ] }
- 编辑"Method Response"并将新模型映射到
400
- 添加新的集成响应
- 设置代码为
400
- 设置regex匹配"ClientException"类型,并允许空白:
.*"type"s*:s*"ClientException".*
为
application/json
添加Body Mapping Template以将errorMessage
的内容映射到您的模型:#set($inputRoot = $input.path('$')) #set ($errorMessageObj = $util.parseJson($input.path('$.errorMessage'))) { "isError" : true, "message" : "$errorMessageObj.message", "type": "$errorMessageObj.type" }