ASP.NET MVC Routes: How do I omit "index" from a URL - c#

I have a controller called "StuffController" with a parameterless Index action. I want this action to be called from a URL in the form mysite.com/stuff
My controller is defined as
public class StuffController : BaseController
{
public ActionResult Index()
{
// Return list of Stuff
}
}
I added a custom route so the routes are defined like this:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
// Custom route to show index
routes.MapRoute(
name: "StuffList",
url: "Stuff",
defaults: new { controller = "Stuff", action = "Index" }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
But when I try to browse to mysite.com/stuff I get an error
HTTP Error 403.14 - Forbidden
The Web server is configured to not list the contents of this directory.
The URL mysite.com/stuff/index works fine. What I am doing wrong?

HTTP Error 403.14 - Forbidden The Web server is configured to not list the contents of this directory.
The error indicates that you have a virtual directory (probably a physical one) in your project called /Stuff. By default, IIS will first reach this directory and look for a default page (for example /index.html), and if no default page exists will attempt to list the contents of the directory (which requires a configuration setting).
This all happens before IIS passes the call to .NET routing, so having a directory with the name /Stuff is causing your application not to function correctly. You need to either delete the directory named /Stuff or use a different name for your route.
And as others have mentioned, the default route covers this scenario so there is no need for a custom route in this case.
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
// Passing the URL `/Stuff` will match this route and cause it
// to look for a controller named `StuffController` with action named `Index`.
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}

It seems that your scenario is covered fine by default route, so there is no need for a custom Stuff one.
As to why the error is thrown, the fact that action is listed in defaults does not mean that it is actually becoming a part of a route. It should be mentioned in the route, otherwise it appears as there is no action at all. So what I think happens here is that first route is matched, but it cannot be processed as there is no action specified, so MVC passes request on to IIS, which throws the named error.
The fix would be simple:
// Custom route to show index
routes.MapRoute(
name: "StuffList",
url: "Stuff/{action}",
defaults: new { controller = "Stuff", action = "Index" }
);
But again, you shouldn't need that at all.

Related

Why does an ASP.NET MVC route have a name?

In an ASP.NET MVC 5 web application, there is a RouteConfig class that registers many routes. In all of the examples I have seen so far, only the "Default" route has a non-empty name. The URL pattern and default route values seem to be sufficient to correctly associate any URL to the controller and action to execute. So, is there any benefit to naming a route? For debugging or logging? Just for self-documenting the code?
For example:
public class RouteConfig
{
public static void RegisterRoutes( RouteCollection routes )
{
routes.IgnoreRoute( "{resource}.axd/{*pathInfo}" );
// Most pages.
routes.MapRoute( name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "home", action = "index", id = UrlParameter.Optional }
// A special page.
// Should the route have a name?
routes.MapRoute( name: "",
url: "special",
defaults: new { controller = "special", action = "index", HttpVerbs = HttpVerbs.Get } );
}
}
The main reason to name a route is so that if you need a link to a route from somewhere in the app, you can reference the name. That way if the actual URL/path changes you don't break everywhere in the code that references that route.

How can I implement routing for an extended routes in the RouteConfig file without creating a route error

I implemented a logout link in an ASP.NET MVC 5 application (C#), and it works perfectly with the current routing of my application:
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Login", id = UrlParameter.Optional });
When I log in, I see the logout button and it works seamlessly.
However, I have other areas of the app that are designated for registered users (for which I have not considered the routing), and after going to an area designed for authorized users and I attempt to log out, I get the following path returned when the browser displays an error:
/Managers/Home/LogOff
I have a home controller with an action link of Logoff, but I'm uncertain how to represent my Managers folder (along with others on that level). And I could not find (and really, I have not searched exhaustively) readily available documentation of routing beyond what I implement in my routes.MapRoute file (seen above). I would appreciate it if someone can steer me in the right direction or tell me the correct pattern to use. I can't imagine the issue being anything other than routing. Thanks!
RouteConfig.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
namespace HRCoreUS
{
using System.Web.UI.WebControls;
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapMvcAttributeRoutes();
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Login", id = UrlParameter.Optional });
}
}
}
I see two options for you to define a route that suits you.
Fist one, if you want to log off all users from a specific folder, you can use a route like the following one. The keyword 'allusers' prevents you to have a conflict with your Default route. This also make your route more 'readable'.
config.Routes.MapHttpRoute(
"LogOffAllUserFromFolder",
"{controller}/{action}/allusers/{folder}",
new { controller = "Home", action = "Logoff", folder = RouteParameter.Optional });
This endpoint would then looks like :
/home/logoff/allusers/Managers
Second one, if you want to log off a specific user (for example user with id 124) from a 'Managers' folder, you can try the following route that won't conflict with Default since there are 4 arguments
config.Routes.MapHttpRoute(
"LogOffSpecificUserFromFolder",
"{controller}/{action}/{id}/{folder}",
new { controller = "Home", action = "Logoff", id = RouteParameter.Optional, folder = RouteParameter.Optional },
new { id = #"\d+" });
The endpoint would then looks like
/home/logoff/124/Managers
As you're id is an int I suppose, you can tell it by adding that to your route definition
new { id = #"\d+" }
And as {folder} is a string you don't need any other definition as it is the default type.
If I missunderstand your point, I hope this will gives you tips on how to declare MVC routes.
Take care.
If you want to use Attribute routing inside your Controllers folder Simply create a ManagersController
[RoutePrefix("managers")]
public class ManagerController
Then simply add a [Route("")] attribute on every function inside the controller.
Depending on any URL parameters you need define them in the Route attribute.
[HttpGet, Route("logoff}")]
public HttpResponseMessage LogOff() //route would be Managers/logoff
[HttpGet, Route("{id:int/view-profile}")]
public HttpResponseMessage ViewProfile(int id) //route would be Managers/4/view-profile
[HttpPost, Route("{id:guid/save-profile}")]
public HttpResponseMessage ViewProfile(Guid id) //route would be Managers/405750C8-0B3F-4EF2-A2A2-17A1EFBCDA39/save-profile
More info on attributes -
https://blogs.msdn.microsoft.com/webdev/2013/10/17/attribute-routing-in-asp-net-mvc-5/
or for general catchall maproute in Routeconfig you would want something like this:
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { action = "Index", id = UrlParameter.Optional }
);
Make sure this is the last route defined in your RouteConfig.
Then inside a ManagersController.cs create a
public HttpResponseMessage LogOff()
This Route would be localhost.com/managers/logoff
But from how I interpreted your question it seems as if your Authentication logic is tied to a specific controller (Home)? You should have all that logic encapsulated in something like a AuthenticationController which could handle your login / logoff logic. This way its not dependent on anything else and you could have a route like this:
routes.MapRoute(
name: "Authentication",
url: "authentication/{action}",
defaults: new { controller = "Authentication", action = "Index"}
);
with a Login / LogOff Function. These would route to /authentication/login and /authentication/logoff

Implementing Attribute Routing

I'm trying to get a handle on attribute routing in MVC.
Initially, the routing for my sitemap controller was as follows:
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "SitemapXml",
url: "sitemap.xml",
defaults: new { controller = "Sitemap", action = "Index" }
// Additional mappings...
}
}
That works fine. But then I tried commenting out the SitemapXml routing above and instead adding an attribute in my controller.
// GET: Sitemap
[Route("sitemap.xml")]
public ActionResult Index()
{
// Generate sitemap
}
I also added the following line at the end of RegisterRoutes:
routes.MapMvcAttributeRoutes();
But now when I navigate to domain.com/sitemap.xml, I get a page not found error.
Questions:
What steps am I missing to get my routing attribute to work?
Since mappings can now be specified in two places (as attributes or set directly in the RouteCollection), what happens when those two places contradict each other?
if you remove the extension .xml , your attribute routing will work perfectly. And its better to use the extension related code in action method.
Also make sure your route config looke like (routes.MapMvcAttributeRoutes(); should exist before default route)
routes.MapMvcAttributeRoutes();
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Account", action = "Login", id = UrlParameter.Optional }
);

