Problem with globalization of ASP.NET MVC routes - asp.net

I tried to use the solution explained at http://weblogs.asp.net/paulomorgado/archive/2010/01/31/web-site-globalization-with-asp-net-routing.aspx to localize my application using the language parameter in my routes.
Here's the code I have in my Global.asax:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.Add("en", new Route("en/{*path}", new GlobalizationRouteHandler(CultureInfo.GetCultureInfo("en-US"))));
routes.Add("fa", new Route("fa/{*path}", new GlobalizationRouteHandler(CultureInfo.GetCultureInfo("fa-IR"))));
routes.MapRoute(
"AdminHome",
"{language}/admin",
new { controller = "Admin", action = "Index" }
);
}
But when I point my browser to /en/admin or /fa/admin I receive a 404 error message.
I tried this one too:
routes.MapRoute(
"AdminHome",
"admin",
new { controller = "Admin", action = "Index" }
);
But still a 404 error for /en/admin - (in this case "/admin" works.)
Any idea?

I have a very similar route pattern in my own MVC site.
routes.MapRoute(
"BlogSpecific", // Route name
"{blogSubFolder}/{controller}/{action}", // URL with parameters
new { blogSubFolder = "", controller = "", action = "Index" } // Parameter defaults
);
The two main differences that I can see are that I specify the {action} in my route, and I also call out the first route param as a parameter in my object ("blogSubFolder = "",").
Now I just did some testing, and I found the same behavior that you are seeing, I take out the {action} out of my route and I get a 404. But if I specify the action everything works out.
Ok, so I created a new project, with the default route, and I don't have to specify the action, it defaults to Index just like I'd expect it to. I then add a new route where I specify the controller {language}/Foo/{action}, and I continue to get errors if I don't include the index in my url. Long story short, As near as I can tell if your route has a variable that precedes the controller you have to specify the action in your url.

Related

How to create wildcard controller in ASP.NET MVC?

In a default application ASP.NET MVC 3.0 app I have a controller with Index() and Details(string id) methods. These map to /orders/ and orders/details/ORDER_ID_HERE. How do I change the routing so that Details maps to orders/ORDER_ID_HERE, and continue to have Index() serve up a default index page?
If you would add a new route to you route collection like:
routes.MapRoute(
"Detailed", // Route name
"{contoller}/{id}", // URL with parameters
new { controller = "Orders", action = "Details", id = UrlParameter.Optional } // Parameter defaults
);
It would work, but you break a lot of things, a url like [site]/Home/Search for instance would result in a:
System.ArgumentException: The parameters dictionary contains a null entry for parameter 'id' of non-nullable type 'System.Int32' for method 'System.Web.Mvc.ActionResult Details(Int32)' in 'MvcApplication3.Controllers.OrdersController'.
The reason for this is that /Home/Search matches the route {controller}/{id}.
An other, similar way to solve it would be:
routes.MapRoute(
"Detailed", // Route name
"order/{id}", // URL with parameters
new { controller = "Orders", action = "Details", id = UrlParameter.Optional } // Parameter defaults
);
Make sure that the static part in the route, order, does not match the name of your actual orders controller, otherwise you'll have the same problem, rendering [url]/orders unreachable with an System.ArgumentException instead of calling the Index() method (the default route)
The negative side of this approach is that you'll need to do this for every controller where you would like to have such functionality.
Try adding this route before the default route:
routes.MapRoute("SomeName", "{controller}/{id}", new { controller = "Orders", action = "Details" });
Notice how the {action} has been removed from the URL parameter.

ActionLink 404 Error

