Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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?

like image 255
Andrew Murphy Avatar asked Oct 23 '14 13:10

Andrew Murphy


2 Answers

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"] })
like image 171
Chris Pratt Avatar answered Nov 14 '22 01:11

Chris Pratt


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).

like image 22
Andrew Murphy Avatar answered Nov 13 '22 23:11

Andrew Murphy