ASP.Net MVC 4 w/ AttributeRouting and multiple RoutePrefix attributes

12,241

Solution 1

I ended up finding a solution to this

I just overrided the default routes to include this. ASP.Net automatically keeps the usertype value and puts it back in when it regenerates the routes

const string userTypeRegex = "^(full|lite)$";
routes.Add("Default", new Route("{usertype}/{controller}/{action}/{id}",
            new { controller = "Sessions", action = "Login", id = UrlParameter.Optional }, new { usertype = userTypeRegex }));

I found that this didn't work with the Route or RoutePrefix attributes, and so I had to remove all of them. Forcing me to add specific routes in these cases

routes.Add("Profile-Simple", new Route("{usertype}/profile/simple",
            new { controller = "ProfileSimple", action = "Index" }, new { usertype = userTypeRegex }));

I thought that a half-dozen hard coded routes in my RouteConfig file was a better solution than having to manually add values to each place I generated a URL (as in Chris's solution).

Solution 2

How about something like:

[RoutePrefix("{version:regex(^full|lite$)}")]

Then, when you create your links:

@Url.RouteUrl("SomeRoute", new { version = "full" })

Or

@Url.RouteUrl("SomeRoute", new { version = "lite" })

You could even do the following to just keep whatever was already set:

@Url.RouteUrl("SomeRoute", new { version = Request["version"] })
Share:
12,241
Andrew Murphy
Author by

Andrew Murphy

Updated on June 19, 2022

Comments

  • Andrew Murphy
    Andrew Murphy almost 2 years

    TL;DR

    I need a way to programtically choose which RoutePrefix is chosen when generating URLs based on the properties of a user in my MVC app

    Not TL;DR

    I have an MVC 4 app (with the AttributeRouting NuGet package)

    Due to the requirements of the hosting environment I have to have two routes for a lot of my actions so that the hosting environment can have different permissions for access.

    I am solving this by decorating my controller with with [RoutePrefix("full")] [RoutePrefix("lite)]. which allows each action method to be accessed via /full/{action} and /lite/{action}.

    This works perfectly.

    [RoutePrefix("full")]
    [RoutePrefix("lite")]
    public class ResultsController : BaseController
    {
        // Can be accessed via /full/results/your-results and /lite/results/your-results and 
        [Route("results/your-results")]              
        public async Task<ActionResult> All()
        {
        }
    
    }
    

    However, each user should only use either full or lite in their urls, which is determined by some properties of that user.

    Obviously when I use RedirectToAction() or @Html.ActionLink() it will just choose the first available route and won't keep the "correct" prefix.

    I figured I can override the RedirectToAction() method as well as add my own version of @Html.ActionLink() methods.

    This will work, but it will involve some nasty code for me to generate the URLs because all I get is a string representing the action and controllers, but not the reflected types. Also there might be route attributes such as in my example, so I am going to have to replicated a lot of MVCs built in code.

    Is there a better solution to the problem I am trying to solve?

    • John
      John over 9 years
      I'm confused, is there any difference in what those routes are actually providing or just so the url looks different but the same data is returned to the user? Are you using the url to check for authentication?
    • Andrew Murphy
      Andrew Murphy over 9 years
      The URL needs to look different but the same data is returned to the user. There's an external authentication system for the two different types of user that requires "lite" users to have all their URLs prefixed with /lite/ and "full" users to have all their URLs prefixed with "full"
    • Samuele
      Samuele over 9 years
      I'm not sure if it would work (typing from my tablet atm), but you could try subclassing your ResultsController to FullResultsController and LiteResultsController, and apply the relative RoutePrefix attributes to both. No need to duplicate any code.
    • John
      John over 9 years
      I'm hoping thats not the only authentication that is happening or it would be very easy (though it does seem pointless) for the user to change from lite to full. You might want to see about using re-write rules or something or like you mentioned wrap the route change methods with your own that does the replacement after creating the route.
    • Andrew Murphy
      Andrew Murphy over 9 years
      @John No, there is external authentication. It's just for that to work it requires my URLs to be structured as I mentioned
    • Andrew Murphy
      Andrew Murphy over 9 years
      @Samuele I'm not duplicating code. The solution I have allows the correct URLs to access the correct methods. It's more about the generation of Url.Action and RedirectToAction values