Map Custom Route ASP.NET MVC5

I haven't used .NET Routing before.
I have a URL: http://myurl.com/Account/Login/?IsIPA=true.
I want to be able to hit this URL with the following: http://myurl.com/IPA
This is the only custom route I want hit.
Can I create a route just for a single URL like this?
My code that isn't working is:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapMvcAttributeRoutes();
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
routes.MapRoute("IPA", "Account/Login/{IsIPA}", new { controller = "Account", action = "Login", IsIPA = "true" });
}
I get the error:
The constraint entry IsIPA on the route with route template Account/Login/{IsIPA}=True must have a string value or be of a type which implements System.Web.Routing.IRouteConstraint.
Route matching is similar to a switch case statement. The url parameter and any default values and constraints are all considered to determine whether or not it is a match with the incoming URL. If the route matches, it will then create a dictionary of route values based on the configuration. If the route does not match, the next route in the collection is tried until a match is found (or not).
This means the order that routes are specified is important. The default route matches any URL with 0, 1, 2, or 3 segments. Therefore, in most cases you will need to define your custom route before the default route.
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapMvcAttributeRoutes();
routes.MapRoute(
name: "IPA",
url: "IPA",
defaults: new { controller = "Account", action = "Login", IsIPA = "true" });
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
The above configuration will route http://myurl.com/IPA to the Controller named Account and Action method named Login, and pass the additional route key IsIPA. This same URL will be built for the Controller/Action/IsIPA combination because it is the first one that matches in the list.
Note that the original URL http://myurl.com/Account/Login/?IsIPA=true will still work and still route to the same location. This configuration just adds an extra route to that resource.
Without testing it, I think that you want this:
routes.MapRoute("IPA", "Account/Login/{IsIPA}",
new { controller = "Account", action = "Login", IsIPA = "true"});

RouteConfig setup to accept an optional parameter

I am having problems setting up the RouteConfig file to accept an optional record ID as part of the URL like in the examples below.
http://localhost/123 (used while debugging locally)
or even
http://www.foobar.com/123
Ideally, I would like to have the record ID (123 as in the examples above) be passed in as a parameter to the Index view of the Home controller. I had thought that the default routeconfig would suffice for this (using the ID as an optional element), but apparently the application is apparently trying to direct the browser to a view called '123' which obviously doesn't exist.
My current RouteConfig.cs looks like this:
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 }
);
}
Any assistance on this would be greatly appreciated.
Your routing says:
{controller}/{action}/{id}
that's your site then your controller, then your action and then an id.
You want just site then id:
routes.MapRoute(
name: "Default",
url: "{id}",
defaults: new { controller = "Default", action = "Index", id = UrlParameter.Optional }
);
This would then hit a controller at
public class DefaultController : Controller
{
public ActionResult Index(int id)
{
}
}
"{controller}/{action}/{id}" tells MVC that a route may have a controller name, or it may have a controller name followed by an action name, or it may have those two followed by an ID. There's no way, given just an ID, for the routing to understand that it's supposed to be an ID and not an action or a controller name.
If you're never planning to have any other controllers or actions, something like this might work:
routes.MapRoute(
name: "Default",
url: "{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
But that's probably a bad idea. Pretty much every site has at least one key word to indicate what the ID represents. For example, StackOverflow has "/questions/{id}".

Categories

Resources