Building my first ASP.NET MVC 3 application and trying to implement the ability to disassociate a given ice cream from a menu. Each has an integer identifier associated with it and on a page I display these mappings and provide a link by them to remove the ice cream from the menu.
I've got an ActionLink that looks like this:
#Html.ActionLink("Remove", "RemoveMenuIceCreamMapping", "IceCream", new { iceCreamId=item.IceCreamId, menuId=item.MenuId}, null)
In my IceCreamController I've got an Action that looks like this:
[HttpPost]
public PartialViewResult RemoveMenuIceCreamMapping(int iceCreamId, int menuId)
{
...
}
Did a little searching and believe I may need to modify the routes in the Global.asax.cs file's RegisterRoutes to handle these two parameters. So I tried this like so:
public static void RegisterRoutes(RoutesCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
// I added this route in an attempt to handle the two parameters:
routes.MapRoute(
"RemoveMenuIceCreamMapping", // Route name
"IceCream/RemoveMenuIceCreamMapping/{iceCreamId}/{menuId}", // URLwith parameters
new
{
controller = "IceCream",
action = "RemoveMenuIceCreamMapping",
iceCreamId = UrlParameter.Optional,
menuId = UrlParameter.Optional
}
);
// this was there by default
routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new {controller = "Home", action = "Index", id = UrlParameter.Optional }
};
}
But this doesn't work - I get a "The resource cannot be found." error, 404. Requested URL: /IceCream/RemoveMenuIceCreamMapping/1/10
1 is the Id of the IceCream and 10 is the menu's Id.
What I was expecting to happen was that the action RemoveMenuIceCreamMapping would get called, passing those two parameters, but I'm obviously not doing something right here and may just misunderstand how to accomplish what I want and be going about this the wrong way. Any guidance would be most appreciated.
Update
So, one more thing I've learned, after reading this SO question, my ActionLink isn't triggering a POST so removing the [HttpPost] from the action seemed like the right thing to do. And, in fact, as soon as I did that, the route was found and the action executed.
I think you problem is that the ActionLink uses an HTTP GET and you are only accepting HTTP POST.
You will probably need to change your view to issue an HTTP POST (e.g. with a regular HTML button inside a form) so that the verb that the browser sends matches with what you accept on the controller.

Am I approaching routes in my application wrong?

