MVC default route with id not working? - asp.net-mvc

I want to link to a controller with actually specifying the action parameter. I'm pretty sure this is possible. And got the concept from here:
http://www.asp.net/mvc/tutorials/controllers-and-routing/creating-a-route-constraint-cs
These are the two links:
https://localhost/mvcapplication1/customer/order/index/6463 (WORKS)
https://localhost/mvcapplication1/customer/order/6463 (WANT THIS)
I specified a special rout for this controller (Customer_orders).
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"Customer_orders",
"Customer/Order/{orderId}",
new { controller = "MvcApplication1.Areas.Customer.Controllers.Order", action = "Index" },
new[] { "MvcApplication1.Areas.Customer.Controllers" } // only map routes to controllers in the specified namespace
);
context.MapRoute(
"Customer_default",
"Customer/{controller}/{action}/{id}",
new { action = "Index", id = UrlParameter.Optional },
new[] { "MvcApplication1.Areas.Customer.Controllers" } // only map routes to controllers in the specified namespace
);
}
And this is my controller with the namespace:
namespace MvcApplication1.Areas.Customer.Controllers
{
[Authorize]
public partial class OrderController : SupertextController
{
I've also enabled the Route Debugger and according to that my routes are ok. So why do I get a "The resource cannot be found."? Specially since it works if I add "index" to the URL.

In your routes the default value of the controller token should be your controller name without any namespace and without the "Controller" postfix:
So change your route to:
context.MapRoute(
"Customer_orders",
"Customer/Order/{orderId}",
new { controller = "Order", action = "Index" },
new[] { "MvcApplication1.Areas.Customer.Controllers" }
);

Related

MVC routes with static Prefix

I have two routes in my routeConfig files as follow.
Route with admin prefix which handles request for admin part
default Route without a prefix, for which I have added a datatoken to map routes in candidate Area
routes.MapRoute(
name: "admin",
url: "Admin/{controller}/{action}/{id}",
defaults: new { controller = "Account", action = "Login", id = UrlParameter.Optional },
namespaces: new[] { "abc.namespace1" }
);
routes.MapRoute(
name: "default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Account", action = "Login", id = UrlParameter.Optional },
namespaces: new[] { "abc.namespace2" }
).DataTokens.Add("area", "Candidate");
But the problem is when i type in a url localhost/MyApp/Admin/Home/Index
it is hitting the controller in abc.namespace1 (which is expected) and localhost/MyApp/Home/Index also hitting Home controller inside abc.namespace1 instead of HomeController inside abc.namespace2 in candidate Area.
What i want to do here is handle all routes with Admin prefix with controllers inside abc.namespace1 and all routes without any prefix with controllers inside abc.namespace2 which is my candiate Area.
regards
I believe this might have something to do with the way you have specified your namespaces. The namespace must be for where the controller classes reside.
The pattern is typically <namespace of area>.<area name>.<controller namespace>
For example, in a project with an area named "Admin", the namespace must be:
"MvcMusicStore.Areas.Admin.Controllers"
In my experience, the conventions for how areas are set up are pretty strict. You should not set up the route in an AreaRegistration rather than the root of your project in order to get it to work.
public class CandidateAreaRegistration : AreaRegistration
{
public override string AreaName
{
get
{
return "Candidate";
}
}
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"Candidate_default",
"{controller}/{action}/{id}",
new { controller = "Account", action = "Login", id = UrlParameter.Optional },
new string[] { "<project name>.Areas.Candidate.Controllers" }
);
}
}
Areas are convention-based. If you deviate too far from the anticipated conventions, they simply don't function.

Visual Studio MVC URL Rewrite

When I route to an address in MVC routing-config file, it works. But the URL in the bar doesn't update accordingly, it remains the same though contents are routed.
routes.MapRoute(
"User",
"Profile{id}",
new { controller = "Home", action = "Details" }
);
It re-routes localhost/profile4 to localhost/home/details, but the URL in the bar doesn't update.
You can create two actions in your controller with two routes and make redirect.
routes.MapRoute(
"User",
"Profile{id}",
new { controller = "Home", action = "ProfileRedirect" }
);
routes.MapRoute(
"Details",
"Details",
new { controller = "Home", action = "Details" }
);
public class HomeController: Controller
{
public ActionResult ProfileRedirect(int id)
{
return RedirectToAction("Details");
}
}
Another solution is make some HttpHandler and redirect requests on global level.

