2

I'm trying to implement multi tenancy based on the first folder segment of the request url.

Consider the following fragments from my startup.

foreach (SiteFolder f in allFolders)
{
    PathString path = new PathString("/" + f.FolderName);
    app.Map(path,
    siteApp =>
    {
        //here I'm trying to specify a different auth cookie name for folder sites and some other configuration
        // but problem happens even with no code here at all
        // I also tried adding the folder specific routes here but still 404
    });
}

app.UseMvc(routes =>
{
    List<SiteFolder> allFolders = siteRepo.GetAllSiteFoldersNonAsync();

    foreach (SiteFolder f in allFolders)
    {
        routes.MapRoute(
        name: f.FolderName + "Default",
        template: f.FolderName + "/{controller}/{action}/{id?}",
        defaults: new { controller = "Home", action = "Index" },
        constraints: new { name = new SiteFolderRouteConstraint(f.FolderName) }
    );

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

If I comment out app.Map at the top then my routes work as expected but otherwise if I have branching on the same folder names as as in my folder routes, the folder urls all result in an IIS 404 page.

I thought maybe siteApp doesn't see the routes added to app so I tried adding the routes to siteApp but it still resulted in 404.

A similar approach worked for me in MVC 5, I feel like either there is a bug in the framework (beta5) or I'm missing some important concept about branching and the new framework.

4
  • moving the routing logic above the branching logic fixed the routing problem but then I get an error when logging in even though the login url is not one of the folder branch urls. [Microsoft.AspNet.Diagnostics.ErrorHandlerMiddleware] An unhandled exception has occurred: The following authentication scheme was not accepted: Microsoft.AspNet.Identity.Application Commented Jul 25, 2015 at 18:23
  • I found this question stackoverflow.com/questions/24689763/… which indicates that cookie auth must be configured before MVC so I'm caught in a trap. If I set main site auth stuff before mvc I can login to the main site but if I try to branch and configure folder site auth before mvc the folder routes get broken. if I configure auth then mvc then try to adjust the auth for main or folder sites it has no affect Commented Jul 25, 2015 at 18:37
  • ok it turns out I do need to add the default route to siteApp and app does not need the folder routes added. but in order for the siteApp route to work the template of the route must NOT have the folder name template: "{controller}/{action}/{id?}" works but template: f.FolderName + "/{controller}/{action}/{id?}" does not match even though the request url has the foldername segment Commented Jul 25, 2015 at 19:33
  • so it seems that app.When is almost equivalent to marking a sub folder of a site as an application in IIS. It really does need its own routes relative to the path we mapped on. The result is also similar as in IIS apps because ~/ now resolves to the mapped folder, that is if my _Layout.cshtml has this: <link rel="stylesheet" href="~/js/lib/bootstrap/dist/css/bootstrap.css" /> it resolves the url as appfolder/js/lib....etc. which is the same thing that happens in an IIS child app Commented Jul 25, 2015 at 19:48

1 Answer 1

2

That's exactly how Map worked in Katana and is supposed to work in ASP.NET 5.

The most important thing to note with Map is that it always terminates the request when the request path corresponds to the registered path. In other words, the middleware registered after a app.Map call are never executed when the path matches the path parameter, which is why your app.UseMvc middleware is never executed in your configuration.

Since you don't handle the request in the app.Map delegate, the default handler is executed and applies a 404 response.

You should give this extension method a try. Unlike app.Map, it never stops processing the request and basically enables what I name "conditional middleware processing" scenarios:

public static IApplicationBuilder UseWhen(
    [NotNull] this IApplicationBuilder app,
    [NotNull] Func<HttpContext, bool> condition,
    [NotNull] Action<IApplicationBuilder> configuration) {
    var builder = app.New();
    configuration(builder);

    return app.Use(next => {
        builder.Run(next);

        var branch = builder.Build();

        return context => {
            if (condition(context)) {
                return branch(context);
            }

            return next(context);
        };
    });
}

It was specifically developed for an AspNet.Security.OpenIdConnect.Server sample, but you can of course use it everywhere.

You can see it in action there: https://github.com/aspnet-contrib/AspNet.Security.OpenIdConnect.Server/blob/vNext/samples/Mvc/Mvc.Server/Startup.cs#L46-L80

// Create a new branch where the registered middleware will be executed only for API calls.
app.UseWhen(context => context.Request.Path.StartsWithSegments(new PathString("/api")), branch => {
    branch.UseOAuthBearerAuthentication(options => {
        options.AutomaticAuthentication = true;
        options.Audience = "http://localhost:54540/";
        options.Authority = "http://localhost:54540/";
    });
});
Sign up to request clarification or add additional context in comments.

15 Comments

I managed to solve all the issues with using Map from my above post as noted in my comments above. It seems different to me than MVC 5 since I'm porting an app from MVC 5 with very similar code. In MVC 5 the routes I added on the main app still worked but they did not work for me with asp.net 5 and I had to add routes to the branch that are relative to the branch folder I map on. By doing it that way my nav links automatically are updated to include the folder like /folder/Home/About which is nice but css and js also get resolved to folder/realpath/file.css which is not what I want.
what namespace has the app.UseWhen extension? or do I need to add that to my own code to try it?
It worked with ASP.NET MVC 5 because MVC was not a middleware and was not executed by your Katana pipeline but directly by ASP.NET/IIS: that's why the app.Map returned a 404 response had no effect on MVC 5. Concerning the extension, you need to add it to your own code, so you're free to choose the most appropriate namespace.
what namespace do I need for those [NotNull] attributes?
This looks to be exactly what I need since MapWhen doesn't execute code after the MapWhen declaration. Unfortunately, I'm working with an older Web API that is not ready to be upgraded yet, and this .NET Core example won't work in the classic OWIN implementations. Do you know where I can find a version of this snippet that'll work with Classic ASP.NET OWIN?
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.