The simplest example with MVC attribute routing doesn't work - asp.net-mvc

I use VS2013 and created MVC application by wizard. I also deleted all extra files and have the following:
1) RouteConfig.cs
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapMvcAttributeRoutes();
}
2) HomeController.cs
public class HomeController : Controller
{
[Route("Home/Index")]
public ActionResult Index()
{
return View();
}
}
3) Index.cshtml
#{
ViewBag.Title = "Home Page";
}
Home page
I've got the page with error:
HTTP 403.14 - Forbidden
But, if I add to URL in browser's address bar manually - Home/Index:
http://localhost:50600/Home/Index
The page appears.
What I'm doing wrong?

Remove the "Home" from the route as the controller name HomeController is already starting your route with "Home". If you want to change that "Home" prefix, you can add an attribute to the HomeController class to define that.
Also, the default route name for an action will match the action name, so in this case you could use [Route("")] and the url /Home/Index would work.

My guess is that when you try this url:
http://localhost:50600
It does not work because you have removed the default route from your routes config. I don't know if you removed it yourself, but RoutesConfig.cs file usually comes with the following default route:
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
This code ensures that if the user does not provide the controller or the action the site will default to index action of the home controller (you ca see that under the defaults parameter). This would also explain why it works when you try this route:
http://localhost:50600/Home/Index.

I think I know what's your problem now. You're expecting the default url to show up your Index View in HomeController but you've not setup the default route. You can set the default route by adding the following lines in your RouteConfig.cs
config.Routes.MapRoute(
name: "Default",
routeTemplate: "{controller}/{action}",
defaults: new { controller = "Home", action = "Index" }
);
Alternatively, if you wish to use attribute routing only without mixing with route template, you can just add the default route as following:-
config.Routes.MapRoute(
name: "Index",
url: "",
defaults : new { controller = "Home", action = "Index" }
);

Related

Adding routing in MVC 5

