I'm using attribute routing from ASP.NET 5 RC, included in the Visual Studio 2013 RC release.
I'd like for the root path, /, to lead to the canonical /Home/Index path, but I can't find a way to do this with just attribute routes. Is it possible, and if not, how would I do it if I'm also using OWIN SelfHost? In other words, I'm setting up my own HttpConfiguration class manually in the WebApp.Start<T> method (where T has a Configure(IAppBuilder) method invoked at startup) and not going through the RouteTable.Routes object. Or should I be going through the RouteTable.Routes object? I haven't had much luck with that when I tried it...
EDIT: Here's what I've tried so far:
// normal Web API attribute routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultWeb",
routeTemplate: "{controller}/{action}",
defaults: new { controller = "Home", action = "Index" }
);
The second try below looks a little dubious, since it's not clear how my HttpConfiguration object is related to the static RouteTable.Routes object:
// normal Web API attribute routes
config.MapHttpAttributeRoutes();
RouteTable.Routes.MapRoute(
name: "DefaultWeb",
url: "{controller}/{action}",
defaults: new { controller = "Home", action = "Index" }
);
You can set the default route for the app like this:
[Route("~/", Name = "default")]
public ActionResult Index() {
return View();
}
For anyone who is using .NET Core 1.x, you'll need to do this as well as use Jay's answer above.
In your Startup.cs file, add this to your Configure method.
app.UseMvcWithDefaultRoute();
Then, any controller action you want as the default, put the attribute [Route("", Name = "default")]
Related
Is there a simple catch-all way to ensure only rewritten URLs can invoke a controller?
For example, if we have a URL www.somesite.com/about pointing to action "About" in controller "Shared", can it be ensured that any requests to www.somesite.com/shared/about end up at the rewritten URL, in this case www.somesite.com/about?
In other words, the user should not be able to just type /controller/action without being redirected to the rewritten URL.
However, we don't want to actively check and redirect but were hoping for some built-in function of MVC. The only suggestions I found along those lines were ChildActionOnly and HttpPost attributes, but they don't seem to be the answer (normal links don't work).
As mentioned, we're looking for something simple, more or less built-in - if it doesn't exist then so be it...
The built-in way of blocking routes is to use IgnoreRoute. It short-circuits routing and always makes the path throw a 404 not found.
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
// Ignore /Home/About
routes.IgnoreRoute("Home/About");
// Register /About
routes.MapRoute(
name: "About",
url: "About",
defaults: new { controller = "Home", action = "About" }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
}
Note that under the covers, it uses the StopRoutingHandler, which can be used (as a replacement for MvcRouteHandler) in any custom Route or RouteBase implementation to make more dynamic ignore rules than this.
NOTE: It is extremely important that IgnoreRoute is registered before the route you want to ignore in the route table.
I have Home controller and Details action which received int id parameter.
I want map "/" url to Home controller, Details action, id = 1.
Also I want map urls like "/st15" to Home controller, Details action, id = 15.
So I wrote following attributes
[Route("~/{id:int:min(1):max(1)=1}")]
[Route("st{id:int:min(2)}")]
public ActionResult Details(int id)
{...}
The problem is url "/1" is also mapping to this action, but I need 404 for it
I would recommend leveraging the RouteConfig.cs file that is created with default MVC projects in VS2012.
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Details",
url: "/st{id}",
defaults: { controller = "Home", action = "Details", id = "1" }
);
routes.MapRoute(
name: "Default",
url: "/",
defaults: new { controller = "Home", action = "Details", id = "1" }
);
}
This will try to match urls to those two designated routes in the order you add them to your RouteConfig file. Since the 'Default' route doesn't have any additional parameters on its URL definition, a call to "/1" won't match a route and you'll get a 404.
If you used a blank project, adding a RouteConfig file is a trivial matter. Just add a RouteConfig.cs file and define the class to contain the RegisterRoutes method I listed, then in your Global.asax file's Application_Start function, add a line for
RouteConfig.RegisterRoutes(RouteTable.Routes);
And you'll be good to go.
(EDIT)
I'm not certain that there still won't be conflicts, but you can combine Attribute routing with the standard routing paradigm by simply adding
routes.MapMvcAttributeRoutes();
to your RegisterRoutes function, immediately after the IgnoreRoute call, and the application will always defer to your Route attributes first, then check the defined routes.
However, since you've indicated you'd like to avoid using the standard routing approach altogether, you could simply define the attributes to take the following routes:
[Route("~/")]
[Route("st{id:int:min(2)}")]
public ActionResult Details (int id = "1")
{...}
To set a default value for the id parameter and avoid trying to handle the case in the attribute itself. Since you define a minimum value for the {id} parameter in your second Route attribute, you shouldn't have to worry about the case of "/st" trying to route to that action. It wouldn't match either defined route and so would 404.
Example : My User will enter www.xyz.com/Promo/PROMO123
where "PROMO123" is value, which i require.
above code produces error :
Server Error in '/' Application.
The resource cannot be found.
Description: HTTP 404. The resource you are looking for (or one of its dependencies) could have been removed, had its name changed, or is temporarily unavailable. Please review the following URL and make sure that it is spelled correctly.
However
www.xyz.com/Promo/Index/PROMO123 will work properly,
but i dont want this.
How can i archive this
www.xyz.com/Promo/PROMO123
Have you tried Routing?
Such as
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
//Don't forget to add this before default one.
routes.MapRoute(
name: "PromoRoute",
url: "{controller}/{myString}",
defaults: new { controller = "Promo", action = "Index", myString = UrlParameter.Optional }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
You need to define a route pattern for this.
If you want this to work application wide, then you will need to change the default route. But I would suggest simply adding a specific route for this controller, in addition to the default route, because you probably don't want to override the default MVC routing for the whole app or you will lose the ability to use multiple actions per controller.
See your RouteConfig, try this route (MVC pre-5):
routes.MapRoute("myRoute", "PromoRoute/{id}",
new {controller="PromoRoute", action = "Index"});
With MVC5 you can add this directly to your action assuming you've enabled attribute routes:
[Route("PromoRoute/{id}")]
public ActionResult Index(string id) {
}
I've been trawling through 1000s of questions and blogs and still don't fully understand dam routing!
Along the lines of Scott Hanselman's blog I am trying to route a certain call to a .GIF to a custom HttpHandler while the rest of the MVC4 site behaves normally. I'm 90% of the way there.
So in the my RouteConfig I have
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.Add("AnalyticsRoute", new Route("analytics/a.gif", new AnalyticsRouteHandler()));
routes.MapRoute("Default", "{controller}/{action}/{id}", new { controller = "Home", action = "Index", id = UrlParameter.Optional });
routes.RouteExistingFiles = true;
}
and in my web.config I have
<handlers>
...
<add name="analytics" verb="*" path="analytics/a.gif" type="Lms.Analytics.AnalyticsHandler, Lms.Analytics" preCondition="managedHandler" />
</handlers>
Now this way, http://mysite.com/analytics/a.gif routes correctly and all is happy, however all my ActionLinks are resolving as http://mysite.com/analytics/a.gif?action=Index&controller=Category
If I reverse the order in the RouteConfig i.e.
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute("Default", "{controller}/{action}/{id}", new { controller = "Home", action = "Index", id = UrlParameter.Optional });
routes.Add("AnalyticsRoute", new Route("analytics/a.gif", new AnalyticsRouteHandler()));
routes.RouteExistingFiles = true;
}
All the links resolve just fine, but a call to http://mysite.com/analytics/a.gif results in a 404 error?
I must be doing something stupid and just can't see it?!
Thanks in advance
After finally coming across a similar post, I cracked it. The post was why is the httphandler not running
You need to ignore the path to the file you want the HttpHandler to handle
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.IgnoreRoute("analytics/a.gif");
routes.MapRoute("Default", "{controller}/{action}/{id}", new { controller = "Home", action = "Index", id = UrlParameter.Optional });
routes.RouteExistingFiles = true;
}
The problem was when I added the web.config and removed the Route.Add I got a 404. I guess the Routing engine was over ruling the Handler.
What you need to do is to create your own custom Route class.
In this class you must override the GetVirtualPath method (on MSDN), which will examine the route data, and generate the right Url. Implement it so that it returns null if your special Url it's not in the provided RequestContext.
What is going on now in your app is that you're using the standard Route class which treat the controller and action in the RouteValue dictionary as overflow paramters, so they're added to the created url.
When you add the route to the route collection use your custom class instead of the default Route class.
More explanation: when you use any method like Url.Action to create a Url, RouteCollection.GetVirtualPath is called. And this method calls the GetVirtualPath of every registered Route, until one of them returns a value different from null (the Url string).
As you're not providing your own Route class, the "standard" Route class is being used, and returning the undesired Url. If you create your custom Route class with you own GetVirtualPath implementation you'll return the desired Url.
According to Hanselman's article, you don't want to add a route for your image, you just want to set up the handler in your web.config. Have you tried removing your AnalyticsRoute and see if it works? You may also need to add an ignore route to keep MVC from trying to handle the request.
I haven't used it, but I've heard RouteMagic, written by Phil Haack, is great at troubleshooting route issues.
I have a project that I recently upgraded to ASP.NET MVC 3. On my local machine, everything works fine. When I deploy to the server, I get an error anytime I use a RedirectToAction call. It throws a System.InvalidOperationException with the error message No route in the route table matches the supplied values. My assumption is that there is some configuration problem on the server, but I can't seem to be able to figure it out.
I ran into this with areas within MVC3 when redirecting across areas. As others have said, Glimpse is very useful here.
The solution for me was to pass in the Area within the route values parameter changing:
return RedirectToAction("ActionName", "ControllerName");
to:
return RedirectToAction("ActionName", "ControllerName", new { area = "AreaName" });
I had a similar problem once with RedirectToAction and found out that you need a valid route registered that leads to that action.
Check out glimpse and see if you can get some route debugging information:
http://getglimpse.com/
You could add a route table to your RouteConfig.cs file like below:
public static void RegisterRoutes(RouteCollection routes)
{
routes.MapMvcAttributeRoutes();
var namespaces = new[] { typeof(HomeController).Namespace };
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute("name", "url", new { controller = "controllerName", action = "actionName" }, namespaces);
}
NB: the "url" is what you'd type into the address bar say: localhost:/home
After setting up the route, use RedirectToRoute("url").
Or if you'd prefer the RedirectToAction() then you don't need to set up the above route, use the defaults.
RedirectToAction(string action name, string controller name);
I hope this helps.
There's a difference with trailing slashes in routes not working with MVC 3.0. MVC 2.0 doesn't have a problem with them. I.e., if you change the following:
"{controller}.mvc/{action}/{id}/"
to:
"{controller}.mvc/{action}/{id}"
it should fix this (from this thread, worked for me). Even when you use the upgrade wizard to move to MVC 3.0, this still throws InvalidOperationException. I'm not aware whether this is what Schmalls was talking about though.
In my case, default route was missing:
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);