I'm working on my first ASP.NET MVC 3 application and I've got a couple of actions defined to handle adding/removing an ice cream from a menu. They look like so:
[HttpPost]
public PartialViewResult AddMenuIceCreamMapping(int iceCreamId, int menuId)
[HttpPost]
public PartialViewResult RemoveMenuIceCreamMapping(int iceCreamId, int menuId)
In order for these actions to not result in a 404 error, I went into the Global.asax.cs file and added the following:
routes.MapRoute(
"AddMenuIceCreamMapping", // Route name
"IceCream/AddMenuIceCreamMapping/{iceCreamId}/{menuId}", // URLwith parameters
new
{
controller = "IceCream",
action = "AddMenuIceCreamMapping",
iceCreamId = UrlParameter.Optional,
menuId = UrlParameter.Optional
}
);
routes.MapRoute(
"RemoveMenuIceCreamMapping", // Route name
"IceCream/RemoveMenuIceCreamMapping/{iceCreamId}/{menuId}", // URLwith parameters
new
{
controller = "IceCream",
action = "RemoveMenuIceCreamMapping",
iceCreamId = UrlParameter.Optional,
menuId = UrlParameter.Optional
}
);
and those work, meaning I can click an Add or Remove button on my page and add or remove an ice cream/menu mapping. Great. But I'm expecting that there will be more situations like this and I can see this routes container having more entries such as these. And to my novice web programmer eye, it seems a bit clunky and I begin to think that perhaps I'm going about this the wrong way. Am I? Is there a better way to approach this so that I don't end up doing the "go create some action that takes N parameters and then go add a route" thing. Any suggestions here?
You can use placeholders in your mappings to deal with this... Instead of what you've got above, you could use this (note that I don't specify a default):
routes.MapRoute(
"TwoParameterRoute", // Route name
"{controller}/{action}/{id1}/{id2}", // URL with parameters
);
And if you have to add a route that takes three parameters, you extend the idea:
routes.MapRoute(
"ThreeParameterRoute", // Route name
"{controller}/{action}/{id1}/{id2}/{id3}" // URL with parameters
);
In a controller for the two parameter route, the code would look like this (for a contrived sample):
public ActionResult Index(int id1, int id2)
{
ViewData["id1"] = id1;
ViewData["id2"] = id2;
return View();
}
One final thing to note is that you can change the naming to match a pattern you might have. In the first example, instead of id1 and id2 you might have entityId and relatedId or something similar.
You know that you can also use querystring parameters even with POST requests? Using just the default route (that comes with new ASP.NET MVC projects), you can reach your actions with /IceCream/AddMenuIceCreamMapping/?iceCreamId=1&menuId=1 and /IceCream/RemoveMenuIceCreamMapping/?iceCreamId=1&menuId=1.
Using querystringparameters also show that this url is a parameterized method call, and not a reference to a resource, as the original URLs would indicate.
If your set of CRUD actions will be repeating with every custom Entity that you introduce in your application, then you can have the controller as a parameter too. As it is now, you have controller fixed. Also I would avoid such long action names. I would also avoid spelling Icecream as IceCream. Just coz of its looks.
Probably you should have a controller Menu, which handles Icecreams. It kinda doesn't make sense that Icecream handles its own things because it is a part of something else.
Such as
routes.MapRoute(
"RemoveIcecreamFromMenu", // Route name
"menu/remove-icecream/{iceCreamId}/{menuId}", // URLwith parameters
new
{
controller = "Menu",
action = "RemoveIcecream",
iceCreamId = UrlParameter.Optional, // i don't think those two should be optional
menuId = UrlParameter.Optional
}
);
But it makes sense for an Icecream controller to handle its own CRUD:
routes.MapRoute(
"CreateIcecream", // Route name
"icecream/create", // URLwith parameters
new
{
controller = "Icecream",
action = "Create"
}
);
routes.MapRoute(
"DeleteIcecream", // Route name
"icecream/delete/{id}", // URLwith parameters
new
{
controller = "Icecream",
action = "Delete",
id = #"\d+" // this tells the routing engine only integer are accepted
}
);
I would go with something that had descriptive urls and no default route:
routes.MapRoute(
"IceCreamEdit", // Route name
"{controller}/{action}/icecream/{iceCreamId}/menu/{menuId}" // URLwith parameters
);

where to use route-name of routing in aspnet mvc

i'm new to routing in aspnet mvc.. i have following code:
Action Controller
public ActionResult SchoolIndex()
{
return View(SchoolRepository.GetAllSchools());
}
here is the routing
routes.MapRoute(
"School", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "School", action = "SchoolIndex", id = "" } ); // Parameter defaults
when i enter "localhost/school" in addressbar, it is giving 404 error instead it should route to my "schoolIndex" action
i have given route-name as "School" where it is used ?
You can't specify a route name in a URI, ever.
Route names are for, e.g., Html.RouteLink(). RouteLink allows you to specify exactly the route you want, even if it's not the first matching route for the arguments you pass.
URIs are matched in order. The first matching route wins.
Read my post on routing for much more detail.

ASP.NET MVC 2 controller-url problems

I am still very new to the MVC framework, but I managed to create a controller that reads from a database and writes JSON to an url;
host.com/Controllername?minValue=something&maxValue=something
However when I move the site to a subfolder;
host.com/mvc/
it doesn't seem to be able to call the controller from there when I do it like this;
host.com/mvc/Controllername?minValue=something&maxValue=something
Did I forget to do something somewhere to make this url call valid from that subfolder?
Any help here would be greatly appreciated.
In the first case you are specifying the controller name while in the second case you are not. You could setup a default route:
routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new
{
controller = "Controllername",
action = "ActionName",
id = UrlParameter.Optional
}
);
Once this default route points to the controller and action both urls should work:
host.com/?minValue=something&maxValue=something
host.com/mvc/?minValue=something&maxValue=something

Resources