I have a requirement in which I have to map the below url
/amer/us/en/ = Home controller
/amer/us/en/login/index = Home controller
/amer/us/en/confirmation = Confirmation controller
along with the regular default action.
Eg if user goes to
http:\\test.com --> http://test/home/index
http:\\test.com/amer/us/en/login/index --> http://test/home/index
http:\\test.com/amer/us/en/ --> http://test/home/index
I was looking into attribute routing and so I added the below code in HomeController
[RoutePrefix("amer/us/en/")]
[Route("{action=index}")]
public class HomeController : Controller
{
}
and I am getting this error
The route prefix 'amer/us/en/' on the controller named 'Home' cannot begin or end with a forward slash and also the default routing is not working now so http://test.com is not loading anything. Below is my default RouteConfig class.
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 }
);
}
Very new to MVC. Can someone tell me what I am doing wrong here.
Routing in MVC works either by defining your routes in the RouteConfig class or by attribute routing (or you can use Areas). Routing with RouteConfig works with the order you define the routes with. When a request comes, MVC will try your routes from top to bottom and execute the first one that it can match with the requested url. So the routing need in your example can be achieved by:
routes.MapRoute(
name: "RootLogin",
url: "amer/us/en/login/index/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
routes.MapRoute(
name: "DefaultAmer",
url: "amer/us/en/{controller}/{action}{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
this will map login as a special route and all the other /amer/us/en/ routes will go to whatever controller comes after and whatever action of it. The last route, if the request does not start with /amer/us/en will perform the default behavior.
Looks like, however, you want to define /amer/us/en/ as an Area, so you might want to take a look into that as well.

MVC route attribute no controller

I'm building an intranet where I have the following home controller:
[Route("{action=index}")]
public class HomeController : Controller
{
public ActionResult Index()
{
return View(HomeModelBuilder.BuildHomeModel());
}
public ActionResult FormsHome()
{
return View(HomeModelBuilder.BuildFormsHomeModel());
}
}
I'm trying to get my forms homepage to have a url of http://intranet/forms so I thought I could do this using the following routing attribute:
[Route("~/forms")] // have also tried 'forms' and '/forms'
public ActionResult FormsHome()
but when I go to the url, it complains that multiple controllers have that route:
The request has found the following matching controller types:
HRWebForms.Website.Controllers.ChangeDetailsController
HRWebForms.Website.Controllers.InternalTransferController
HRWebForms.Website.Controllers.LeaverController
...
I have also tried adding [RoutePrefix("")] to the controller but this didn't work either
Is there a way to give that action a url of "forms" (without any controller or without adding a separate forms controller with an index) by just using routing attributes?
You could try adding [RoutePrefix("forms")] to your controller, but this will result in all your actions expecting the same prefix.
There is a walkaround for this too (by using [Route("~/RouteParam/AnotherRouteParam")] to have Route "RouteParam/AnotherRouteParam") but it seems to me that FormsController would cost less work.
Ok so ranquild's comment pushed me in the right direction. In my route config, I had the default route of
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
So that my homepage would still work on the url with nothing in. If I changed this to
// Needed for homepage
routes.MapRoute(
name: "Home",
url: "",
defaults: new { controller = "Home", action = "Index" }
);
// Needed for Html.ActionLink to work
routes.MapRoute(
name: "Default",
url: "{controller}/{action}",
defaults: new { controller = UrlParameter.Optional, action = UrlParameter.Optional }
);
It seemed to solve the problem

ASP.Net MVC Redirect Specific Routes to External Site

I have an nicely functioning ASP.Net MVC site using the simple standard routing scheme:
routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = "" }
);
My client would like to redirect the static pages to a secondary site, so that they can edit them, template-style, at will. The pages that actually do something will remain on the original site.
What I need to do is set up routes for my functional views/controller-actions and redirect the remaining urls to the external site regardless of whether or not the specified url has a matching controller/action. I don't want to mess with the existing code, but use routing to execute some of the pages and redirect from others.
For example:
mysite.com/sponsors/signup would be executed
mysite.com/sponsors/information would be redirected
Even though the sponsors controller contains actions for both signup and information and there are existing views for both signup and information.
So far, I have been unable to wrap my head around a way to do this.
Any ideas?
You can use attribute routing to make it easier.
Your RouteConfig will look like below:
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapMvcAttributeRoutes(); // enable attribute routing
routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = "" }
);
}
}
Then you can add an action like below:
public class SponsorsController : Controller
{
[Route("sponsors/information")]
public ActionResult RedirectInformation()
{
return RedirectPermanent("http://yoururl.com");
}
}
EDIT ONE
If you don't want to use attribute routing, you are still going to need the action but your RouteConfig will look like below:
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
//The order is important here
routes.MapRoute(
name: "redirectRoute",
url: "sponsors/information",
defaults: new { controller = "Home", action = "RedirectToInformation"}
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
}
In routing like this, if the match is found the rest of the routes are ignored. So, you'd want to put most specific route on top and most general in the bottom
EDIT TWO (based on the comment)
You can put a simple appsettings in Web.config like below:
<appSettings>
<add key="UseAttributeRouting" value="true" />
</appSettings>
Then in RegisterRoutes you can read it like below and make the decision.
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
if (Convert.ToBoolean(ConfigurationManager.AppSettings["UseAttributeRouting"]))
{
routes.MapMvcAttributeRoutes();
}
else
{
routes.MapRoute(
name: "redirectRoute",
url: "sponsors/information",
defaults: new {controller = "Home", action = "RedirectToInformation"}
);
}
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
}
The browser sometimes caches these redirects, so you may want to recommend clearing browser caches if you change these settings. Check this superuser post for clearing the cache for Chrome.
You have two options
i) This is perfect use case to write a custom mvchandler that is instatiated by a custom IRoutehandler. You can follow this example.
http://www.eworldui.net/blog/post/2008/04/aspnet-mvc---legacy-url-routing.aspx.
ii) You can write HttpHandler for these matching paths you can redirect them to the other site.
Step 1: add this to RouteConfig.cs
routes.IgnoreRoute("yourwebpage.aspx");
Step 2: create new file at root of website called "yourwebpage.aspx"
Step3: Inside yourwebpage.aspx put:
<%
Response.RedirectPermanent("http://yourwebsite.com");
%>
It is pretty simple.
Create an Action
public ActionResult ext(string s)
{
return Redirect(s);
}
And in Routeconfig file add
routes.MapRoute(name: "Default17", url: "routeyouwant", defaults: new { controller = "Home", action = "ext", s = "http://externalurl.com" });

Adding #Html.Action to _layout errors with "No route in the route table matches the supplied values."

