MVC3 Route/redirect problem - c#

My application is multilingual and I wrote the following route in order to handle the languages:
routes.MapRoute(
"Default", // Route name
"{language}/{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index",
language = "pt", id = UrlParameter.Optional }, // Parameter defaults
new { language = #"(pt)|(es)|(en)" }
);
This works for domain.com and domain.com/pt/home/index. However, if I type domain.com/home/index it fails (404).
The desired behavior would be it being redirected to domain.com/pt/home/index (pt is the default language).
Whats the best way to achieve this? I've been reading a lot about routes and ActionFilters but nothing seems quite right.

i would suggest using custom route handler like following
public class LanguageRouteHandler : IRouteHandler
{
public IHttpHandler GetHttpHandler(RequestContext requestContext)
{
IRouteHandler handler = new MvcRouteHandler();
var vals = requestContext.RouteData.Values;
if(vals["language"] == null)
{
vals["language"] = "pt";
}
return handler.GetHttpHandler(requestContext);
}
}
and have another route without language route value and set its route handler (global.asax)
routes.MapRoute(
"Default2", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
).RouteHandler = new LanguageRouteHandler();
This will not redirect Home/index to pt/home/index yet it will provide language = "pt" to your index action method (and all others). if you want to redirect you can implement an actionfilter but redirecting will create problems with post requests. For example when you post a form to /home/index and suppose it is redirected by action filter, the redirected request will lose posted form data

You need two routes, the other one without the language, or add the language parameter at the end

Try this
routes.MapRoute(
"Default", // Route name
"{language}/{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional }, // Parameter defaults
new { language = #"(pt)|(es)|(en)" }
);
and modify your action in this way
public ActionResult Index([DefaultValue("pt")] string language)
{
...
}

Related

Tenant area route with default route

So I have a site that has basically an 'area' per tenant. so it will show up as www.site.com/ and that will go to that groups page using an area.
Thing is I also have a default route for outside the area so you can go to www.site.com/ which will take you to the actual ~/Views/Home/Index page. However if you try to type www.site.com/Home/Index or say the page to create a new group www.site.com/Group/Create it thinks it needs to go to the area which that doesn't exist and gives the 404 resource cannot be found.
Here is the default route in the RouteConfig.cs
routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
new[] { "TicketSystem.Controllers" }
);
Here is the route config for the area:
context.MapRoute(
"Group_default",
"{group}/{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
new[] { "TicketSystem.Areas.Group.Controllers" });
so the {group} is whatever group you are currently visiting and then it goes to the regular controller/action for that group. However for the default route it still seems to go to the area route instead no matter what.
I was thinking that there could be a fallback. So when it tries to go to the area and it can't find the correct controller/action it will check the default route next. If it still can't find anything it will give the 404 error resource cannot be found. Though I am not exactly sure how to do this.
So to make www.site.com/ to work and allow www.site.com/Home/Index to work.
The problem is, When you try to access /Home/Index The route engine does not know by "Home" , you meant the controller name or a groupName!
To solve this, you can create a custom route constraint which checks whether the group value in the request url is a valid controller name in your app. If yes, The request won't be handled by the area route registration definition.
public class GroupNameConstraint : IRouteConstraint
{
public bool Match(HttpContextBase httpContext, Route route, string parameterName,
RouteValueDictionary values, RouteDirection routeDirection)
{
var asm = Assembly.GetExecutingAssembly();
//Get all the controller names
var controllerTypes = (from t in asm.GetExportedTypes()
where typeof(IController).IsAssignableFrom(t)
select t.Name.Replace("Controller", ""));
var groupName = values["group"];
if (groupName != null)
{
if (controllerTypes.Any(x => x.Equals(groupName.ToString(),
StringComparison.OrdinalIgnoreCase)))
{
return false;
}
}
return true;
}
}
Register this constraint when you register your area route.
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"Group_default",
"{group}/{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
new { anything = new GroupNameConstraint() }
);
}
This should work assuming you will never have a groupName same as your controller name (Ex : Home )

MVC ActionLink appending route as querystring rather than route values