Controller in Sub Folder

My area is below. Only the concerned part is highlighted.
Route Table
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"SubFolder", // Route name
"SubFolder/ChildController",
new { controller = "ChildController", action = "Index" },
new[] { "Practise.Areas.SOProblems.Controllers.SubFolder" });
routes.MapRoute(
"Default", // Route name
"{controller}/{action}", // URL with parameters
new { controller = "Home", action = "Index" } // Parameter defaults
);
}
This only works when the url is like this
localhost:2474/SOProblems/ChildController/index
This does not works when the url is like this
localhost:2474/SOProblems/SubFolder/ChildController/index
Can you please tell me what is missing?
This does not works when the url is like this
localhost:2474/SOProblems/SubFolder/ChildController/index
That's normal. You route pattern looks like this: SubFolder/ChildController and not SubFolder/ChildController/index. In addition to that you defined your route in the WRONG place. You defined it in your main route definitions and not in your area route definitions. So get rid of the custom route definition from your main routes and add it to the SOProblemsAreaRegistration.cs file (which is where your SOProblems routes should be registered):
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"SubFolderRoute",
"SOProblems/SubFolder/ChildController",
new { controller = "ChildController", action = "Index" },
new[] { "Practise.Areas.SOProblems.Controllers.SubFolder" }
);
context.MapRoute(
"SOProblems_default",
"SOProblems/{controller}/{action}/{id}",
new { action = "Index", id = UrlParameter.Optional }
);
}
Also since your route pattern (SOProblems/SubFolder/ChildController) doesn't have the possibility to specify an action name, you can only have one action on this controller and that would be the default action that you registered (index) in this case.
If you wanted to have more actions on this controller and yet index be the default one you should include that in your route pattern:
context.MapRoute(
"SubFolder",
"SOProblems/SubFolder/ChildController/{action}",
new { controller = "ChildController", action = "Index" },
new[] { "Practise.Areas.SOProblems.Controllers.SubFolder" }
);
In both cases your main route definition could remain with their default values:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default",
"{controller}/{action}",
new { controller = "Home", action = "Index" }
);
}
Your new route "SubFolder" does not include the possibility of including an action in the route (in your case, "Index").
Your sample URL
localhost:2474/SOProblems/SubFolder/ChildController/index
Wants to try to match a route like:
"SubFolder/ChildController/{action}"
But you don't include the "{action}" in your route, so it won't match your route. It then tries the default route, which obviously fails.
Try adding "{action}" to your route:
routes.MapRoute(
"SubFolder", // Route name
"SubFolder/ChildController/{action}",
new { controller = "ChildController", action = "Index" },
new[] { "Practise.Areas.SOProblems.Controllers.SubFolder" });
or take "index" off your test URL.
For any future users looking to do this; Look into using Areas.
Here's a helpful video.
Organizing an application using Areas

MVC Routing and Areas - how to construct a route to match action on default controller in area?

