I apologize in advance for the long question.
I am using FastEndpoints in an abp.io project. It's strange, not? Let's ignore the reasons behind and suppose we are forced to do that. Here's the sample codes:
Endpoint class in HttpApi project
public class Login(IMediator mediator) : Endpoint<LoginRequest, LoginResponse>
{
private readonly IMediator _mediator = mediator;
public override void Configure()
{
Post("auth/login");
AllowAnonymous();
}
public override async Task<LoginResponse> ExecuteAsync(LoginRequest request, CancellationToken cancellationToken)
{
// ******** exception A: from inside Endpoint itself *********
// throw new BusinessException(AuthErrorCodes.Users.UserNotFound);
var requestClient = _mediator.CreateRequestClient<LoginRequest>();
var accepted = await requestClient.GetResponse<LoginResponse>(request, cancellationToken);
return accepted.Message;
}
}
Handler class in Application project
public interface ILoginHandler : IConsumer<LoginRequest> {} // this interface declared in Application.Contracts project
public class LoginHandler : ILoginHandler
{
public LoginHandler() {}
public async Task Consume(ConsumeContext<LoginRequest> context)
{
// logic
// ******** exception B: from inside MassTransit consumer *********
throw new BusinessException(AuthErrorCodes.Users.UserNotFound);
// logic
}
}
The problem: When I added FastEndpoints I found that default abp Exception Handling mechanism didn't work correctly. After checking AbpExceptionHandlingMiddleware
's source code , I noticed that theHttpContext
only handles exception when it has an item with "_AbpActionInfo" key in its Items
collection. To overcome this issue, I wrote a PreProcessor class:
public class HttpContextItemsModifier : IGlobalPreProcessor
{
public Task PreProcessAsync(IPreProcessorContext ctx, CancellationToken ct)
{
ctx.HttpContext.Items["_AbpActionInfo"] = new AbpActionInfoInHttpContext { IsObjectResult = true };
return Task.CompletedTask;
}
}
And configured FastEndpoint this way:
app.UseFastEndpoints(c =>
{
c.Endpoints.Configurator = ep =>
{
ep.PreProcessor<HttpContextItemsModifier>(Order.Before);
};
// otehr configurations
});
With above changes, the problem fixed for only exceptions those thrown directly from FastEndpoints's Endpoint
classes(A exception marked above). But the problem still exists for exceptions thrown from MassTransit Consume
methods(B exception). Why? It's because MassTransit consumer wraps B exception into a MassTransit.RequestException class. Adding a try catch block and throw InnerException(BusinessException here), magically solves the problem:
public override async Task<LoginResponse> ExecuteAsync(LoginRequest request, CancellationToken cancellationToken)
{
// ******** exception A: from inside Endpoint itself *********
// throw new BusinessException(AuthErrorCodes.Users.UserNotFound);
try
{
var requestClient = _mediator.CreateRequestClient<LoginRequest>();
var accepted = await requestClient.GetResponse<LoginResponse>(request, /*Callback,*/ cancellationToken);
return accepted.Message;
}
catch (System.Exception ex)
{
throw ex.InnerException;
}
}
The Question: But I'm looking for a way to force MassTransit to not wrap exceptions, and throw them in a bare manner