jezzsantos / saastack

A comprehensive codebase template for starting your real-world, fully featured SaaS web products. On the .NET platform
The Unlicense
36 stars 9 forks source link

Move MultitenancyDetective to Middleware, and earlier in the pipeline #23

Closed jezzsantos closed 6 months ago

jezzsantos commented 7 months ago

With Minimal APIs it is currently very difficult (at the moment) to obtain the instance of the request being used in the handler of the minimal API. All of our generated APIs have the following signature (as can be seen as the output of the MinimalApiMediatRGenerator:

            apiGroup.MapGet("/health",
                async (IMediator mediator, [AsParameters] HealthCheckRequest request) =>
                     await mediator.Send(request, CancellationToken.None));

In ASPNET middleware, there seems to be no easy way to obtain the instance of the 2nd request parameter.

It is reasonably easy to get the metadata about the handler like this:

       var endpoint = httpContext.GetEndpoint();

        //TODO: get the second parameter of the endpoint request
        var method = endpoint.Metadata.GetMetadata<MethodInfo>();
        var args = method?.GetParameters();
        var requestDtoType = args?[1].ParameterType;
        if (requestDtoType.NotExists())
        {
            return false;
        }

        if (requestDtoType.IsAssignableTo(typeof(ITenantedRequest)))
        {
            //TODO: extract the ITenantedRequest.Organization from the request;
            tenantId = "atenantid";
            return true;
        }

But getting an instance of the actual request object is far harder to do.

The only clue we have is by following the code in the RequestDelegateFactory that creates an instance of a EndpointFilterInvocationContext by using the instance of the RequestDelegateFactoryContext derived from a bunch of things:

This code is used for the EndPointMiddleware to provide its sub-pipeline for any registered IEndpointFilter where it feeds each IEndpointFilter an instance of the EndpointFilterInvocationContext which gives access to the instance of the Arguments which is what we are after.

So this capability is currentlyonly available in a custom IEndpointFilter but not in middleware.

This means that we cannot implement middleware to do certain things that require the instance of the request. What we can do is wire in a IEndpointFilter and get access to it, but this may mean that this IEndpointFilter comes too late in the request pipeline to make use of that information.

At present, ASPNET dictates that all IEndpointFilter come after the EndpointMideelware executes, which is the last piece of middleware, coming after any custom middleware we add. So this is potentially a problem.

For now, or until we can find a better way, the MultiTenancyFilter is how we will process tenanted requests to obtain, validate and perform multitenancy