Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support filters: IResultFilter for classic controllers and IEndpointFilter for minimalAPI #72

Open
AKlaus opened this issue May 28, 2024 · 1 comment · May be fixed by #73
Open

Support filters: IResultFilter for classic controllers and IEndpointFilter for minimalAPI #72

AKlaus opened this issue May 28, 2024 · 1 comment · May be fixed by #73
Labels
enhancement New feature or request

Comments

@AKlaus
Copy link
Owner

AKlaus commented May 28, 2024

Currently, the API methods have to call .ToActionResult() / .ToResult() extension methods for converting a DomainResult to IActionResult/IResult.
To avoid the repetitive calls of those methods, the MVC package can offer filters (IResultFilter for the classic controllers and IEndpointFilter for the minimal API). So, if the returning type is DomainResultBase or Task<DomainResultBase>', it would

  • call .ToActionResult() or .ToActionResultOfT() for the classic controllers;
  • call .ToResult() or .ToResultOfT() for the minimal API.

Notes:

@AKlaus AKlaus added the enhancement New feature or request label May 28, 2024
@AKlaus
Copy link
Owner Author

AKlaus commented May 28, 2024

After some preliminary research, the idea seems viable but has some downsides.

Experiment
In my research, I created a test filter:

public class ActionFilter : IActionFilter, IEndpointFilter
{
  public void OnActionExecuted(ActionExecutedContext context)
  {
    var actionResult = (context.Result as ObjectResult)?.Value;
    if (actionResult is IDomainResult result)
      context.Result = result.ToActionResult();

      // ... Other types are harder to handle
  }
	
  public async ValueTask<object?> InvokeAsync(EndpointFilterInvocationContext context, EndpointFilterDelegate next)
  {
    // See context here
    return await next(context);
  }
}

which was registered globally for the classic API as

services.AddSingleton<IActionFilter, ActionFilter>();
services.AddControllers(options => options.Filters.AddService<IActionFilter>());

Downsides

  1. The classic controllers would have to step away from the usual strongly typed returned signature (e.g.IActionResult,ActionResult<T>) in favour of IDomainResult or IDomainResult<T>. Hence, it'll likely cause integration issues (e.g. with Swagger docs). Example:
[HttpGet("[action]")]
public IDomainResult<int> Get200OkWithNumber()  => _service.GetSuccessWithNumericValue();
  1. For the Minimal API, it seems that a globally registered filter for all routes is not an option. Hence, a piped call would be required (not much different from explicitly calling an extension method on IDomainResult). Though, depending on how the routes are added, the filter call can be consolidated in one place. Example:
app.MapGet("200OkWithNumber", () => service.GetSuccessWithNumericValue())
     .AddEndpointFilter<ResultFilter>()
     Produces<int>();

Notes

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant