Why do you need a route defined for Html.Action? - asp.net-mvc

I have created an otherwise empty ASP.NET MVC 3 application with 2 controllers, HomeController and OtherController.
HomeController.cs looks like this:
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
}
Index.cshtml looks like this:
#Html.Action("Index", "Other")
And, of course, Othercontroller.cs:
public class OtherController : Controller
{
[ChildActionOnly]
public ActionResult Index()
{
return Content("OK!");
}
}
So far, so good. I run the app, and it tells me everything is OK!
Now, I take the default RegisterRoutes from Global.asax.cs:
public static void RegisterRoutes(RouteCollection routes)
{
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
);
}
And I crumple it up, so that no routes match OtherController:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute("Default", "", new { controller = "Home", action = "Index" });
}
Now, when I run the page, it crashes with this error message:
System.InvalidOperationException: No route in the route table matches the supplied values.
Source Error:
Line 1: #Html.Action("Index", "Other")
I specified the controller name and action name in the call to .Action. No URLs are being generated, and no requests are being made. Why does routing even need to get involved?

I think that this blog post will help you understand a little more:
http://blogs.charteris.com/blogs/gopalk/archive/2009/01/20/how-does-asp-net-mvc-work.aspx.
Essentially, routing is involved in determining which controller to 'fire up' to handle the request and appropriate action to invoke based on the parameters that you sent and the MVCRouteHandler uses those to make a decision. Just because you tell it which controller in your action does not make it magically ignore the routing table, go straight to that controller class and bypass all the other MVC goodness that happens in the back-end. Remember, these #HTML.Action methods can take a whole lot of overloads which could influence which route in the routing table to use (think URL structure for instance).
The MVC paths are not to static content and as such, have to be parsed through the URLRoutingModule which uses the routing table to decide on what to do. Since you have no matching route - you get an error.
EDIT
In my diatribe, I did not actually address your final statement. You're right, no URL was generated, but a request to the application was generated. HTML.Action still will use routing to determine what controller, action, area, parameters to use. I think it be fair to say in simple terms that it's like generating an ActionLink and clicking it for you.

Routing got involved by using Html.Action on a controller. When the engine couldn't find the "Other" HtmlHelper with an action of "Index" it defaulted to executing that path which means passing through routing. No route matched so it threw an error.
InvalidOperationException The required virtual path data cannot be found.
http://msdn.microsoft.com/en-us/library/ee721266.aspx

The solution for me was as follows:
I had the following line that was giving me an error:
#{Html.RenderAction("ActionName", "ControllerName");}
Adding a thrid parameter solved the error:
#{Html.RenderAction("ActionName", "ControllerName", new { area = string.Empty });}

Related

Attribute Routing : Object reference not set to an instance of an object

I'm using this codes to Attribute Routing .
my controller :
[RouteArea("Administrator")]
[Route("{action}")]
public partial class HomeController : Controller
{
[HttpGet]
[Route("~/Home/Template/{id}")]
public virtual ActionResult Template(string template)
{
switch (template.ToLower())// error :Object reference not set to an instance of an object..
{
case "main":
return PartialView(Url.Content(MVC.Administrator.Home.Views.Main));
default:
throw new Exception("template not known");
}
}
}
RouteConfig :
internal static class RouteConfig
{
internal static void RegisterRoutes(AreaRegistrationContext context)
{
context.MapRoute(
"Administrator_default",
"Administrator/{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
namespaces: new[] { string.Format("{0}.Controllers", typeof(RouteConfig).Namespace) });
}
}
url : http://localhost:22738/home/template/main
how to use Attribute Routing ?
Is there a way to solve the problem? I have no idea
It would behoove you spend some time reading the documenation for attribute routing. See: http://blogs.msdn.com/b/webdev/archive/2013/10/17/attribute-routing-in-asp-net-mvc-5.aspx
First, you haven't enabled attribute routing. You need the following in RouteConfig.cs:
routes.MapMvcAttributeRoutes();
Second, you can't apply [Route] to a controller class. You can use [RoutePrefix] if that's what you're looking for. However, bear in mind, that if you include a param in your route prefix, all your action must accept that param. Although, since you have a param of action here, it's entirely possible that you just don't understand how this works. You don't pass the action name with attribute routing. The action that is hit is determined by the one that has the matching route.
Third, when you define a route on an action, you need only specify the portion of the route not covered by RouteArea or RoutePrefix. Using the tilde (~) says that you want to ignore all set prefixes and define the whole route for the action, if that's what you actually want here, that's fine, but just keep in mind that you don't have to follow the /Controller/Action/{id} convention employed by the default route in RouteConfig.cs. The whole point of attribute routing is to define custom routes easily. If you're going to rely on the default route, you might as well just use that and forget about attribute routing.
Finally, in your route, you're accepting the param, id, but your action doesn't take it. Instead it has its own param of template. The params need to match or either the route will not match or the action won't be able to work.
To summarize, the following is likely what you're looking for:
[RouteArea("Administrator")]
[RoutePrefix("home")]
public partial class HomeController : Controller
{
[Route("template/{template}")]
public virtual ActionResult Template(string template)
{
switch (template.ToLower())// error :Object reference not set to an instance of an object..
{
case "main":
return PartialView(Url.Content(MVC.Administrator.Home.Views.Main));
default:
throw new Exception("template not known");
}
}
}

