Wrong route selected in ASP.NET MVC 3 - asp.net-mvc-3

Could please someone shed some light into this issue, it drives me crazy!
The routes:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"DefaultOrderingRoute", // Route name
"{controller}/{action}/{id}/{slug}", // URL with parameters
new { controller = "Order", slug = UrlParameter.Optional }, // Parameter defaults
new { controller = "^Order$" }
);
routes.MapRoute(
"DefaultImageRoute", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Image", }, // Parameter defaults
new { controller = "^Image$" }
);
routes.MapRoute(
"FooterRoute", //route for invoking actions for the Footer
"{controller}/{action}", // URL with parameters
new { controller = "Footer", }, // Parameter defaults
new { controller = "^Footer$" }
);
routes.MapRoute(
"DefaultDealRoute", // Route name
"{city}/{category}/{id}/{slug}", // URL with parameters
new { category = Deals.Globals.Global.CATEGORY_ALL_NAME, controller = "Deal", action = "Details", slug = UrlParameter.Optional }, // Parameter defaults
new { controller = "^Deal$", id = #"\d+" }
);
routes.MapRoute(
"DealRouteForCategory", // Route name
"{city}/{category}", // URL with parameters
new { city = "", category = Deals.Globals.Global.CATEGORY_ALL_NAME, controller = "Deal", action = "Details" }, // Parameter defaults
new { controller = "^Deal$" }
);
}
I have add a reference to routedebugger to see what is going on under the hood.
So for the selected URL:
http://my.SERVER.IP/VirtualDirectoryName/Order/PayPalNotify/9/blabla
the URL debugger shows the following:
AppRelativeCurrentExecutionFilePath: ~/Order/PayPalNotify/9/adfaf (exactly what i have expected)!!!
The debugger also shows that routes DefaultOrderingRoute and DefaultDealRoute are matched (this i didn't expect! since i have constraints on the routes!!).
It also shows that the matched route is:
Matched Route: {controller}/{action}/{id}/{slug}
with route data:
controller: Deal
action: Details
id: 9
slug: adfaf
city: Athens
category: All
How is this possible?? What am i doing wrong?
PS. I've noticed that by reissuing the URL the correct route gets executed!!!

Mystery solved: i was doing something very bad(!):
in Session_Start() i had somewhere code which did the following (amongst others)
Session_Start()
{
// code snippet out for brevity
//setup the city route values
HttpContext.Current.Request.RequestContext.RouteData.Values["city"] = cityToBeginWith;
//setup the category route values
HttpContext.Current.Request.RequestContext.RouteData.Values["category"] = Deals.Globals.Global.CATEGORY_ALL_NAME;
//setup the controller route values
HttpContext.Current.Request.RequestContext.RouteData.Values["controller"] = "Deal";
//setup the action route values
HttpContext.Current.Request.RequestContext.RouteData.Values["action"] = "Details";
}
This caused the selection of the "false" route... very bad!!

Related

how to route custome url in MVC

I have return my Controller with attribute routing like below.
[Route("{CourseName}/{CourseCode}")]
public ActionResult getAllProductList(string CourseName,string CourseCode)
{
ViewBag.CourseName = CourseName;
ViewBag.CourseCode = CourseCode;
return View("CoursePage");
}
it works fine. but if there is any ajax method calls (for ex. ../controllername/methodname) from JS then its hitting my above controller/Action instead of "methodname" . Please suggest.
My RouteConfig Code:
routes.MapRoute(
name: "CoursePage",
url: "{CourseName}/{CourseCode}",
defaults: new { controller = "Course", action = "getAllProductList" },
constraints: new { CourseName = "\\d +", CourseCode = "\\d +" }
);

Url.Action and Url.RouteUrl renders empty on production box(MVC3)

I am having a strange issue. The Url.Action and Url.RouteUrl renders correctly in the development server(IIS 7.5, Windows 7) but the same code on production box(dedicated server running IIS 7.5, Win 2008 R2) renders it blank.
The html in my view is :
<div style="display:none;">
</div>
The output in view source is:
<div style="display:none;">
</div>
The difference between dev and prod environment is that on my dev machine mvc4 is installed and on prod box only mvc3 is installed.
I have tried routeDebug on production box. When I browse for home/generateCaptcha the "default" route is selected.
I do not understand why the url.action is not able to generate the correct url only on production box.
While deploying, I have even added the deployable dependencies.
I have the following routes defined:
routes.MapRoute(
name: "ByCatSubCat" // Route name
, url: "browse/{categoryName}/{subcategoryName}/" // URL with parameters
, defaults: new { controller="Greeting", action="List"} // Parameter defaults
);
routes.MapRoute(
name: "CardDetail" // Route name
, url: "browse/{categoryName}/{subcategoryName}/card{id}" // URL with parameters
, defaults: new { controller = "Greeting", action = "Details" } // Parameter defaults
);
routes.MapRoute(
name: "ByCatSubCatAll" // Route name
, url: "browse/{categoryName}/{subcategoryName}/" // URL with parameters
, defaults: new { controller = "Greeting", action = "List", subcategoryName="all" } // Parameter defaults
);
routes.MapRoute(
name: "ByCat" // Route name
, url: "browse/{categoryName}/" // URL with parameters
, defaults: new { controller = "Greeting", action = "List" } // Parameter defaults
);
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}/", // URL with parameters
new { controller = "Greeting", action = "List", id = UrlParameter.Optional } // Parameter defaults
);
Please help.
Update:
It seems that UrlParameter.Optional does not work here.
When I added one more route without having UrlParameter.Optional, all the links started working in production box.
My updated routes are:
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "ByCatSubCat" // Route name
, url: "browse/{categoryName}/{subcategoryName}/" // URL with parameters
, defaults: new { controller = "Greeting", action = "List" } // Parameter defaults
);
routes.MapRoute(
name: "CardDetail" // Route name
, url: "browse/{categoryName}/{subcategoryName}/card{id}" // URL with parameters
, defaults: new { controller = "Greeting", action = "Details" } // Parameter defaults
);
routes.MapRoute(
name: "ByCatSubCatAll" // Route name
, url: "browse/{categoryName}/{subcategoryName}/" // URL with parameters
, defaults: new { controller = "Greeting", action = "List", subcategoryName = "all" } // Parameter defaults
);
routes.MapRoute(
name: "ByCat" // Route name
, url: "browse/{categoryName}/" // URL with parameters
, defaults: new { controller = "Greeting", action = "List" } // Parameter defaults
);
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/", // URL with parameters
new { controller = "Greeting", action = "List" } // Parameter defaults
);
routes.MapRoute(
"Default1", // Route name
"{controller}/{action}/{id}/", // URL with parameters
new { controller = "Greeting", action = "List", id = UrlParameter.Optional } // Parameter defaults
);
You need to set up an area, for example: Url.Action("Models", "Russian", new { brand = mod.Brand.NameEngShort, area = "Catalog" })

