Why there are so many ways to select an action method in ASP.NET MVC and how I avoid this? I mean, I can go to Index action method from /, /Home, /Home/Index. I think it will affect SEO ranking.
This is due to the default routing setup in Global.asax.cs:
routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
This configures a route with default values for both the controller and action. Thus:
/Home/Index will match this route with controller and action values explicitly defined
/Home will match the Default route and the routing config will supply a default controller of "Home"
/ will match the Default route and the routing config will supply a default controller and action
You could change this by removing controller = "Home", action = "Index" from the third argument to MapRoute. This removes the defaults and requires that the URL explicitly specifies the controller and action.
You would want a second MapRoute call to explicitly specify a home page route:
// route the root URL to the home page controller/action
routes.MapRoute("HomePage", "", new { controller = "Home", action = "Index" });
Otherwise a request to http://yourdomain.com will not route to any controller/action pair and you would get a 404 instead of the home page.
Quoting the answer from this site,
public class RemoveDuplicateContentAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var routes = RouteTable.Routes;
var requestContext = filterContext.RequestContext;
var routeData =requestContext.RouteData;
var dataTokens = routeData.DataTokens;
if (dataTokens["area"] == null)
dataTokens.Add("area", "");
var vpd = routes.GetVirtualPathForArea(requestContext, routeData.Values);
if (vpd != null)
{
var virtualPath = vpd.VirtualPath.ToLower();
var request = requestContext.HttpContext.Request;
if (!string.Equals(virtualPath, request.Path))
{
filterContext.Result = new RedirectResult(virtualPath + request.Url.Query, true);
}
}
base.OnActionExecuting(filterContext);
}
}
Related
I'm trying to redirect to a route that is in the RecomController and the action is Index, i have defined the path in my RouteConfig file and have also specified an id parameter that i pass when i call my RedirectToRoute function. For some reason it can't find that path.
I have also created a Route attribute above the RecomController Index action but it still doesn't navigate me to that path. Am i missing something?
RouteConfig.cs:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
routes.MapRoute(
name: "Recomroute",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Recom", action = "Index", id = UrlParameter.Optional }
);
}
RecomController.cs:
[Route("Recom/Index")]
public ActionResult Index(int id)
{
............//Functioning
}
Calling the function (IN ANOTHER CONTROLLER):
ProjectController.cs:
return RedirectToRoute("Recom/Index/{id}", new {id = projectdto.Id });
Sounds like you're using wrong route name or path in RedirectToRoute(), hence it doesn't work:
// this is wrong because first parameter did not match
return RedirectToRoute("Recom/Index/{id}", new {id = projectdto.Id });
For two parameters overload, it requires route name (not route path) and route values, as shown in definition below:
protected internal RedirectToRouteResult RedirectToRoute(string routeName,
RouteValueDictionary routeValues)
Hence, you need to provide complete route name and route values defined in RegisterRoutes (e.g. Recomroute).
return RedirectToRoute("Recomroute", new {
controller = "Recom",
action = "Index",
id = projectdto.Id
});
Side notes:
1) You still need to provide controller, action and id parameters in order to match route definition.
2) The Recomroute seem defined below default route with same route segments definition which overrides all custom routes beneath it, if you want to evaluate Recomroute first move it to the top order and use different path against default route.
I'm having a problem with MVC 4, and I guess it's something really trivial, but it's been bugging me for the last day and I can't seem to figure it out.
I have this url:
http://www.example.com/my-dashed-url
I have a Controller named:
public class MyDashedUrlController: Controller
{
}
I have only two Routes like this:
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute("my-dashed-url",
"my-dashed-url/{action}",
new { controller = "MyDashedUrl", action = "Index" });
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home",
action = "Index",
id = UrlParameter.Optional }
);
I get to the index just fine. However, when I do this:
public ActionResult Index()
{
if (NoUserIsLoggedOn)
return RedirectToAction("Logon", "MyDashedUrl");
return View();
}
public ActionResult Logon()
{
Contact c = GetContact();
return View(c);
}
It doesn't redirect me to the "Logon" action properly.
It should redirect me to:
http://www.example.com/my-dashed-url/logon
but instead it tries to redirect me to:
http://www.example.com/logon
... which doesn't work (404 Not Found)
I'm missing something. Can anyone spot it? If anyone needs any more information, let me know.
And it's EVERY RedirectToAction that does the same thing in this controller. A Html.BeginForm("Logon", "MyDashedUrl") would also generate:
http://www.example.com/logon
I guess it has to do something with the Routes I defined, but I can't find the faulty one, seeing as they're all the same. If I disable all of my Routes besides the default one from MVC, the problem remains the same
Make sure that you have declared this custom route BEFORE the default one:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"my-dashed-url",
"my-dashed-url/{action}",
new { controller = "MyDashedUrl", action = "Index" }
);
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
}
Remember that routes are evaluated in the order you declared them. So the first route that matches a request will be used. If you declare your custom route after the default one, it is the default route that will match the request.
In my limited (2 weeks) experience in asp.net MVC3, for most action methods, I have never needed to add a route registration. But I have noticed that if the action method has an input parameter, then I can't access the method with a url of the form www.mysite.com/myController/myAction/myParameter1/myParameter2/myParameter3 (without the ? mark ) unless I map the route. Is that how its supposed to be?
By default, you already have registered route:
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
it accepts one parameter, named id, so your action:
public ActionResult MyAction(string id)
will "catch" the request:
www.mysite.com/MyController/MyAction/parameter_value
and id will get value "parameter_value".
If you need more than one parameter (or parameter has to be names something else than "id"), then you have to register new route.
In case when you have 2 parameters, you will register route like this:
routes.MapRoute(
"Default",
"{controller}/{action}/{parameter1}/{parameter2}",
new { controller = "Home", action = "Index", parameter1 = UrlParameter.Optional, parameter2=UrlParameter.Optional }
);
and your action might be:
public ActionResult MyAction(string parameter1, int? parameter2)
Yeah, you need to register the route customizing the route in global.asax according to your requirement.You have to register the route in following way:
routes.MapRoute(
"routeName", // Route name
"{controller}/{action}/{myParameter}", // URL with parameters
new { controller = "Home", action = "Index", myParameter= "" } // Parameter defaults
);
So with above route, it ensures that whenever your url goes in above format, the parameter right after "action/" will be taken as parameter.....
For more than one parameter in your url, you can register like this:
routes.MapRoute(
"routeName", // Route name
"{controller}/{action}/{myParameter1}/{myParameter2}/{myParameter3}", // URL with parameters
new { controller = "Home", action = "Index", myParameter1= "", myParameter2= "", myParameter3= "" } // Parameter defaults
);
My Setup
I have a set of controllers in the normal fashion, which have their usual CRUD action methods inside them. Examples of these controllers are Testimonials, Galleries, and FAQs. These have backing models in Entity Framework, such as Testimonial, Gallery and FAQ, respectively.
You get to these by this sort of URL: /Galleries/Edit/2
All good so far, and all by default conventions...
I also have a set of pages that need to have editable content in them, and these have their content populated from a database via Entity Framework. They use an EF model behind them called "Page". This has a content property (html), and a name property so that I can match the incoming request. These pages are the Home, About and Prices pages.
I have chosen the Home controller to do this - I intend to have the index Action work out which Page to load from the DB by a name parameter:
[AllowAnonymous]
public ActionResult Index(string name = "Home")
{
// look up the page by name in the DB.
var model = context.Pages.FirstOrDefault(p => p.Title == name);
// trap errors.
if (model == null)
{
return RedirectToAction("NotFound", "Error", new { aspxerrorpath = name } );
}
// normal path
return View(model);
}
So, I could in theory add new items to the Pages table/DbSet and these would get mapped properly to this controller and action. I will then add an edit action for admin to edit the content that has the same signature as the index action above.
The Problem
The issue comes with Routing requests...
I had 2 initial routes:
routes.MapRoute("DynamicAccess",
"{name}/{action}",
new { controller = "Home", action = "Index" });
routes.MapRoute("Default",
"{controller}/{action}/{id}",
new { controller = "Home", action="Index", id=UrlParameter.Optional});
This fails when I go to "Galleries/", as it goes through the Home controller each time, and fails if I swap them around. I was also getting requests for Scripts/ folder through to the home controller too....
My Temporary Solution
My current routes now look like this:
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.IgnoreRoute("favicon.ico");
routes.MapRoute("Gallery",
"Gallery/{action}/{id}",
new { controller = "Galleries", action = "Index", id = UrlParameter.Optional });
routes.MapRoute("Testimonials",
"Testimonials/{action}/{id}",
new { controller = "Testimonials", action = "Index", id = UrlParameter.Optional });
routes.MapRoute("FAQs",
"FAQs/{action}/{id}",
new { controller = "FAQs", action = "Index", id = UrlParameter.Optional });
routes.MapRoute("DynamicAccess",
"{name}/{action}",
new { controller = "Home", action = "Index" });
routes.MapRoute("Default",
"{controller}/{action}/{id}",
new { controller = "Home", action="Index", id=UrlParameter.Optional});
routes.MapRoute("Root",
"",
new { controller = "Home", action = "Index" });
routes.MapRoute("AdminAccess",
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional }, // Parameter defaults
new { authenticated = new AuthenticatedAdminRouteConstraint() });
You can see here that I've had to declare a route for each of my static pages above the route for the dynamically resolved Home Route.
Question
This looks clumsy to me - having to add each non-dynamic page to my routes table.
Can anyone point me to a cleaner way of doing this please?
Thanks in advance.
Why not put a constraint on your static routes, which will allow routes that don't match to fall through to the dynamic route?
routes.MapRoute("default",
"{controller}/{action}/{id}",
new {controller="home", action="index", id=UrlParameter.Optional},
new {controller="^(|home|gallery|testimonial|faq)$"});
routes.MapRoute("dynamic",
"{name}/{action}",
new {controller="home", action="index"});
You will have to change your controllers to match the singular name in the constraint but other than that, it ought to work.
I have the following route definitions in my MVC3 website:
routes.MapRoute(
"FB", // Route name
"fb/{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
).RouteHandler = new RH();
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
My custom "RH" handler's code is
public class RH : MvcRouteHandler
{
protected override IHttpHandler GetHttpHandler(RequestContext requestContext)
{
//here I store somewhere that 'fb' prefix is used, so logic is different in some places
return base.GetHttpHandler(requestContext);
}
}
What I want to achieve, that when my website is accessed with the 'fb' subpath-prefix, then my website-logic executes a little bit different way.
The problem is, that when I access my site normally (eg. http://localhost), then when I execute
Url.Action('action' 'controller')
, then the output is "http://localhost/fb/controller/action".
I want to achieve, that when my site was accessed with 'fb' prefixed subpath, then my Url.Action calls output /fb/controller/action path, and if I access the website normally (without 'fb' prefix subpath), then Url.Action calls output /controller/action
The main thing, that /fb/controller/actions have to route to the same controllers/actions as when the site is accessed via /controller/action format.
The 'fb' route is just needed to store some temporary info when 'fb' prefix i used.
Seems I found a solution based on this link (MVC 3 Routing and Action Links not following expected contextual Route), new path-placeholder introduced and constraint added.
Maybe it's not good enough, or you know better than this, but seems to work for me:
routes.MapRoute(
"FB", // Route name
"{path}/{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional }, // Parameter defaults
new { path = "fb" }
).RouteHandler = new RH();
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);