Ant Swift
Ant Swift

Reputation: 21217

ASP.NET Core seperate middleware for area controllers

I am building a prototype whereby I host my ASP.NET Core website (standard controllers/views etc) and the API within the same project.

I wish to use the following route scheme:

My approach thus far is to look at areas and the route config below works perfectly:

app.UseMvc(routes =>
{
    routes.MapRoute(
        name: "areaDefault",
        template: "{area:exists}/{controller=Values}/{action=Index}");

    routes.MapRoute(
                name: "default",
                template: "{controller=Home}/{action=Index}/{id?}");
});

Now I want to expand this to use one set of middleware when accessing the API and another set when accessing the website. In reality this is to use different authentication setups between the areas. Google-fu lead me to the IApplicationBuilder.Map method and this is where my problem lies.

The config below works for the default route, Home/Index is executed but anything after /api returns 404.

// TODO: Add common middleware.

app.Map("/api", builder =>
{
    // TODO: Add api specific middleware.

    app.UseMvc(routes =>
    {
        routes.MapRoute(
            name: "apiDefault",
            template: "api/{controller=Values}/{action=Index}");
    });
});

// TODO: Add website specific middleware.

app.UseMvc(routes =>
{
    routes.MapRoute(
                name: "default",
                template: "{controller=Home}/{action=Index}/{id?}");
});

Routes i have tried without success are:

The ValuesController is under the folder Areas/Api/Controllers and is defined as:

[Area("api")]
public class ValuesController : Controller
{
    public IActionResult Index()
    {
        return Json(new { test = 1 });
    }
}

The full source to reproduce this is available here: https://github.com/AntSwift/ApiRouteTest

Am I heading down the right path with this, is there something obvious I'm missing or is what I am attempting simply not possible.

Upvotes: 0

Views: 3451

Answers (2)

Esteban Elverdin
Esteban Elverdin

Reputation: 3582

Check this, in the map section replace app.UseMvc with builder.UseMvc

app.Map("/api", builder =>
{
    // TODO: Add api specific middleware.

    app.UseMvc(routes =>   // should be builder.UseMvc(routes
    {
        routes.MapRoute(
            name: "apiDefault",
            template: "api/{controller=Values}/{action=Index}");
    });
});

Upvotes: 0

Dave Anderson
Dave Anderson

Reputation: 31

I suggest to do it differently and don't mix them together. Just create your api area or use api routing the way you want. Then create a middleware and name it "ApiAuthenticationMiddleware". Put it somewhere at the top of the configure pipeline. In that middleware look at the different properties in httpcontext . There are a lot of useful values in httpcontext. In your case you could use "path" property to find out where the request are going.

If the path has "/api/" in it. It means its going to your api's. Otherwise its going to your normal controllers. In that case you should short-circuit to next step in the pipeline.

Again in your case you could use below snippet in your middleware as an example. You should change it the way you want:

    public async Task Invoke(HttpContext httpContext, YourDbContext dbContext)
    {
        string requestPath = httpContext.Request.Path.Value;

        if(!requestPath.Contains("/api/"))
            await _next.Invoke(httpContext);

        string authHeader = httpContext.Request.Headers["Authorization"];
        if (authHeader != null && authHeader.StartsWith("Basic"))
        {
            //Extract credentials
            // other stuff

            // example when no authorization header exists and want to reject.
            httpContext.Response.StatusCode = 401; //Unauthorized
            return;


        // Call the next delegate/middleware in the pipeline
        await _next.Invoke(httpContext);
        }
    }

Upvotes: 3

Related Questions