Attribute Routing not working in areas

Scenario: I have a Forms area in my ASP.NET MVC 5 site.
I'm trying to redirect to the Details Action which uses a custom route defined using the new Attribute Routing feature.
The RedirectToAction:
return RedirectToAction("Details", new { slug });
The action I'm redirecting to:
[HttpGet]
[Route("forms/{slug}")]
public ActionResult Details(string slug)
{
var form = FormRepository.Get(slug);
...
return View(model);
}
I would expect a redirect to http://localhost/forms/my-slug but instead the app is redirecting me to http://localhost/Forms/Details?slug=my-slug.
This means that the attribute routing is not working.
How can this be solved?
I have added the routes.MapMvcAttributeRoutes(); line to my RouteConfig:
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 = "Index", id = UrlParameter.Optional }
);
}
}
And here's my Application_Start():
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
You are probably combining convention based routing with attribute routing, and you should register your areas after you map the attribute routes.
The line
AreaRegistration.RegisterAllAreas();
should be called AFTER this line:
routes.MapMvcAttributeRoutes();
The explanation (from https://devblogs.microsoft.com/aspnet/attribute-routing-in-asp-net-mvc-5/):
If you are using both Areas with route attributes, and areas with convention based routes (set by an AreaRegistration class), then you need to make sure that area registration happen after MVC attribute routes are configured, however before the default convention-based route is set. The reason is that route registration should be ordered from the most specific (attributes) through more general (area registration) to the mist generic (the default route) to avoid generic routes from “hiding” more specific routes by matching incoming requests too early in the pipeline.
When you create a blank asp.net mvc website, add an area and start using attribute routing, you will encounter this problem because the "Add Area" action in visual studio adds the RegisterAllAreas call in your Application_Start, before the route configuration..
Alternative solution
Perhaps you do not intend to keep using convention based routing and prefer to only use attribute routing.
In this case you can just delete the FormsAreaRegistration.cs file.
Moving the AreaRegistration.RegisterAllAreas() to RouteConfig.cs wasn't enough for me. I also needed to use the AreaPrefix parameter for the RouteArea attibute:
//Use the named parameter "AreaPrefix"
[RouteArea("AreaName", AreaPrefix = "area-name-in-url")]
[RoutePrefix("controller-name-in-url")]
public class SampleController : Controller
{
[Route("{actionParameter}")]
public ActionResult Index(string actionParameter)
{
return View();
}
}
Edit: At some point, I came across a sample solution from Microsoft that nicely showed how to handle attribute routing. It also showed some nice examples of how to translate a SelectList into an array of input[type="radio"] items as well as doing the same with an array of input[type="checkbox"] items (if I recall). This sample solution is probably a better answer to this question--as well as giving some good examples on displaying radio buttons and checkbox items. If anyone knows of this sample solution, please add a comment with a link to it.

Simple MVC route does not work

I've created a new MVC project. I've created an empty controller called APIController:
public class APIController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult Test()
{
return View();
}
}
I've created the corresponding views, which are empty apart from the word 'index' and 'test'.
When I go to myurl.com/Account it works
When I go to myurl.com/Account/Test I get an xml error saying:
No HTTP resource was found that matches the request URI [...]
No type was found that matches the controller named 'Account'.
This must be a noob error. Everything else in the project is default. The RegisterRoutes method is the default:
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
What silly thing am I missing?
Bonus points if you can explain why the error is an XML error and not the standard yellow screen of death...
Problem solved.
Turns out if you call your controller APIController MVC treats it completely differently. Renaming it to ABCController worked great.
I'm not writing an API, I'm writing a website that manages an API!
Argh, thanks MVC!
Can you check if the controller you created is inheriting ApiController instead of the standard Controller?
This would also explain why the error message is being returned in XML.

Can't access (404) ASPX from a View directory while HTML and (normal) ASPX are accessible