I've got a simple MVC4 web site containing an area (called "User") containing a Controller called "HomeController".
On this controller there are two action methods: Index and Details:
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult Details(int id)
{
return View();
}
}
The "UserAreaRegistration.cs" class is as follows:
public class UserAreaRegistration : AreaRegistration
{
public override string AreaName
{
get
{
return "User";
}
}
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"User_default",
"User/{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
namespaces: new[] { "MvcApplication1.Areas.User.Controllers" }
);
context.MapRoute(
"User_default_no_contoller",
"User/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
namespaces: new[] { "MvcApplication1.Areas.User.Controllers" }
);
}
}
There's also a Controller that's not in an area called "HomeController":
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
}
RouteConfig.cs (for non-area routes) is as follows:
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
namespaces: new[] { "MvcApplication1.Controllers" }
);
}
}
I am able to access the "homepage" via either
/
/Home
/Home/Index
I'm also able to get to the "Index" action in my "User" area
/User
What I cannot do is work out how to get to the other method on my Controller in the Area called "Details":
/User/Details/5 does not work, it returns a 404
I also notice that trying to access the Index method explicitly is also not routed correctly:
/User/Index does not work, it returns a 404
What am I doing wrong?
Is it possible to get to any action method other than "Index" on a controller that's the "default" controller in an area?
There are too many defaults on your first route, so it's swallowing everything up.
the URI /User/Details/5 matches the User_default route (Controller: "Details" and Action: "5").
the URI /User/Index also matches the first route (Controller: "Index" and the default Action: "Index")
The way to approach routes is
put the most selective route first
and set only the minimum default values necessary
use route constraints to ensure that the MVC router doesn't attempt to match parameters incorrectly (ie, give route values intended for int parameters a numeric constraint)
put the most general route (ie, User_default) last (as a catch-all for URIs that didn't match any more specific route)
I'd try setting up the area routes like this.
context.MapRoute(
"User_default_no_contoller",
"User/{action}/{id}",
defaults: new { controller = "Home", id = UrlParameter.Optional },
constraints: new { id = #"\d+" },
namespaces: new[] { "MvcApplication1.Areas.User.Controllers" }
);
context.MapRoute(
"User_default",
"User/{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
namespaces: new[] { "MvcApplication1.Areas.User.Controllers" }
);
Another suggestion to overcome this "Multi-defaults" problem is to separate using Namespaces.
In your example my suggestion is to have the Class UserAreaRegistration in the namespace "Areas.User" . Subsequently you would have to put your HomeController in the User Area in the Namespace "Areas.User.Controllers"
It works for me and keeps the code clean.
Peter
Thanks for the answer dbaseman. It helped me look at my own routing problem. I am not sure if I was running into the same issue but that is what it seems like.
routes.MapRoute("Tag", "Tag/{tag}", new { controller = "Blog", action = "Tag" });
routes.MapRoute("Category", "Category/{category}", new { controller = "Blog", action = "Category" });
routes.MapRoute("Post", "Archive/{year}/{month}/{day}/{title}", new {controller = "Blog", action = "Post"});
routes.MapRoute("Action", "{action}", new { controller = "Blog", action = "Posts" });
When I had the last route with the "Action" parameters above any of the other routes the ones below "Action" would not be caught. Moving it to the bottom of the RouteConfig allowed all the ones above it to be hit successfully now.

Setting up Index as the default route for a controller

I have a url
http://www.roadkillwiki.org/Page/Index/documentation
which I want to turn into
http://www.roadkillwiki.org/Page/documentation
That could also be something like http://www.roadkillwiki.org/Page/my-url-with-spaces - the parameter is a string. The route setup I've tried is:
routes.MapRoute(
"ControllerDefault",
"{controller}/{id}",
new { controller = "Page", action = "Index", id = UrlParameter.Optional }
);
However this is interfering with the default "id" route that MVC projects come with. Is there any way of achieving this?
You don't need to lose the default route. The key to avoiding your routes interfere with each other is to order them so the more specific rules precede the less specific ones. For example:
// Your specialized route
routes.MapRoute(
"Page",
"Page/{slug}",
new { controller = "Page", action = "Index" }
);
// Default MVC route (fallback)
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
Then your PageController would look like this:
using System.Web.Mvc;
public class PageController : Controller
{
public string Index(string slug)
{
// find page by slug
}
}
That said, I would strongly advice you to do this instead:
// Your specialized route
routes.MapRoute(
"Page",
"Page/{id}/{slug}",
new { controller = "Page", action = "Index", slug = UrlParameter.Optional }
);
// MVC's default route (fallback)
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
And your PageController:
using System.Web.Mvc;
public class PageController : Controller
{
public string Index(int id)
{
// find page by ID
}
}
By including the page ID either at the beginning of your URL (like StackOverflow does) or at the end, you can then just ignore the slug, and instead retrieve your pages by ID. This will save you a ton of headaches if your users change the page name. I have gone through this and it's painful; you basically have to keep a record of all names your pages have had in the past, just so your visitors/search engines don't get a 404 every time a page is renamed.
Hope this helps.
If you don't need a default route that came with project template you can set up one like this:
routes.MapRoute(
"ControllerDefault",
"{controller}/{pagename}",
new { controller = "Page", action = "Index" }
);
And than in your controller you would have an action:
public ActionResult Index(string pagename)
{
//do something
}

Resources