What kind of route would I need to provide vanity urls?

I'd like to provide my users a vanity url, something like:
www.foo.com/sergio
What kind of route would I need to create?
Imagine I have the following controller and action, how can I map a vanity URL to that controller?
public ActionResult Profile(string username)
{
var model = LoadProfile(username);
return View(model);
}
Here is what I've tried and what happens:
Option A:
Every url is caught in this route, meaning every URL I type directs me towards the Account controller, instead of only foo.com/[USERNAME]. No good.
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Profile",
"{username}",
new { controller = "Account", action = "Profile", username = UrlParameter.Optional }
);
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
}
Option B:
Default routes work well, but when trying to visit a profile foo.com/[USERNAME] I get an HTTP 404 error.
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
routes.MapRoute(
"DentistProfile",
"{username}",
new { controller = "Account", action = "Profile", username = UrlParameter.Optional }
);
}
one solution could be using custom route constraint as,
public class VanityUrlContraint : IRouteConstraint
{
private static readonly string[] Controllers =
Assembly.GetExecutingAssembly().GetTypes().Where(x => typeof(IController).IsAssignableFrom(x))
.Select(x => x.Name.ToLower().Replace("controller", "")).ToArray();
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values,
RouteDirection routeDirection)
{
return !Controllers.Contains(values[parameterName].ToString().ToLower());
}
}
and use it as
routes.MapRoute(
name: "Profile",
url: "{username}",
defaults: new {controller = "Account", action = "Profile"},
constraints: new { username = new VanityUrlContraint() }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
downside of this approach, profile view for usernames same as existing controller name will not work, like if there are usernames like "asset", "location", and "AssetController", "LocationController" exists in project, profile view for "asset", "location" will not work.
hope this helps.
Have you tried:
routes.MapRoute(
"YourRouteName",
"{username}",
new {controller="YourController", action="Profile", username=UrlParameter.Optional}
);
This should trap www.foo.com/{username}. Remember that routes are checked in the order you add them, so you can add
routes.MapRoute(
"default",
"{controller}/{action}/{input}",
new {controller="controller", action="action", input=UrlParameter.Optional}
);
first to maintain the "default" behavior.

ASP MVC 3 switching language with ActionLink

In an ASP MVC 3 project i want to enable language switching.
The routing is defined like this:
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"DefaultWithLanguage", // Route name
"{language}/{controller}/{id}/{slug}", // URL with parameters
new { language = "en", controller = "Front", action = "Details", id = UrlParameter.Optional, slug = UrlParameter.Optional } // Parameter defaults
);
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
Trying to switch languages (in _Layout.cshtml) works like this:
<li>#Html.ActionLink("Spanish", ViewContext.RouteData.Values["action"].ToString(), ViewContext.RouteData.Values["controller"].ToString(), new { language = "es" })</li>
Instead of getting a URL like the following (after having selected Spanish)
.../es/ControllerName/ActionName
i 'm getting this:
.../ControllerName/ActionName?Length=11
If i set the ActionLink to the following (notice the last null parameter):
<li>#Html.ActionLink("Spanish", ViewContext.RouteData.Values["action"].ToString(), ViewContext.RouteData.Values["controller"].ToString(), new { language = "es" }, null)</li>
i get this:
.../ControllerName/ActionName?language=es
What am i missing?
Thanks in advance!
There are some issues with your routes registration DefaultWithLanguage route registration. It doesn't allow you to specify the {action}. This means that this route will only match a Details action (since you have specified it in the default values). Another issue is that you made the {id} parameter optional. But that's impossible. Only the last parameter of a route can be optional. In your case it is followed by a {slug} parameter.
So one possibility is the following:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"DefaultWithLanguage",
"{language}/{controller}/{action}/{id}",
new
{
language = "en",
controller = "Front",
action = "Details",
id = UrlParameter.Optional
}
);
}
and then:
#Html.ActionLink(
"Spanish",
ViewContext.RouteData.GetRequiredString("action"),
ViewContext.RouteData.GetRequiredString("controller"),
new { language = "es" },
null
)
If you wanted to add a {slug} parameter then your {id} can no longer be optional:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"DefaultWithLanguage",
"{language}/{controller}/{action}/{id}/{slug}",
new
{
language = "en",
controller = "Front",
action = "Details",
slug = UrlParameter.Optional
}
);
}
and then:
#Html.ActionLink(
"Spanish",
ViewContext.RouteData.GetRequiredString("action"),
ViewContext.RouteData.GetRequiredString("controller"),
new {
language = "es",
id = ViewContext.RouteData.GetRequiredString("id")
},
null
)
This is a follow up to Darin's answer.
I want to have routes like this:
.../en/ControllerName/Id
.../en/ControllerName/Id/Slug
.../en/ControllerName
This last route causes the problem (see the answer of Darin). The "hack" to keep getting this routes is to define also another route (the second named DefaultWithLanguageWithoutId) :
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"DefaultWithLanguage", // Route name
"{language}/{controller}/{id}/{slug}", // URL with parameters
new { language = "en", controller = "Front", action = "Details", id = UrlParameter.Optional, slug = UrlParameter.Optional } // Parameter defaults
);
routes.MapRoute(
"DefaultWithLanguageWithoutId", // Route name
"{language}/{controller}", // URL with parameters
new { language = "en", controller = "Front", action = "Details"} // Parameter defaults
);
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
}
This is causing the code to be adjusted in the master page to the following
<li>#Html.ActionLink("Deutsch", ViewContext.RouteData.Values["action"].ToString()
, ViewContext.RouteData.Values["controller"].ToString(), new { language = "de"
, id = ViewContext.RouteData.Values["id"]!=null ? ViewContext.RouteData.Values["id"].ToString():null }, null)</li>
<li>#Html.ActionLink("English", ViewContext.RouteData.Values["action"].ToString()
, ViewContext.RouteData.Values["controller"].ToString(), new { language = "en"
, id = ViewContext.RouteData.Values["id"] != null ? ViewContext.RouteData.Values["id"].ToString() : null }, null)</li>
Works perfect!
The only "drawback" is the fact that there is some code involved (i would have liked to avoid) in the view, but i think it is worth since it is very simple!