My end goal is to have a <Script> line in the _layout that will be generated from Database data. Step 1 for me is to simply display a comment line in the "head" portion of the page. I have a test program that does this just fine. When I copy the code over to my site I get "No route in the route table matches the supplied values." when I run the web. The error is pointing to the #Html.Action("OVars","MyO") statement in _layout.
I have tried to use Glimpse and RouteDebugger but I am not able to get any data on what this route actually is being generated as and then why it is failing. The output from RouteDebugger has a list of about 30 to 40 routes of which only the "Default" route is in Global.asax.cs. So I can only assume that these routes are being created elsewhere in the code.
#Html.Action("OVars","MyO")
The controller contains:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace MySite.Controllers
{
public class MyOController : Controller
{
// ChildActionOnly will not allow direct displaying of this page
[ChildActionOnly]
public ActionResult OVars()
{
return PartialView("_OVars");
}
}
}
The _OVars partialview:
<!-- Test comment simply for displaying -->
Global.asax.cs
public static void RegisterRoutes(RouteCollection routes)
{
ViewEngines.Engines.Clear();
ViewEngines.Engines.Add(new RazorViewEngine());
routes.IgnoreRoute("{resource}.ico");
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
}
Any suggestions on what to look at would be a help. For all I know there is a parameter set that I am not aware of that needs to be changed.
Thanks
Solved (but I do not understand "Areas, something more to learn").
I changed the line #Html.Action("OVars","MyO") to #Html.Action("OVars","MyO", new { area = string.Empty }).
I kept making code changes until I received an error message that when I looked it up found some text similar to "This error could also happen if inside a view in your area, you use the Html.Action helper. This helper will always use the area as a prepend, unless you specifically tell it not to.". The example presented was to add , new { area = string.Empty } to the #Html.Action statement.
Now the #Html.Action is working correctly and I can move forward with having the controller access the database for information.
Next up on the learning agenda is "Understanding Areas".
In case someone has the same struggle as me.
I had a custom route:
routes.MapRoute(
name: "MyPrefixRoute",
url: "myprefix/{code}/{name}",
defaults: new { controller = "Account", action = "MyPrefix", name = UrlParameter.Optional }
);
And then my default route (which is a slight variation of the MVC default route):
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}/{name}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional, name = UrlParameter.Optional }
);
The exception was only raised when browsing /myprefix/123/the-name and not /myprefix/123?name=the-name nor /myprefix/123.
The only thing that fixed it was to add a third custom route without UrlParameter.Optional:
routes.MapRoute(
name: "DefaultAction",
url: "{controller}/{action}",
defaults: new { controller = "Home", action = "Index" }
);
Although it works now, I would be happy to get an explanation ;-)

ASP.NET MVC 4 Routing Query - Passing query-string to Index Action

I have a controller with an index action.
public ActionResult Index(int id = 0)
{
return view();
}
I wish to pass id into the index action, however it doesnt appear to work in the same way as the details action.
e.g. if I want to pass id 4 into the index action, I have to visit url:
http://localhost:8765/ControllerName/?id=4
With the details Action... I can do this.
http://localhost:8765/ControllerName/Details/4
What I want to do with Index is something like...
http://localhost:8765/ControllerName/4
When I visit this url, I get an 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.
Requested URL: /fix/1
Version Information: Microsoft .NET Framework Version:4.0.30319; ASP.NET Version:4.0.30319.17929
Is this possible? How can I get MVC to automatically treat the index action in the same way as the details one?
Thanks
UPDATE - MY CURRENT ROUTES CONFIG
public class RouteConfig
{
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 }
);
}
}
UPDATE NEW RouteConfig Class still doesn't work when I visit localhost:1234/Fix/3
public class RouteConfig
{
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: "FixIndexWithParam",
url: "Fix/{id}",
defaults: new { controller = "Fix", action = "Index", id = UrlParameter.Optional });
}
}
Update It's worth pointing out, /ControllerName/Index/4 should work with the default route.
With the default route there, it expects the second parameter to be the controller name.
So with the Default Route /ControllerName/4 is being interpereted as ControllerNameController Action 4, which of course does not exist.
If you add
routes.MapRoute(
name: "IndexWithParam",
url: "{controller}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional });
before the default one it would allow
/Home/4 to be routed to HomeController action Index with id=4
I have't tested this, it may conflict with the default. You may need to specify the controller explicitly in the route, ie:
routes.MapRoute(
name: "HomeIndexWithParam",
url: "Home/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional });
(Obviously, replace Home with whatever controller you're actually wanting to route to)

Resources