mvc routes to share the same action? - c#

I have the following two routes defined in my MVC app.;-
At the moment I have two "MVC View content pages" defined
/ShowName/NameById
/ShowName/Index
However the content on these two pages are identical? Is it possible for two routes to share the same content page? If not then can I a) create a single rule for both routes or b) should I create a usercontrol to share between both content pages to show my data?
routes.MapRoute(
"NameById",
"Name/{theName}/{nameId}",
new
{
action = "NameById",
controller = "ShowName",
theName = "Charley"
}
,new { nameId = #"\d+" }
);
routes.MapRoute(
"ShowName",
"Name/{theName}",
new
{
action = "Index",
controller = "ShowName",
theName = "Charley"
}
);
EDIT
I have read the answers below and I have the following action result methods. If I remove one of the methods (e.g. the Index) then how would I rewrite my routes to a single route?
public ActionResult Index(string theName)
public ActionResult NameById(string theName, int? nameId)
So the following works url's work?
/Name/Charley
/Name/Charley/11234

You could create a partial view for the detail area of the page, keeping the separation of the two actions in case they change at a later point. Or you could just
return View("DetailView", model);
But that can introduce an extra string to manage between the two controller actions. Since MVC does not support overloading by action name (unless you have a GET/POST pair, one with no arguments), you could just check the {nameId} parameter and see if it is empty/null before using it.

Do you really need 2 different routes? You could make the pattern for your Index route
Name/{theName}/{nameId}
and make nameId a nullable input to your action. Then just add some logic to your action which checks whether nameId has a value and acts accordingly.

Related

MVC Routing: How to map two names to one action

I'd like to have one route that gives the option of two urls but maps to one action. A good example would be for multilingual application. Lets take english and french for example.
This seems simple at first, technically you can do:
routes.MapRoute(
"the hi route english" ,
"welcome/sayhi/{id}" ,
new { controller = "Welcome" , action = "SayHi" , id = UrlParameter.Optional }
);
routes.MapRoute(
"the hi route french" ,
"bienvenu/direallo/{id}" ,
new { controller = "Welcome" , action = "SayHi" , id = UrlParameter.Optional }
);
But that means that you'll have to define two routes for every action. Or a little better solution, create a custom Route class that takes more params to handle bilingualism.
If I go option 1 or 2, It means I have to define every single routes of the WelcomeController because I cannot use {action} in my route.
Ideally, i'd like to be able to define at least action name via metadata and then grab it via reflection or something.
i.e.:
[ActionName( { "fr", "direallo" }, {"en", "sayhi"})]
public ActionResult SayHi(string id){
//check current thread culture...
}
I am not quite sure where to starts, any ideas? Tips?
Thank you,
You have several options starting points here, roughly they are (in order of implementation complexity):
A route per language (as you outlined above)
A regex route constraint e.g.
routes.MapRoute(
"the hi route",
"{controllerName}/{actionName}/{id}",
new { controller = "Welcome" , action = "SayHi" , id = UrlParameter.Optional },
new { controllerName = #"welcome|bienvenu", actionName = #"sayhi|direallo" }
);
You could create a base controller, which is inherited by a subclass per language and define a language specific action name for each base controller action method
You could create your own (or use the one provided in the answer to the comment by Justin Pihony) custom routing constraint

MVC special routing

I have an asp.net MVC project where i need to define some custom routes. Similar to what you see for posts on Wordpress where the route is of the form postid-postname:
12-i-am-post
I know how to do something like postid/postname:
12/i-am-post.
But how do I make a route that combines the two, such as:
mywebsite.com/12-postname-is-her
routes.MapRoute(
"PostRoute", // Route name
"{controller}/{id}-{postName}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional, postName = UrlParameter.Optional } // Parameter defaults
);
public ActionResult Index(int id, string postName)
{
return View();
}
Should work for the following request http://localhost/Post/1-MyPostName
Since there are multiple hyphens you won't be able to use it as a delimiter/separator, unless you want it to always match the first occurrence. Your best bet is to do a catchall {*path} and parse the value in your controller.

Action renders View of the first called Action (when an Action calls another Action)

The name is pretty confusing probably. I have a requirement where a URL must be name friendly to represent dates (schedule/today, schedule/tomorrow etc). I don't want to clutter my route mappings with DateTime.Now, DateTime.Now.AddDays(1) etc for different parameters so I decided to create routes that map to an action of the same name:
routes.MapRoute(RouteNames.ScheduleToday, "schedule/today", new { controller = "Schedule", action = "Today" });
routes.MapRoute(RouteNames.ScheduleTomorrow, "schedule/tomorrow", new { controller = "Schedule", action = "Tomorrow" });
The idea for the actions is that I'd like to be able to call the Today() action but actually call the List(DateTime date) action with, for example, DateTime.Now as the date parameter.
This works great like this:
public ActionResult Today()
{
return this.List(DateTime.Now);
}
public ViewResult List(DateTime date)
{
this.ViewData["Date"] = date;
return this.View("List");
}
I'd like to be able to call this.View() instead of this.View("List"). Is this possible other than what I've posted above? It seems as though the view that is rendered matches the name of the first action that is called since the only way to get this to work is to explicitly render the List view.
I'm not aware of any way to make the parameterless View() return a view other than the one that matches the name of the first action method. But what about this approach to solve your issue without putting DateTime.Now in your route mappings - if you define your route mappings like this:
routes.MapRoute(RouteNames.ScheduleToday, "schedule/today", new { controller = "Schedule", action = "List", identifier = "today" });
routes.MapRoute(RouteNames.ScheduleTomorrow, "schedule/tomorrow", new { controller = "Schedule", action = "List", identifier = "tomorrow" });
Here we've introduced a new route token called "identifier" which matches what you have in the route. The other thing you could do is define a single route like this:
routes.MapRoute(RouteNames.ScheduleToday, "schedule/{identifier}", new { controller = "Schedule", action = "List" });
But in that case, you'd want a route constraint to constrain your {identifier} token to only your valid values that you support. With these routes in place, you can simply create a custom ActionFilterAttribute which has the single responsibility for setting up dates.
Something like this:
public class DateSelectorAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var identifier = filterContext.RouteData.Values["identifier"] as string;
switch (identifier)
{
case "today":
filterContext.ActionParameters["date"] = DateTime.Now;
break;
case "tomorrow":
filterContext.ActionParameters["date"] = DateTime.Now.AddDays(1);
break;
}
}
}
Now your List() method can just look like this:
[DateSelector]
public ActionResult List(DateTime date)
{
this.ViewData.Model = date;
return this.View();
}
And as a side note, I realized setting up the DateTime.Now in the routes wouldn't work anyway because that would only get called at application start up, effectively caching the date values. The action filter is a better approach because it gets called real-time and gives you the accurate date.
Hope this helps.
What you are doing is wrong in the way you redirect to another controller action from Today(). You should be using one of the RedirectToAction() overloads. That way you won't have to specify a View in the List() action. And you can provide DateTime as a route value to RedirectToAction().
I still can't find why the first action that is called matches the view and not the last action (perhaps I'll dig into the source). For the time being I'll stick with what I have since there's no reason to over-complicate things.