ASP.NET MVC 3.0 Routing behaviour

I have controller BlogController with a couple of actions:
1)Index(string id) - show all posts/show single post if parameter id specified
2)New() - add new post
3)Delete() - delete post
4)And some more another actions
So if i type in browser mysite/blog i could see all posts if i type mysite/blog/postnameid i want to see single post.
the problem is when i type mysite/blog/postnameid it is not working (The resource cannot be found.), but if i type mysite/blog/index/postnameid this way it is working. How could i make mysite/blog/postnameidto work as well.
Here is my blog route in global.ascx
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRouteLowercase(
"Blog", // Route name
"Blog/{action}/{id}", // URL with parameters
new { controller = "Blog", action = "Index" } // Parameter defaults
);
routes.MapRouteLowercase(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
Than if i change it like that
routes.MapRouteLowercase(
"Blog", // Route name
"Blog/{id}", // URL with parameters
new { controller = "Blog", action = "Index" } // Parameter defaults
);
the mysite/blog/postnameid working but all another actions like New(), Delete() stop working after that (The resource cannot be found. )
UPDATE:
I forgot to mention that id is sting, not int. so from #Darin answer i changed new { id = #"\w+" } to new { id = #"\d+" } and all seams to be working but now when i typed blog/new for example, it is routing to show/new insteard blog/new
It is a bad idea and against RESTful conventions to have a single controller action that do 2 things and of course violating the single responsibility principle (list all posts if no id is specified and show a given post if an id is specified). The correct way would be to have the following:
/blog => BlogController/Index => list all posts
/blog/123 => BlogController/Show(id = 123) => show details of a given post
/blog/new => BlogController/New() => start writing a new post
/blog/delete/123 => BlogController/Delete(id = 123) => delete given post
which would be achieved with the following routes:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Blog",
"blog/{id}",
new { controller = "Blog", action = "Show" },
new { id = #"\d+" }
);
routes.MapRoute(
"Default",
"blog/{action}/{id}",
new { controller = "Blog", action = "Index", id = UrlParameter.Optional }
);
}
Notice the required route constraint on the first definition which indicates what form all ids must be so that the routing engine could disambiguate between an id and an action name.
This being said if you want to violate the 2 principles I mentioned earlier and have the routes you want a slight adaptation would be required:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Blog",
"blog/{id}",
new { controller = "Blog", action = "Index" },
new { id = #"\d+" }
);
routes.MapRoute(
"Default",
"blog/{action}/{id}",
new { controller = "Blog", action = "Index", id = UrlParameter.Optional }
);
}
Now:
/blog => BlogController/Index(id = null) => list all posts
/blog/123 => BlogController/Index(id = 123) => show details of a given post
/blog/new => BlogController/New() => start writing a new post
/blog/delete/123 => BlogController/Delete(id = 123) => delete given post

Resources