Thanks for the reproduction, makes it super clear what the issue you are seeing is, so much appreciated!
Currently the default behavior won’t return what is populated in the response, so that is something we can look at supporting.
In the meantime, you can use your own IServiceGateway
to handle this kind of behavior. To do so, you will need to implement a few interfaces and add some dependencies to your IoC.
Custom InProcessGateway
public class MyInProcessGateway : InProcessServiceGateway
{
public MyInProcessGateway(IRequest req) : base(req) { }
protected override async Task<TResponse> ExecAsync<TResponse>(object request)
{
if (Request is IConvertRequest convertRequest)
request = convertRequest.Convert(request);
var appHost = HostContext.AppHost;
var filterResult = await appHost.ApplyGatewayRequestFiltersAsync(Request, request);
if (!filterResult && !Request.Response.IsClosed)
return default;
// If req.Response is closed, assume populating the response is handled by the filter
if (Request.Response.IsClosed)
return await ConvertToResponseAsync<TResponse>(Request.Response).ConfigAwait();
if (request is object[] requestDtos)
{
foreach (var requestDto in requestDtos)
{
await ExecValidatorsAsync(requestDto);
}
}
else
{
await ExecValidatorsAsync(request);
}
var response = await HostContext.ServiceController.GatewayExecuteAsync(request, Request, applyFilters: false);
var responseDto = await ConvertToResponseAsync<TResponse>(response).ConfigAwait();
if (!await appHost.ApplyGatewayResponseFiltersAsync(Request, responseDto))
return default;
return responseDto;
}
}
Here, the main change is the population of the result if the req.Response
is closed.
var filterResult = await appHost.ApplyGatewayRequestFiltersAsync(Request, request);
if (!filterResult && !Request.Response.IsClosed)
return default;
// If req.Response is closed, assume populating the response is handled by the filter
if (Request.Response.IsClosed)
return await ConvertToResponseAsync<TResponse>(Request.Response).ConfigAwait();
Custom IServiceGatewayFactory
public class MyInProcessGatewayFactory : IServiceGatewayFactory
{
public IServiceGateway GetServiceGateway(IRequest request)
{
return new MyInProcessGateway(request);
}
}
This enables the ability to register and override the default InProcessGateway
.
Register dependencies within IoC
Depending on which IoC you are using, the syntax might be a bit different, but here is the code for registering against the built in .NET 8 IoC.
public class AppHost() : AppHostBase("GatewayTest"), IHostingStartup
{
public void Configure(IWebHostBuilder builder) => builder
.ConfigureServices(services => {
// Configure ASP.NET Core IOC Dependencies
services.AddTransient<IServiceGatewayFactory>(provider => new MyInProcessGatewayFactory());
});
The code in your example for your GatewayRequestFiltersAsync
stays the same.
this.GatewayRequestFiltersAsync.Add(async (req, dto) =>
{
if (dto is HelloGateway hello && hello.Mode == 2)
{
var res = req.Response;
res.StatusCode = (int)HttpStatusCode.OK;
res.ContentType = MimeTypes.Json;
var responseDto = new HelloGatewayResponse
{
Result = $"Hello, {hello.Name}, short-circuited in the Request Filter"
};
await res.WriteToResponse(req, responseDto).ConfigAwait();
await res.EndRequestAsync();
}
});
And the request is short-circuited.
Hope that helps.