How this controller can get the values it needs?

Say I have set up a url structure as follows (ASP.NET MVC2)
http://localhost:XXXX/Product/
Click on link browse by color
http://localhost:XXXX/Product/Color/
Click on link browse red color items by type (i.e. pen's)
http://localhost:XXXX/Product/Color/Red/Pen
In the controller, I will need to do a select based on these criteria. Except when previously, I could go
public ActionResult ShowTypesForColor(string color)
but to do this one:
public ActionResult ShowItems(string type)
I also need the color that was selected.
How could I do this? Is splitting up the url string the only way?
edit: maybe i've gotten ahead of myself in the global.asax.cs
routes.MapRoute(null, "Product/Color/", new { controller = "Product", action = "ShowAllColors" });
routes.MapRoute(null, "Product/Color/{color}", new { controller = "Product", action = "ShowTypesForColor" });
routes.MapRoute(null, "Product/Color/{color}/{type}", new { controller = "Product", action = "ShowDetail" });
I don't think I can define the last one like that can I? with two {} values?
Your last route seems perfectly valid. It will map to action with signature like this:
ActionResult ShowDetails(string color, string type) {
return View(/*view params*/);
}
EDIT I think the order is wrong, so if the last route is not being fired, try doing this:
routes.MapRoute(null, "Product/Color/{color}/{type}", new { controller = "Product", action = "ShowDetail" });
routes.MapRoute(null, "Product/Color/{color}", new { controller = "Product", action = "ShowTypesForColor" });
routes.MapRoute(null, "Product/Color/", new { controller = "Product", action = "ShowAllColors" });
The order of MVC routes should be from most specific to least specific, otherwise the least specific route (/product/color/{color}) will match a url product/color/red/pen before the more specific /product/color/{color}/{type}
You can put multiple tokens in your route (e.g., {color} and {type}) but it's not going the work the way you have it there. Why have you defined "Color" as the second segment of your URL? Why not just do /Products/Red and /Products/Red/Pen? It's inconsistent to do .../Colors/Red and not .../Types/Pen so I'd just leave the "Colors" and "Types" qualifiers out altogether.
I'd define your ShowItems() method like this:
public ActionResult ShowItems(string color, string type)
this will allow you to have a route like /Products/Red/Pen where your route maps to this ShowItems() method. But you'll still need to differentiate that from the ShowTypesForColor() method where it also takes a first parameter of color. The routing framework will just treat type as null - for the route that has both tokens, make sure you have a route constraint specifying that neither color nor type can be null/empty (i.e., for the ShowItems() route).

MVC Custom Routing

I'm new to MVC. I'm having trouble trying to accomplish having a route setup in the following manner:
System/{systemName}/{action}
Where systemName is dynamic and does not have a "static" method that it calls. i.e.
http://sitename.com/Systems/LivingRoom/View
I want the above URL to call a method such as,
public void RouteSystem(string systemName, string action)
{
// perform redirection here.
}
Anyone know how to accomplish this?
routes.MapRoute(
"Systems_Default",
"System/{systemName}/{action}",
new { controller="System", action = "RouteSystem", systemName="" }
);
Should route your request as you specified.
Note that with the above route, your Url should be:
http://sitename.com/System/LivingRoom/View
I had a similar problem. I used the following route.
routes.MapRoute(
"BlogSpecific", // Route name
"{blogSubFolder}/{controller}/{action}/{id}", // URL with parameters
new { blogSubFolder = "", controller = "", action = "", id = "" } // Parameter defaults
);
blogSubFolder just gets passed to my controller actions as a parameter. Controller and action all work just as they usually do. Just swap out blogSubfolder with your "System" paramter and that will hopefully work for you.
It looks like you intend to route to a controller based on the system name. If that is correct then you would simply need this:
routes.MapRoute("Systems",
"Systems/{controller}/{action}"
new{controller = "YourDEFAULTController", action = "YourDEFAULTAction"});
Please note that the third line only sets the default values specified if they are NOT included in the url.
Given the route above, MVC would route this:
http://sitename.com/Systems/LivingRoom/View
to the View action method on the LivingRoom controller. However this:
http://sitename.com/Systems/LivingRoom
would route to the YourDEFAULTAction method on the LivingRoom controller.

Categories

Resources