I have an MVC Action Link:
#Html.ActionLink("Update Information", "Index", "Performance",
new { performanceid = item.PerformanceId }, null)
This action link's href looks like this: /Performance/Index?performanceid=100
In my RouteConfig.cs I have the following routes in the following order:
routes.MapRoute(
"ShowPerformanceOptions",
"Performance/{performanceid}/Index",
new { controller = "Peformance", action = "Index" }
);
routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
I do not want a querystring added to the end of the URL, I would instead like the URL to look like this: /Performance/360/Index
I have been through a variety of different options including adding the route parameters and optional url parameters and changing the way I write my ActionLink. Nothing seems to work.
Any ideas anyone?
To generate URL based on route name, use Html.RouteLink() method
#Html.RouteLink("Update Information", "ShowPerformanceOptions", new { performanceid = item.PerformanceId })
A good read What's the difference between RouteLink and ActionLink in ASP.NET MVC?
As #Satpal pointed out, the ActionLink wasn't working because of the typo in the route itself:
routes.MapRoute(
"ShowPerformanceOptions",
"Performance/{performanceid}/Index",
new { controller = "**Peformance**", action = "Index" }
);
routes.MapRoute(
"ShowPerformanceOptions",
"Performance/{performanceid}/Index",
new { controller = "**Performance**", action = "Index" }
);

Parameter only route always being triggered MVC3

I have two very simple routes
routes.MapRoute(
"post", // Route name
postPage + "/{slug}", // URL with parameters
new { controller = "Home", action = "Article" } // Parameter defaults
);
routes.MapRoute(
"page", // Route name
"{slug}", // URL with parameters
new { controller = "Home", action = "Page", slug = homePage} // Parameter defaults
);
And here is my controller logic
public ActionResult Article(string slug)
{
return View(repo.GetPost(slug));
}
public ActionResult Page(string slug)
{
if (slug.ToLower() == MetaData.PostsPage.ToLower())
return View("listPosts", repo.GetAllPosts());
else
return View("page", repo.GetPage(slug));
}
homePage and postPage are set from value's in the database. Allowing the user to define the default page as well as the page to show posts.
My issue occurs when adding an area named "Admin". I get a controller added to my RouteTable
context.MapRoute(
"Admin_default",
"Admin/{controller}/{action}/{id}",
new { action = "Index", id = UrlParameter.Optional }
);
Now when a user Access Admin/Account/Logon the page loads fine, but my debugger still tries to go into the Home controller and the Page action. But the RouteDebugger says it doesn't match the current request. I'm puzzled on how to fix this.
RouteDebugger screenshot: http://i.stack.imgur.com/7cpHm.png
Debugger going into my HomeControler Page AtionResult: http://i.stack.imgur.com/uSJBK.png
Actually the problem is, Area routes are overriding the global routes, to distinguish both the routes set the relevant namespace of area's controller in the context.MapRoute method in adminAreaRegistraton.cs file. i.e.
context.MapRoute(
"admin_default",
"admin/{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
null,
new string[] { "MVCApplication1.Areas.admin.Controllers" }
);
I found out the issue.
I had a favicon.ico set in the main area of my site, but not the Admin area.
So when I went to the Admin area the browser made a request for favicon.ico that got picked up by that route. Thats why my routes looked fine in the RouteDebugger, because they were.
Thanks for the help Kundan!

MVC Razor routing suggestion

I have a web site with something like this:
http://website/Controller/Action/Id
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Controller", action = "Action", id = UrlParameter.Optional } // Parameter defaults
);
Is there any way to route to the same action if the url changes to be
http://website/Action/Id
I was trying to change global.asax but nothing is working.
Based on how I'm reading your question, you're saying that you may only ever have one controller and want to be able to route all actions to that single controller?
If I'm reading it correctly, you should be able to do something similar to this:
routes.MapRoute(
"Default", // Route name
"{action}/{id}", // URL with parameters
new { controller = "Controller", action = "Action", id = UrlParameter.Optional } // Parameter defaults
);

Why is the wrong route being used?

I am trying to do what SO does for its Question controller.
/Posts/{id}/{title} when viewing a post (action name not shown)
/Posts/New when you are posting something new.
/Posts/Delete/10 etc....
I have two routes set up (well, one if you don't count the default). What appears to be happening is all actions in the Post controller are being routed through the first one.
What is that? I obviously have it wrong, but I can't figure this out.
routes.MapRoute("ViewPosts",
"Posts/{postid}/{title}",
new { controller = "Posts", action = "View", postid = "", title = "" });
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
The first route handles all your requests which starts from /Posts.
You need to use constraints to allow {postid} be only number:
routes.MapRoute("ViewPosts",
"Posts/{postid}/{title}",
new { controller = "Posts", action = "View", postid = "", title = "" },
new { postid= #"\d+" });
In this case only if numeric Id is provided this route will handle it, otherwise "Default" route will handle.
All routes are going through the first because you have not specified that the postid field can only be numeric, or defined an earlier route that will catch /Posts/New. It is passing New as the postid with the View action.
You can add this route definition before the ones you have now:
routes.MapRoute("NewPost",
"Posts/New",
new{controller="Posts", action="New"});
Or whatever the appropriate controller/action would be.

Categories