Chalice 中间件(Middleware)完全指南:自定义 Lambda 请求与响应生命周期的标准做法
后端ServerlessCLI【免费下载链接】chalicePython Serverless Microframework for AWS项目地址https://gitcode.com/gh_mirrors/ch/chalice点击查看免费下载导读本文以 AWS Chalice 官方中间件文档为骨架系统讲解 Chalice 中间件的定义、注册方式、错误处理策略与常见实战模式。中间件让你在不动业务代码的前提下拦截并改造每个 Lambda 处理函数的请求与响应生命周期可用于统一日志、鉴权校验、性能埋点、第三方工具集成等场景。读完本文你将掌握app.middleware()装饰器、register_middleware()方法、ConvertToMiddleware适配器三类注册手段以及 REST API 特有的异常处理行为并能在真实 Chalice 应用中直接落地这些模式。Chalice 开箱即用地提供了大量特性但实际项目中你常常需要针对自己的业务定制框架行为——中间件正是为此设计的它是一个注册到应用上的函数每当你的 Lambda 函数被调用时Chalice 会自动执行它。下文所有源码引用均出自当前仓库 chalice/app.py测试佐证来自 tests/unit/test_app.py。中间件基础一段最小示例from chalice import Chalice app Chalice(app_namedemo-middleware) app.middleware(all) def my_middleware(event, get_response): app.log.info(Before calling my main Lambda function.) response get_response(event) app.log.info(After calling my main Lambda function.) return response app.route(/) def index(): return {hello: world} app.on_sns_message(mytopic) def sns_handler(event): pass这个示例中的中间件在 Lambda 函数被调用前后各输出一条日志。由于注册时指定的事件类型是all无论触发的是 REST API 的index()还是 SNS 消息处理函数sns_handler()my_middleware都会被执行。从源码结构看中间件的核心载体是 chalice/app.py 中的MiddlewareHandler类与BaseLambdaHandler._build_middleware_handlers()调用链通过把每个中间件包装成MiddlewareHandler(handlerhandler, next_handlercurrent)形成洋葱模型最后层的original_handler才是真正的 Lambda 处理函数。Chalice.__call__中通过_get_middleware_handlers(http)等调用把匹配的中间件注入到对应事件处理器中见 chalice/app.py 与 chalice/app.py。编写中间件的四项要求中间件必须满足以下约束必须是可调用对象接受两个参数event和get_response。event的具体类型取决于中间件注册时指定的事件类型详见下文注册中间件一节。必须返回一个响应这个响应会最终返回给调用方。必须调用get_response(event)才能触发链上的下一个中间件并最终执行真正的 Lambda 处理函数。可以短路short-circuit请求中间件可以不调用get_response(event)而直接返回自己的响应此时响应类型应与底层 Lambda 处理函数的响应类型保持一致。下面是最简单的什么都不做的中间件app.middleware(all) def noop_middleware(event, get_response): # event 的类型取决于被调用的 Lambda 处理函数类型。 return get_response(event)测试用例 tests/unit/test_app.py 验证了多层中间件的执行顺序注册在前的中间件先执行随后逐层向内最后执行真正的 handlertests/unit/test_app.py 则验证了短路行为——第一个中间件直接返回响应后后续中间件与 handler 都不会再被调用。错误处理策略除 REST API 外所有事件类型SNS、S3、SQS、CloudWatch、Websocket、纯 Lambda 等的中间件遵循相同的错误处理策略Lambda handler 抛出的任何异常都会原样传播回每一层中间件。你可以在中间件中捕获并处理这些异常。例如app.middleware(all) def handle_errors(event, get_response): try: return get_response(event) except MyCustomError as e: # 不希望 MyCustomError 继续向外传播 # 而是把它转换成一个错误响应字典。 return {Error: e.__class__.__name__, Message: str(e)} app.lambda_function() def noop_middleware(event, context): raise MyCustomError(Raising an error.)如果 Lambda handler 抛出异常且没有任何中间件捕获它该异常会原样返回给调用 Lambda 的客户端。REST API 的特殊错误处理出于向后兼容的考虑REST API 有专门的处理流程如果app.route装饰的 view 函数抛出了异常Chalice 会自动捕获它并转换为设置了适当状态码的Response对象即 views.rst 中view-error-handling所描述的视图错误处理机制。因此注册在 REST API 上的中间件看不到异常传播——它们调用get_response(event)得到的是Response对象而不是异常。如果你想允许异常从 view 函数中向外传播可以抛出chalice.ChaliceUnhandledError。例如from chalice import ChaliceUnhandledError app.middleware(all) def handle_errors(event, get_response): try: return get_response(event) except ChaliceUnhandledError as e: return Response(status_code500, bodystr(e), headers{Content-Type: text/plain}) app.route(/) def index(): # handle_errors 中间件永远看不到这个异常 # 它会被自动转换为状态码为 500 的 Response 对象。 raise MyCustomError(Raising an error.) app.route(/error) def unhandled_error(): # 因为异常类型是 ChaliceUnhandledError # handle_errors 中间件会看到这个异常。 raise ChaliceUnhandledError(Raising an error.)这一设计非常适合对所有事件类型统一做错误处理的中间件。如果ChaliceUnhandledError抛出后没有任何中间件捕获处理则走标准错误处理流程向用户返回 500 响应若开启了 debug 模式则把 traceback 作为响应体返回。源码佐证在 chalice/app.py 的_get_view_function_response()中view 函数的异常处理顺序是ChaliceUnhandledError被显式raise重抛让中间件有机会处理ChaliceViewError子类如BadRequestError、UnauthorizedError定义见 chalice/app.py被转换为对应状态码的响应其他未知异常则通过_unhandled_exception_to_response()转换为 500 响应chalice/app.py。REST API 处理器的中间件链还额外在首位插入了_global_error_handler作为最外层兜底chalice/app.py。注册中间件注册中间件有两种方式。方式一app.middleware()装饰器app.middleware()接受一个参数用于指定该中间件要注册到哪种类型的 Lambda 函数上。这样你可以只把中间件应用于特定事件类型的 handler例如仅 REST API、仅 WebSocket、仅 S3 事件。要注册到所有 Lambda 函数传入all即可。支持的事件类型及对应提供给中间件的 event 类型如下表事件类型中间件收到的 event 类型allAnys3S3EventsnsSNSEventsqsSQSEventcloudwatchCloudWatchEventscheduledCloudWatchEventwebsocketWebsocketEventhttpRequestpure_lambdaLambdaFunctionEvent注意chalice.LambdaFunctionEvent是唯一一个中间件 event 类型与对应 Lambda handler event 类型不一致的情况。出于向后兼容app.lambda_function()装饰器的签名被保留为(event, context)而中间件需要一个统一的单参数签名因此引入了LambdaFunctionEvent。相关定义见 chalice/app.py装饰器名到中间件事件类型的映射见 chalice/app.py_MIDDLEWARE_MAPPING额外支持kinesis与dynamodb映射。各事件类S3Event、SNSEvent、SQSEvent、CloudWatchEvent、WebsocketEvent等的属性提取逻辑都可以在 chalice/app.py 中找到例如S3Event提供bucket与key属性SNSEvent提供message、subject、message_attributes属性。方式二Chalice.register_middleware()方法register_middleware()与middleware()行为一致区别在于把中间件函数作为参数传入而非用装饰器这在导入第三方函数并当作中间件使用时非常方便import thirdparty app.register_middleware(thirdparty.func, all)DecoratorAPI.middleware()的装饰器实现本质上就是在内部调用register_middleware(func, event_type)见 chalice/app.py而Chalice.register_middleware的实现会把(func, event_type)追加到middleware_handlers列表中chalice/app.py。用ConvertToMiddleware复用现有 Lambda 包装器ConvertToMiddleware类可以把已有的Lambda 包装器wrapper转换为中间件。例如你有一个日志装饰器def log_invocation(func): def wrapper(event, context): logger.debug(Before lambda function.) response func(event, context) logger.debug(After lambda function.) return wrapper app.lambda_function() log_invocation def myfunction(event, context): logger.debug(In myfunction().)与其在每个 Lambda 函数上都手动套log_invocation不如用ConvertToMiddleware一次性把该包装器应用到应用中所有Lambda 函数from chalice import ConvertToMiddleware app.register_middleware(ConvertToMiddleware(log_invoation))从实现看ConvertToMiddleware.__call__会先从传入的event中还原出(original_event, context)参数对REST API 请求走Request.to_original_event()其余走event.to_dict()再用get_response(event)桥接回中间件链最终以(event, context)的原始签名调用第三方包装器见 chalice/app.py。该能力同样适合集成那些以 Lambda 包装器形式存在的第三方库完整的集成示例见下文集成 AWS Lambda Powertools。实战模式示例模式一短路请求Short Circuiting假设我们要求所有 HTTP 请求都必须携带某个自定义请求头否则直接返回 400。因为这是 HTTP 特有的逻辑只需注册到http事件类型from chalice import Response app.middleware(http) def require_header(event, get_response): # 由上文表格可知http 事件类型下 event 是 chalice.Request。 if X-Custom-Header not in event.headers: return Response( status_code400, body{Error: Missing required X-Custom-Header}) # 请求头存在则走正常请求流程。 return get_response(event)Request.headers是大小写不敏感的映射因此x-custom-header与X-Custom-Header的写法都能正确命中。短路场景的测试验证见 tests/unit/test_app.py。模式二修改响应Modifying a Response假设我们想测量处理耗时并注入到 Lambda 响应中import time app.middleware(pure_lambda) def inject_time(event, get_response): start time.time() response get_response(event) total time.time() - start response.setdefault(metadata, {})[duration] total return response这里注册为pure_lambda因此get_response(event)返回的是纯 Lambda 处理函数app.lambda_function()的返回字典可以安全地对它做setdefault修改。响应可被中间件逐层修改的行为在 tests/unit/test_app.py 中有对应验证多个中间件依次向响应字典注入自己的键。模式三集成 AWS Lambda PowertoolsAWS Lambda Powertools 是一套面向 Lambda 的工具集让 X-Ray 追踪、结构化日志、自定义指标变得更容易。你可以用 Chalice 中间件轻松地把 Powertools 集成进应用。下面的例子把 Powertools 的Logger与Tracer转成 Chalice 中间件自动应用到应用中所有 Lambda 函数from chalice import Chalice from chalice.app import ConvertToMiddleware # 首先不再使用 Chalice 内置 logger改用 powertools 的结构化 logger。 # 除了自动注入 lambda context假设我们还希望注入当前调用的路由。 from aws_lambda_powertools import Logger from aws_lambda_powertools import Tracer app Chalice(app_namechalice-powertools) logger Logger(serviceapp.app_name) tracer Tracer(serviceapp.app_name) # 自动把 lambda 函数上的装饰器转换为中间件 # 连接到应用中每一个 lambda 函数。 # 这样既避免了在每个 lambda 函数上手动加装饰器 # 也适用于我们无法控制源码的场景例如注册 blueprint。 app.register_middleware(ConvertToMiddleware(logger.inject_lambda_context)) app.register_middleware( ConvertToMiddleware( tracer.capture_lambda_handler(capture_responseFalse)) ) # 这里编写 Chalice 风格的中间件 # 对任何 HTTP API把请求路径注入到结构化日志消息中。 # 这展示了如何把 Chalice 中间件与已有工具组合使用。 app.middleware(http) def inject_route_info(event, get_response): logger.structure_logs(appendTrue, request_pathevent.path) return get_response(event) app.route(/) def index(): logger.info(In index() function, this will have a path key.) return {hello: world} app.route(/foo/bar) def foobar(): logger.info(In foobar() function) return {foo: bar} app.lambda_function() def myfunction(event, context): logger.info(In myfunction().) tracer.put_annotation(keyStatus, valueSUCCESS) return {}这个例子展示了两层用法ConvertToMiddleware把 Powertools 的装饰器统一注入到所有 Lambda 函数包括app.lambda_function()声明的纯 Lambda而app.middleware(http)编写的自定义中间件则专门为 HTTP 请求补充路由信息两者可以无缝共存于同一条中间件链中。该模式对使用 Blueprint 组织的应用尤其有价值——因为 Blueprint 中的 handler 由外部模块定义借助 chalice/app.py 中Blueprint.register_middleware的延迟注册机制应用级中间件也能覆盖到它们。中间件执行原理速览把上述内容汇总为执行链路应用启动时app.middleware(type)或app.register_middleware(func, type)把(func, event_type)存入Chalice.middleware_handlers列表。每次 Lambda 被调用时Chalice._get_middleware_handlers(event_type)以生成器形式筛选出event_type匹配等于指定类型或all的中间件chalice/app.py。之所以用生成器是为了尽量延迟收集从而让 handler 定义之后再注册的中间件也能生效。事件处理器如EventSourceHandler首次调用时通过_build_middleware_handlers()把中间件列表逆序包装成嵌套的MiddlewareHandler链最内层是原始 handlerchalice/app.py。REST API 处理器则额外在最外层套上_global_error_handler。请求进入时逐层穿过中间件每层执行前置逻辑 →get_response(event)调用内层 → 后置逻辑最终返回的响应再逐层向外返回给调用方。基于这一机制你可以把鉴权、限流、日志、追踪、错误兜底等横切关注点统一下沉到中间件层让业务 handler 保持纯粹。相关文档导航视图函数错误处理ChaliceViewError与状态码映射views.rst中间件核心实现MiddlewareHandler、ConvertToMiddleware、事件类与映射表chalice/app.py、chalice/app.py、chalice/app.py中间件行为测试顺序、短路、响应修改、异常传播、REST API 过滤注册tests/unit/test_app.py蓝图中注册中间件的延迟机制chalice/app.py赞分享后端ServerlessCLI【免费下载链接】chalicePython Serverless Microframework for AWS项目地址https://gitcode.com/gh_mirrors/ch/chalice点击查看免费下载相关推荐midwayServerless中间件请求生命周期管理midwayServerless中间件请求生命周期管理 你是否在开发Serverless应用时遇到过这些问题如何在请求前后统一处理日志如何优雅地实现接口鉴后端微服务云原生Rocket 框架 Fairings 完全指南请求生命周期的结构化中间件Rocket 框架 Fairings 完全指南请求生命周期的结构化中间件 Fairings 是 Rust Web 框架 Rocket 实现结构化中间件str后端开发工具Traefik Headers 中间件完全指南自定义请求/响应头、CORS 与安全响应头实战Traefik Headers 中间件完全指南自定义请求/响应头、CORS 与安全响应头实战 本文基于当前仓库 docs/content/reference/后端API网关负载均衡微服务网络云原生上一篇first-contributions 合并分支时出现 merge conflict 怎么解决从 git status 定位冲突文件到完成合并提交下一篇OpenDisplay解码上限maxEncodeWide解析5K面板为何必须限制码流尺寸创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考