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.
Related
I have setup my routes so that I have the following flat URL structure:
mywebsite/service-option-one (goes to Home Controller, Option1 action)
mywebsite/service-option-two (goes to Home Controller, Option2 action)
mywebsite/ (goes to Home Controller, Index)
mywebsite/about (goes to Home Controller, Index with path=about)
mywebsite/contact (goes to Home Controller, Index with path=contact)
This is important as I have a lot of content views and do not want to have individual actions for these generic information pages, the simple code for the resolving the view is at the end of this post.
When building the menu the MVC Html.ActionLink helpers, but they give the incorrect addresses for generic content which makes sense as these actions do not exist!
Given my address scheme, how can is there a helper method I can use to set my anchor link targets or do i just have to resort to hard coding in html (i.e. <a href="~/contact>Contact us</a>)?
// note that the order of the routes is very important!
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
// error route
routes.MapRoute(
"Error", // Route name
"Oops", // URL with parameters
new { controller = "Home", action = "Error" }
);
routes.MapRoute(
"Service1",
"service-option-one",
new { controller = "Home", action = "Option1" }
);
routes.MapRoute(
"Service2",
"service-option-two",
new { controller = "Home", action = "Option1" }
);
// default home controller route for general site content (/content), uses default path value set to Index
routes.MapRoute(
name: "Catchall",
url: "{path}",
defaults: new { controller = "Home", action = "Index", path = "Index" }
);
// home page route, blank url (/)
routes.MapRoute(
"HomePage", // Route name
"", // URL with parameters
new { controller = "Home", action = "Index" }
);
// default route
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "{controller}", action = "{action}", id = UrlParameter.Optional } // Parameter defaults
);
}
public class HomeController : Controller
{
private bool ViewExists(string name)
{
ViewEngineResult result = ViewEngines.Engines.FindView(ControllerContext, name, null);
return (result.View != null);
}
public ActionResult Index(string path)
{
System.Diagnostics.Debug.WriteLine("looking for..." + path);
if (ViewExists(path)==true)
{
System.Diagnostics.Debug.WriteLine("general controller action...");
return View(path);
}
else
{
System.Diagnostics.Debug.WriteLine("page not found...");
return RedirectToRoute("Error");
}
}
public ActionResult Error()
{
return View();
}
public ActionResult Option1()
{
return View();
}
public ActionResult Option2()
{
return View();
}
}
You're approaching the use of the ActionLink helper from the opposite position that you should be when using helpers. You're trying to tell Razor what the routed url should be for the link, when in reality that's what Razor is going to be doing for YOU. So for the example of mywebsite/service-option-one, the only place you should have service-option-one written down is in your routes map. In your views, you should be doing
#Html.ActionLink("The Anchor Text You Want To Display","Option1","Home")
Then when Razor renders the view, it will translate to
The Anchor Text You Want To Display
http://msdn.microsoft.com/en-us/library/dd505070(v=vs.118).aspx
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.
Maybe I don't understand real purpose of asp mvc routing.
I created an application and now I need to fix my url's a to be more understandable.
For example I have area Cities with controller Home and action Index.
So here I need url like: localhost/London but with current routing I get localhost/cityPage/Home.
My question is can I somehow pass parameter like city name and make URL like I want?
This is my current default routing in Global.asax
routes.MapRoute(
"Default",
"{area}/{controller}/{action}/{id}",
new { area = "CityPage", controller = "Home", action = "Index", id = "" },
new string[] { "MyProject.Areas.Cities.Controllers" }).DataTokens.Add("area", "Cities");
New routing:
routes.MapRoute(null,
"CityPage/{cityName}",
new
{
area = "CityPage",
controller = "Home",
action = "Index"
}
);
routes.MapRoute(
"Default",
"{area}/{controller}/{action}/{id}",
new { area = "CityPage", controller = "Home", action = "Index", id = "" },
new string[] { "MyProject.WebUI.Areas.CityPage.Controllers" }).DataTokens.Add("area", "CityPage");
Example of link that I click
#Html.ActionLink("City London", "Index", "Home", new { cityName = "London" }, null)
In order to route the URL localhost/London to the Index action on the HomeController of the Cities area, you need a route like this:
routes.MapRoute(null,
"{id}",
new
{
area = "Cities", controller = "Home", action = "Index"
}
);
Be sure this route is declared before the "Default" route in your CitiesAreaRegistration.cs class.
However if you have a lot of other routes in your application, adding a general route like this can play havoc with other routes in the app. I suggest adding a URL prefix to separate this route from others in your application:
routes.MapRoute(null,
"cities/{id}",
new
{
area = "Cities", controller = "Home", action = "Index"
}
);
This will make your URL look like localhost/cities/London. Is that acceptable?
Update 1
Unless you completely remove your "Default" route definition, you will actually have multiple INBOUND routes that map to this action. You would have localhost/cities/London, localhost/cityPage/Home, localhost/cityPage/Home/Index, and localhost/cityPage/Home/Index/London all resolving to that action. However when MVC chooses to generate an OUTBOUND route, it will choose the first one -- localhost/cities/London.
Update 2
If you want your route parameter to be cityName, you would do this:
routes.MapRoute(null,
"cities/{cityName}",
new
{
area = "Cities", controller = "Home", action = "Index"
}
);
However you would then have to change the Index action on your Cities area's HomeController to have this signature:
public ActionResult Index(string cityName)
By changing the argument from id to cityName, you are telling MVC to pass this URL paramter / route segment to the action method.
Update 3
Is the name of your area "Cities" or "CityPage"? From previous code it looked like the name of your area was Cities.
If it is CitiesPage, try this for your action method:
#Html.ActionLink("City London", "Index", "Home",
new { area = "CityPage", cityName = "London" })
Final Answer
I just reproduced this in an MVC3 project, and it is working as expected:
Created a new area named "CityPage"
Added a HomeController with an Index action to the CityPage area
Added an Index view to the CityPage/Views/Home folder.
CityPageAreaRegistration.cs:
public class CityPageAreaRegistration : AreaRegistration
{
public override string AreaName
{
get
{
return "CityPage";
}
}
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(null,
"CityPage/{cityName}",
new { area = "CityPage", controller = "Home", action = "Index" }
);
//context.MapRoute(
// "CityPage_default",
// "CityPage/{controller}/{action}/{id}",
// new { action = "Index", id = UrlParameter.Optional }
//);
}
}
HomeController.cs:
public class HomeController : Controller
{
//
// GET: /CityPage/Home/
public ActionResult Index(string cityName)
{
return View();
}
}
Index.cshtml:
#{
ViewBag.Title = "Index";
}
<h2>
Index</h2>
#Html.ActionLink("City London", "Index", "Home",
new { area = "CityPage", cityName = "London" }, null)
Finally, here is the link generated by the action link:
City London
yes you can do this way but you have to do following thing
Make sure your route must register before generic route.
Get Information about RouteConstraint
http://www.asp.net/mvc/tutorials/controllers-and-routing/creating-a-route-constraint-cs
http://www.asp.net/mvc/tutorials/controllers-and-routing/creating-a-custom-route-constraint-cs
Just for example Try this way check your required url localhost/London
routes.MapRoute(
"Default",
"{id}",
new { area = "CityPage", controller = "Home", action = "Index", id = "" },
new string[] { "MyProject.Areas.Cities.Controllers" }).DataTokens.Add("area", "Cities");
I have accountController class and its have login & home views.
[HandleError]
public class accountController : Controller
{
[HttpPost]
public ActionResult login(LoginModal model, string returnUrl)
{
//Authentication
return RedirectToAction("home");
}
public ActionResult home()
{
return View();
}
}
------------------------------
-----------------------------
Global.asax have Route entry.. so my urls is
http://lmenaria.com/login
http://lmenaria.com/home
routes.MapRoute(null, "home", new { controller = "account", action = "home" });
routes.MapRoute(null, "login", new { controller = "account", action = "login" });
When I tried the both URL on browser they are working fine. But when login success then its go to http://lmenaria.com/account/home
So how can I remove "account" from this url. this is going when I used return RedirectToAction("home"); and getting 404 error.
So please let me know how can I resolved that issue. I don't need Controller Name in url.
Thanks
Laxmilal Menaria
routes.MapRoute("home", "home", new { controller = "account", action = "home" });
I tried with Above MapRoute & use RedirectToRoute instead of RedirectTOAction and its work.
Thanks.
If you want to default the controller to "account", without showing it in your URL, then you can do something like
routes.MapRoute(null, "{action}", new { controller = "account" });
if you want, you can use a default value for action too
routes.MapRoute(null, "{action}", new { controller = "account", action = "home" });
I'm editing my answer to let you know that you don't need to explicitly define each route as you're doing. MVC routes match patterns. So, instead of
routes.MapRoute(null, "home", new { controller = "account", action = "home" });
routes.MapRoute(null, "login", new { controller = "account", action = "login" });
use just
routes.MapRoute(null, "{action}", new { controller = "account" });
And pay attention to the routes order if you define more than one pattern, because order matters. MVC will use the first pattern that matches your URL.
I think the real issue is that you either didn't create or removed the home controller.
From your description it really sounds like you should be calling RedirectToAction for the Home controller Index action.
you have [HttpPost] on your Login action. Which should be the problem. I just run your codes without [HttpPost] attribute. it is working. when you type http://lmenaria.com/login, your "login" action will not be fired, because of the attribute. so, there must be some other routes that does the routing.
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
}