I can access Ping.HTML and Ping.ASPX but when I try to access the view from my MVC (4.0) project (deployed to the same server, the bogus one, by F5), I get 404.
It's a vanilla project created from the template for MVC 4 with a very default view and controller (no model).
Hints on how to resolved it? I'm out of ideas...
EDIT
My RouteConfig.cs is like this
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 }
);
}
}
Controllers folder contains only one, single file called ÄDefault1Controller.cs*. It only does this:
public class Default1Controller : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult Test()
{
return View();
}
}
EDIT
The exact URLs typed in (besides the server name alone, of course) are:
> http://localhost:49642/Index
> http://localhost:49642/Index.aspx
> http://localhost:49642/Home/Index
> http://localhost:49642/Home/Index.aspx
> http://localhost:49642/Default/Index
> http://localhost:49642/Default/Index.aspx
Based on the information you've given, it sounds like a routing problem. The URL you are requesting isn't firing a controller.
EDIT
MVC works by convention, so by naming your controller Default1Controller the matching URL would start with /Default1.
In the example you've given, you can only access the Test() method by navigating to http://localhost:49642/Default1/Test, which will return the view typically located at /Views/Default1/Test.aspx (or /Views/Default1/Test.cshtml for razor-based views).
Please check out the routing overview at ASP.NET for more information about how the route table maps to controllers and actions. I should point out that the link is for the older versions of MVC, but you should get the idea.
Let me know if I can help further.
Matt

ASP.Net MVC 4. Routing a controller in a separate assembly returns error 404

I am trying to perform what looks like a straightforward task - calling a controller from a separate assembly in my project.
However, when I requesting information from that controller I'm getting error 404. I have been fighting with it for last 5-6 hours and I suspect that I may be missing something small and obvious, that is why I hope for an advise.
I found on stakoverflow similar question about routing and error 404, but as I implemented another prototype project where I didn't get such error and use a similar code structure, I believe that the problem they describe is different from mine.
For a general picture - the overall goal of what I want to achieve is implementing an area inside an independent project like it is described here.
I did everything like it is explained in the link for a small prototype (and it works perfectly) and now trying to apply for the real project.
This is how I include an assembly and manage routes:
There is a main project with one of the Areas representing a hidden child project "CommunicationBus"
"CommunicationBus" project contains a class "CommunicationBusAreaRegistration". Registering a route works correctly, I can get here with debugger and also I can see this route when I use routedebugger (see screenshot below). I also played with namespace for this class, tried to add .Areas.CommunicationBus in the end, but it didn't make any difference I could notice.
namespace MBVD.MainProject.UI
{
public class CommunicationBusAreaRegistration : AreaRegistration
{
public override string AreaName
{
get
{
return "CommunicationBus";
}
}
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"CommunicationBus_default",
"CommunicationBus/{controller}/{action}/{id}",
new { action = "Index", id = UrlParameter.Optional },
new string[] { "CommunicationBus.Controllers" }
);
}
}
}
In the Global.asax of the main project I register routes the following way:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
new[] { "MBVD.MainProject.UI.Controllers" }
);
}
If I reference here "CommunicationBus.Controllers", I can no longer open pages of the main application. However, I believe I need to register here only path to the controllers of the main application
Output path for CommunicationBus project is set to bin folder of the main project. Every time I build CommunicationBus I get a fresh .dll in the main project.
I added a simple controllers to the CommunicationBus project:
namespace CommunicationBus.Controllers
{
public class TestController : Controller
{
[HttpGet]
public string Index()
{
return "It works!";
}
}
}
I added a link to the main project
#Html.ActionLink("test","Index","Test", new {Area="CommunicationBus"}, null)
which is:
localhost:63254/CommunicationBus/Test
I use routedebugger and it doesn't show anything suspicious:
I use ASP.NET MVC 4 for both projects.
I would appreciate any ideas on what I can do to figure out why I'm getting this error.
I believe you are getting this error because of your routing order. Since /CommunicationBus/Test matches both /{controller}/{action}/{id} and /CommunicationBus/{controller}/{action}/{id} the first registered route (/{controller}/{action}/{id}) is taking precedence over the desired route, and MVC can't find a controller in the default namespace (MBVD.MainProject.UI.Controllers) called CommunicationBus.
Try registering your Area routes before your default route. Or, you can just skip that business all together and use Attribute Routing (http://blogs.msdn.com/b/webdev/archive/2013/10/17/attribute-routing-in-asp-net-mvc-5.aspx). If you go with Attribute Routing, you will need to ensure you are loading controllers from all namespaces before calling routes.MapMvcAttributeRoutes(); (if you are just including as a reference, this is no problem, it only becomes an issue if you are dynamically loading the reference).

Resources