.NET 6 Web API部署Lambda后无法处理SQS队列请求
.NET 6 Web API转Lambda后,SQS触发报空引用异常的解决方法
问题场景
- 基于.NET 6 Core Web API构建项目,通过
builder.Services.AddAWSLambdaHosting(LambdaEventSource.HttpApi);转为Lambda并通过CLI部署 - POSTMAN调用Lambda函数正常,但通过AWS控制台的SQS队列发送消息触发时,抛出空引用异常
异常日志
2023-02-01T00:38:06.905Z 04b8dce2-d17c-5125-88d3-80b167d862bd fail System.NullReferenceException: Object reference not set to an instance of an object. at Amazon.Lambda.AspNetCoreServer.APIGatewayHttpApiV2ProxyFunction.MarshallRequest(InvokeFeatures features, APIGatewayHttpApiV2ProxyRequest apiGatewayRequest, ILambdaContext lambdaContext) at Amazon.Lambda.AspNetCoreServer.AbstractAspNetCoreFunction`2.FunctionHandlerAsync(TREQUEST request, ILambdaContext lambdaContext) at Amazon.Lambda.RuntimeSupport.HandlerWrapper.<>c__DisplayClass26_0`2.<GetHandlerWrapper>b__0.MoveNext() --- End of stack trace from previous location --- at Amazon.Lambda.RuntimeSupport.LambdaBootstrap.InvokeOnceAsync(CancellationToken cancellationToken)
测试消息
POSTMAN/SQS直接发送的消息
{ "nameOnCard": "Samuel Ojo", "cardNumber": "5256854950074930", "cvv": "283", "expiryMonth": "04", "expiryYear": "27", "postalCode": "101232", "billingAddress": "Lagos", "billingState": "Lagos", "billingCity": "Lagos", "billingCountry": "Nigeria", "awsSettings": { "topicArn": [redacted], "targetArn": [redacted], "snsTopicFilter": "VirtualCard" } }
SQS模板消息
{ "Records": [ { "messageId": "19dd0b57-b21e-4ac1-bd88-01bbb068cb78", "receiptHandle": "MessageReceiptHandle", "body": "{\"sourceAccount\": \"0123445599\",\"destinationAccount\": \"1838494040\",\"bankCode\": \"011\",\"amount\": 200,\"awsSettings\": {\"topicArn\": [redacted],\"targetArn\": [redacted],\"snsTopicFilter\": \"FundTransfer\"}}", "attributes": { "ApproximateReceiveCount": "1", "SentTimestamp": "1523232000000", "SenderId": "123456789012", "ApproximateFirstReceiveTimestamp": "1523232000001" }, "messageAttributes": {}, "md5OfBody": "{{{md5_of_body}}}", "eventSource": "aws:sqs", "eventSourceARN": "arn:aws:sqs:us-east-1:123456789012:MyQueue", "awsRegion": "us-east-1" } ] }
Program.cs代码
using VirtualCardService.Middlewares; using VirtualCardService.Services.Contracts; var builder = WebApplication.CreateBuilder(args); #region Add Services to Container builder.Services.AddControllers(); builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); builder.Services.AddScoped<IVirtualCardService, VirtualCardService.Services.Implementations.VirtualCardService>(); builder.Services.AddAWSLambdaHosting(LambdaEventSource.HttpApi); #endregion var app = builder.Build(); #region Configure Pipeline if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); } app.UseHttpsRedirection(); app.Use((context, next) => { context.Request.EnableBuffering(); return next(); }); app.UseRouting(); app.UseMiddleware<HttpLoggerMiddleware>(); app.UseMiddleware<ExceptionMiddleware>(); app.UseAuthorization(); app.MapControllers(); #endregion app.Run();
问题原因及解决方法
原因
当前Lambda通过LambdaEventSource.HttpApi配置为仅支持HttpApi触发,而SQS触发的事件格式与HttpApi请求结构完全不兼容。APIGatewayHttpApiV2ProxyFunction尝试将SQS事件转换为ASP.NET Core请求时,找不到预期的HttpApi请求字段,从而抛出空引用异常。
解决步骤
修改Lambda处理逻辑,支持多事件源
不能仅依赖AddAWSLambdaHosting的HttpApi模式,需要自定义Lambda入口函数,分别处理HttpApi请求和SQS事件:- 创建自定义Lambda入口类,继承
APIGatewayHttpApiV2ProxyFunction处理Http请求,同时添加SQS事件处理方法。
- 创建自定义Lambda入口类,继承
示例修改方案
移除Program.cs中的builder.Services.AddAWSLambdaHosting(LambdaEventSource.HttpApi);,新增Lambda入口类:// LambdaEntryPoint.cs using Amazon.Lambda.AspNetCoreServer; using Amazon.Lambda.SQSEvents; using Microsoft.AspNetCore.Hosting; using VirtualCardService.Services.Contracts; namespace VirtualCardService; public class LambdaEntryPoint : APIGatewayHttpApiV2ProxyFunction { protected override void Init(IWebHostBuilder builder) { builder.ConfigureServices(services => { // 注册原有服务 services.AddControllers(); services.AddScoped<IVirtualCardService, Services.Implementations.VirtualCardService>(); }).Configure(app => { // 配置原有管道逻辑 if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); } app.UseHttpsRedirection(); app.UseRouting(); app.UseMiddleware<HttpLoggerMiddleware>(); app.UseMiddleware<ExceptionMiddleware>(); app.UseAuthorization(); app.MapControllers(); }); } // 处理SQS事件的方法 public async Task HandleSQSAsync(SQSEvent sqsEvent, ILambdaContext context) { using var scope = ServiceProvider.CreateScope(); var cardService = scope.ServiceProvider.GetRequiredService<IVirtualCardService>(); foreach (var record in sqsEvent.Records) { // 解析消息体并调用业务逻辑 context.Logger.LogInformation($"Processing SQS message: {record.Body}"); // 替换为你的业务处理代码 // await cardService.YourBusinessMethod(record.Body); } } }部署时指定Lambda处理器:
- HttpApi触发:
VirtualCardService::VirtualCardService.LambdaEntryPoint::FunctionHandlerAsync - SQS触发:
VirtualCardService::VirtualCardService.LambdaEntryPoint::HandleSQSAsync
- HttpApi触发:
简化替代方案
如果业务允许,可拆分两个Lambda函数:一个专门处理HttpApi请求,另一个处理SQS事件,共享业务逻辑层代码,避免混合处理的复杂度。
内容的提问来源于stack exchange,提问作者Samuel Babbs
相关产品推荐
相关产品推荐

