2

I have an MVC net core 2.2 application with API Versioning. I only want the middleware to run for v2 of the api or greater.

The middleware class I have written uses the IApiVersionReader.Read(), however that doesn't seem to be populated until after the UseMVC line in Startup.

var apiVersion = _apiVersionReader.Read(_httpContextAccessor.HttpContext.Request);

So if I register my middleware before UseMVC, then it can't read the version using the versioning method.

But if I put my middleware after the UseMVC line, then it doesn't trigger at all.

It feels like a chicken and egg situation! How can I get this to work?

Here's a simplified version of my startup, for brevity.

public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc(config =>
            {
                // Don't add auth in development.        
                if (!CurrentEnvironment.IsDevelopment())
                {
                    var policy = new AuthorizationPolicyBuilder()
                        .RequireAuthenticatedUser()
                        .Build();
                    config.Filters.Add(new AuthorizeFilter(policy));
                }
            }
        );

        services.AddTransient<IHttpContextAccessor, HttpContextAccessor>();

        services.AddMvcCore()
            .AddAuthorization()
            .AddJsonFormatters(config =>
                config.ReferenceLoopHandling = ReferenceLoopHandling.Ignore);

        services.AddApiVersioning(options =>
        {
            options.ReportApiVersions = true;
            options.AssumeDefaultVersionWhenUnspecified = true;
            options.DefaultApiVersion = new ApiVersion(2, 0);
        });

    }

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {

        app.UseMvc();

        app.UseMiddleware(typeof(UserDataLoggingMiddleware));
    }
David C
  • 2,766
  • 6
  • 29
  • 44

2 Answers2

4

Got a response on the Github page.

API Versioning auto-registers it's middleware purely for simplicity. To control the behavior yourself, do the following:

First, opt out of auto-registration:

services.AddApiVersioning(options => options.RegisterMiddleware = false);

Then, register the middleware however you like in the pipeline:

services.UseApiVersioning();
services.UseMvc();

I hope that helps.

David C
  • 2,766
  • 6
  • 29
  • 44
  • This is the correct setup; however, it's worth noting that the only thing that the middleware does is register the _IApiVersioningFeature_ into the current **HttpContext**. Be aware that the _final decision_ regarding the API version for the current request has not yet been resolved. There's not an easy way to do that in middleware. The **IApiVersioningFeature** will get you the raw and parsed API Version via the **IApiVersionReader**, but know that the parse API version could be `null` (ex: it's invalid) or may not actually exist. You'll need to consider that in your middleware implementation – Chris Martinez Dec 30 '19 at 16:28
0

I can think of a couple of options here

  1. You can get the version in the URL manually via _httpContextAccessor.HttpContext.Request.Path or if its in the headers then _httpContextAccessor.HttpContext.Request.Headers

  2. Use UseWhen when registering your middleware. This will configure the middleware pipeline. What you want is a rejoining branch. See this great article for more info on branching middlewares -> https://thomaslevesque.com/2018/03/27/understanding-the-asp-net-core-middleware-pipeline/

Paul Lorica
  • 703
  • 4
  • 6