使用IRequestClient从状态机获取结果时MassTransit报错排查
问题排查:通过API+MediatR获取状态机结果时超时并返回404
问题场景
尝试通过API结合MediatR模式获取状态机存储在数据库中的结果,但调用后出现超时异常,最终返回404错误。
代码示例
namespace Play.Trading.Purchases.GetPurchase { public record GetPurchaseStateCommand(Guid CorrelationId) : ICommand<GetPurchaseStateResult>; public record GetPurchaseStateResult(PurchaseDto PurchaseDto); public class GetPurchaseStateHandler(IRequestClient<GetPurchaseState>) : ICommandHandler<GetPurchaseStateCommand, GetPurchaseStateResult> { public async Task<GetPurchaseStateResult> Handle(GetPurchaseStateCommand command, CancellationToken cancellationToken) { var response = await purchaseClient.GetResponse<PurchaseState>(new GetPurchaseStateRequest(command.CorrelationId), cancellationToken); if (response.Message.CorrelationId != command.CorrelationId) throw new NotFoundException($"No data with {command.CorrelationId} was fpund"); return new GetPurchaseStateResult(new PurchaseDto(response.Message.UserId, response.Message.ItemId, response.Message.PurchaseTotal, response.Message.Quantity, response.Message.CurrentState, response.Message.ErrorMessage, response.Message.Received, response.Message.LastUpdated)); } } }
错误日志
System.InvalidOperationException: ExceptionHandlerOptions配置的异常处理程序返回了404状态响应。由于这通常是ExceptionHandlingPath配置错误导致的,因此抛出包含原始异常的InvalidOperationException。如果预期异常处理程序返回404状态响应,请将AllowStatusCode404Response设置为true。 ---> MassTransit.RequestException: 处理GetPurchaseState请求时发生异常 ---> System.ArgumentException: 消息类型没有可用的无参构造函数(参数'TMessage') 在 MassTransit.Initializers.MessageFactoryCache`1.Cached.CreateMessageFactory() 在 System.Lazy`1.ViaFactory(LazyThreadSafetyMode mode) 在 System.Lazy`1.ExecutionAndPublication(LazyHelper executionAndPublication, Boolean useDefaultConstructor) 在 System.Lazy`1.CreateValue() 在 MassTransit.Initializers.MessageFactoryCache`1.get_Factory() 在 MassTransit.Initializers.Factories.MessageInitializerBuilder`2.Build() 在 MassTransit.Initializers.Factories.MessageInitializerFactory`2.CreateMessageInitializer() 在 MassTransit.Initializers.MessageInitializerCache`1.CreateMessageInitializer(Type inputType) 在 MassTransit.Initializers.MessageInitializerCache`1.<>c__DisplayClass2_0.<MassTransit.Initializers.IMessageInitializerCache<TMessage>.GetInitializer>b__0() 在 System.Lazy`1.ViaFactory(LazyThreadSafetyMode mode) 在 System.Lazy`1.ExecutionAndPublication(LazyHelper executionAndPublication, Boolean useDefaultConstructor) 在 System.Lazy`1.CreateValue() 在 MassTransit.Initializers.MessageInitializerCache`1.MassTransit.Initializers.IMessageInitializerCache<TMessage>.GetInitializer(Type inputType) 在 MassTransit.Initializers.MessageInitializerCache`1.GetInitializer(Type inputType) 在 MassTransit.Clients.PublishRequestSendEndpoint`1.Send(Guid requestId, Object values, IPipe`1 pipe, CancellationToken cancellationToken) 在 MassTransit.Clients.RequestClient`1.<>c__DisplayClass9_0`1.<<GetResponse>g__Request|0>d.MoveNext() --- 之前位置的堆栈跟踪结束 --- 在 MassTransit.Clients.ClientRequestHandle`1.SendRequest() --- 内部异常堆栈跟踪结束 --- 在 MassTransit.Clients.ClientRequestHandle`1.SendRequest() 在 MassTransit.Clients.ResponseHandlerConnectHandle`1.GetTask() 在 MassTransit.Clients.RequestClient`1.GetResponseInternal[T](SendRequestCallback request, CancellationToken cancellationToken, RequestTimeout timeout, RequestPipeConfiguratorCallback`1 callback) 在 Play.Trading.Purchases.GetPurchase.GetPurchaseStateHandler.Handle(GetPurchaseStateCommand command, CancellationToken cancellationToken) 位置 C:\Users\USER\source\repos\EshopMicroservices\src\Play.Catalog\Play.Trading\Purchases\GetPurchase\GetPurchaseStateHandler.cs:行号 17 在 buildingBlock.Behaviours.LoggingBehaviour`2.Handle(TRequest request, RequestHandlerDelegate`1 next, CancellationToken cancellationToken) 位置 C:\Users\USER\source\repos\EshopMicroservices\src\buildingBlock\buildingBlock\Behaviours\LoggingBehaviour.cs:行号 22 在 buildingBlock.Behaviours.ValidationBehaviour`2.Handle(TRequest request, RequestHandlerDelegate`1 next, CancellationToken cancellationToken) 位置 C:\Users\USER\source\repos\EshopMicroservices\src\buildingBlock\buildingBlock\Behaviours\ValidationBehaviour.cs:行号 20 在 Play.Trading.Purchases.GetPurchase.GetPurchaseStateEndPoint.GetPurchaseStatus(GetPurchaseStateRequest request, ISender sender) 位置 C:\Users\USER\source\repos\EshopMicroservices\src\Play.Catalog\Play.Trading\Purchases\GetPurchase\GetPurchaseStateEndPoint.cs:行号 28 在 Microsoft.AspNetCore.Http.RequestDelegateFactory.ExecuteTaskResult[T](Task`1 task, HttpContext httpContext) 在 Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context) 在 Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context) 在 Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddlewareImpl.<Invoke>g__Awaited|10_0(ExceptionHandlerMiddlewareImpl middleware, HttpContext context, Task task) --- 内部异常堆栈跟踪结束 --- 在 Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddlewareImpl.HandleException(HttpContext context, ExceptionDispatchInfo edi) 在 Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddlewareImpl.<Invoke>g__Awaited|10_0(ExceptionHandlerMiddlewareImpl middleware, HttpContext context, Task task) 在 Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context)
问题根源与解决步骤
核心问题
最内层的ArgumentException是触发所有连锁错误的根源:MassTransit无法初始化消息类型,因为对应的消息类(GetPurchaseStateRequest或PurchaseState)没有无参构造函数。外层的超时、404及InvalidOperationException都是这个异常引发的后续问题。
解决步骤
为消息类添加无参构造函数
- 如果是class类型:添加public无参构造,同时保留带参数的构造逻辑
public class GetPurchaseStateRequest { public Guid CorrelationId { get; set; } // 无参构造 public GetPurchaseStateRequest() {} // 带参构造 public GetPurchaseStateRequest(Guid correlationId) { CorrelationId = correlationId; } } - 如果是record类型:可以通过重载构造函数实现
public record GetPurchaseStateRequest(Guid CorrelationId) { // 无参构造,默认值可按需调整 public GetPurchaseStateRequest() : this(Guid.Empty) {} }
同理检查
PurchaseState类,确保它也符合要求。- 如果是class类型:添加public无参构造,同时保留带参数的构造逻辑
修正代码拼写错误
将throw new NotFoundException($"No data with {command.CorrelationId} was fpund");中的fpund改为found。调整异常中间件配置(可选)
如果确实需要在异常场景返回404,在Program.cs/Startup.cs中修改ExceptionHandler配置:app.UseExceptionHandler(options => { options.AllowStatusCode404Response = true; // 其他自定义异常处理逻辑 });
内容的提问来源于stack exchange,提问作者Vondella21
相关产品推荐
相关产品推荐

