ASP.NET MVC 5 Attribute Routing - c#

I'm trying to use route attributes in MVC 5. I created an empty MVC project in Visual Studio to experiment with and so far I can't get the routing to work. I'm using this page as a reference. I have the latest assemblies and updated all NuGet packages to their latest versions.
Here's my code:
// RouteConfig.cs
namespace MvcApplication1
{
using System.Web.Mvc;
using System.Web.Routing;
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
// Enables MVC attribute routing.
routes.MapMvcAttributeRoutes();
// The default route mapping. These are "out of the bag" defaults.
routes.MapRoute(null, "{controller}/{action}/{id}", new
{
controller = "Home",
action = "Index",
id = UrlParameter.Optional
});
}
}
}
// TestControler.cs
namespace MvcApplication1.Controllers
{
using System.Web.Mvc;
public class TestController : Controller
{
public ContentResult Output1()
{
return Content("Output 1");
}
[Route("Test/Output2")]
public ContentResult Test2()
{
return Content("Output 2");
}
}
}
#* Index.cshtml *#
#Html.Action("Output1", "Test")
#Html.Action("Output2", "Test")
The Output1() method renders properly. However, when Output2() is rendered, I get the error "A public action method 'Output2' was not found on controller 'MvcApplication1.Controllers.TestController'."

Your action is named Test2 not Output2. Change the following
#Html.Action("Test2", "Test")

This is because #Html.Action will not actually use routing. With it you explicitly specify actions and controllers.
The routing will be used when someone for instance makes the http://example.org/Test/Output2 request from a browser.

Related

Adding WebForm to MVC application to work with extension less webform page

I have an MVC application in which I have to integrated few webforms pages.
I simply added a webform "WebForm.aspx" to root and it worked without any issue when I accessed webform with file exetension http://localhost:54363/WebForm.aspx but same files doesnt work when I try to access it without file extension .aspx
http://localhost:54363/WebForm for this is get 404 error.
For this to work I made changes to Global.asax file as per the this article but it didnt work
Below is the code of Global.asax file
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;
namespace ProjectNameSpace
{
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
public static void RegisterRoutes(RouteCollection routes)
{
routes.RouteExistingFiles = true;
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.IgnoreRoute("Content/{*pathInfo}");
routes.IgnoreRoute("Scripts/{*pathInfo}");
routes.IgnoreRoute("{WebPage}.aspx/{*pathInfo}");
routes.IgnoreRoute("{resource}.ashx/{*pathInfo}");
//routes.MapRoute(
// "Default", // Route name
// "{controller}/{action}/{id}", // URL with parameters
// new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
//);
routes.MapPageRoute("home", "WebForm/", "~/WebForm.aspx", false,
new RouteValueDictionary {
{ "path", "page-not-found" },{ "pagename", "page-not-found" }
});
}
}
}
Am I going something wrong in the above code or what is the correct way of setting route for WebForm.aspx file.
UPDATE:
I managed to solve this by adding webform route code also in RouteConfig.cs file also
using AlhabtoorTennisAcademy.CustomFilters;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
namespace ProjectNameSpace
{
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
// webforms page route
//Custom route code for webform
routes.MapPageRoute("home", "WebForm", "~/WebForm.aspx", false,
new RouteValueDictionary {
{ "path", "page-not-found" },{ "pagename", "page-not-found" }
});
}
}
........
From what I see on the example above, you're adding MapPageRoute after the default MVC routing with MapRoute, hence the order of MapPageRoute is processed after MapRoute, which is wrong because routes are processed from top-most to bottom-most order (most-specific to least-specific).
In order to route webforms pages, the MapPageRoute must precede MapRoute on the top order:
public static void RegisterRoutes(RouteCollection routes)
{
routes.RouteExistingFiles = true;
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.IgnoreRoute("Content/{*pathInfo}");
routes.IgnoreRoute("Scripts/{*pathInfo}");
routes.IgnoreRoute("{resource}.ashx/{*pathInfo}");
// webforms page route
routes.MapPageRoute("home", "WebForm", "~/WebForm.aspx", false,
new RouteValueDictionary {
{ "path", "page-not-found" },{ "pagename", "page-not-found" }
});
// default MVC route
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
}
Additional note:
You can use placeholder for page names to map all webforms pages in a single MapPageRoute definition:
routes.MapPageRoute("home", "{WebPage}", "~/{WebPage}.aspx");
Related issue:
URL Routing C# mvc and Web Forms

Asp .net MVC 5 Route Attribute with id between route

I´m trying to set up a route in MVC so that when POSTing to the following url
/organizations/55/repositories
I get all the repositories for organization 55
I've tried using the following route but to no avail, it never reaches the controller action method
[Route("/organizations/{id}/repositories")]
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Repositories(long id, OrganizationSearchParametersDTO parameters)
However if I do it in the RegisterRoutes method, it works:
routes.MapRoute("OrganizationControllerRoute", "organizations/{id}/repositories", new {controller = "Organizations", action = "Repositories"});
But I'd prefer to have it running using attributes because it's our way to work
What am I doing wrong, any ideas?
If your routes.MapRoute(..) definition works, but not the [Route(...)] attribute, it means that you have not enabled attribute routing in the RouteConfig.cs file
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
// Add the following line before any route definitions
routes.MapMvcAttributeRoutes();
... // add routes.MapRoute(...) definitions as required
}
}

MVC Conventional and Attribute routing not working together

I am using conventional routing on an ASP.Net MVC project and would like to enable Attribute routing in parallel. I have created the following but I am getting a 404 on the conventional route when enabling attribute routing
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 }
);
}
Controller
[RoutePrefix("Registration")]
public class RegistrationController : Controller
{
[HttpGet]
[Route("Add/{eventId}")]
public ActionResult Add(int eventId)
{
}
}
Calling
http://localhost/Registration/Add/1
Works, while calling
http://localhost/Registration/Add?eventId=1
No longer works and responds with 404 NotFound
Should work if you make the {eventId} template parameter optional in the route template
[RoutePrefix("Registration")]
public class RegistrationController : Controller {
//GET Registration/Add/1
//GET Registration/Add?eventId=1
[HttpGet]
[Route("Add/{eventId:int?}")]
public ActionResult Add(int eventId) {
//...
}
}
The reason the two were not working is that the route template Add/{eventId} means that the route will only match if the {eventId} is present, which is why
http://localhost/Registration/Add/1
works.
By making it (eventId) optional eventid? it will allow
http://localhost/Registration/Add
to work as the template parameter is not required. This will now allow query string ?eventId=1 to be used, which the routing table will use to match the int eventId parameter argument on the action.
http://localhost/Registration/Add?eventId=1
I also got this issue. Which MVC version are you using?
I faced this issue with MVC in asp.net core.
I think this is a flaw as if you provide Routing attribute on any action method, its conventional route is over ridden and is not longer available so you get 404 error.
For this to work, you can provide another Route attribute to your action method like this. This will work
[Route("Add/{eventId}")]
[Route("Add")]

MVC 6 Views only displaying white page

When I try to make a new view in an area in MVC 6 it only displays a white page. The Home/Index action works fine, and this one will hit the controller but never displays the view. I can return content and get a display, but when I try to return the view it breaks. Any advice?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNet.Mvc;
using PmData.Models;
// For more information on enabling MVC for empty projects, visit http://go.microsoft.com/fwlink/?LinkID=397860
namespace PlantManagement.Areas.Cms.Controllers
{
[Area("Cms")]
public class AssetsController : Controller
{
// GET: /<controller>/
public IActionResult Index()
{
return View();
}
}
}
that is coupled with a blank view that calls to the main layout page.
I found the issue. Sadly, it's because I'm so new to MVC 6 / vNext so I feel silly. It was a matter of their being an issue with an item on the page, but without app.UseDeveloperExceptionPage(); being added in the configure of startup.cs it would never show me the actual error, just give me the generic 500 error and white page. Once I added that it started producing errors I could work with and gave me what I needed.
I had a similar issue, and resolved it by,
a) Create a _ViewStart.cshtml file in each area you have. i.e. Areas/Cms/Views/_ViewStart.cshtml
b) In this _ViewStart.cshtml file add
#{
Layout = "~/Areas/Cms/Views/Shared/_LayoutCms.cshtml
}
c) Add a _LayoutCms.cshtml to Areas/Cms/Views/Shared
d) In this file add the reference to the overall site layout
#{
Layout = "~/Views/Shared/_Layout.cshtml
}
and any other area specific layout code.
That fixed my blank page issue. Hopefully yours too
In addition to my last answer, try these steps
e) Make sure you have an area registration setup within your Cms area folder i.e Areas/Cms/CmsAreaRegistration.cs
public class CmsAreaRegistration : AreaRegistration
{
public override string AreaName
{
get
{
return "Cms";
}
}
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"Cms_default",
"Cms/{controller}/{action}/{id}",
new { action = "Index", id = UrlParameter.Optional }
);
}
}
f) In your App_Start/RouteConfig.cs make sure you are registering all areas by adding AreaRegistration.RegisterAllAreas() something like the following.
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
AreaRegistration.RegisterAllAreas(); //Add this//
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
}

Restrict route to controller namespace in ASP.NET Core

I'm trying to restrict the controllers of my ASP.NET Core routes to a certain namespace.
In previous versions of ASP.NET MVC there was an overload that provided a string[] namespaces parameter when adding routes. This is missing in ASP.NET MVC 6. So after some googling, I tried playing around with something like
app.UseMvc(routes => {
var dataTokens = new RouteValueDictionary {
{
"Namespaces", new[] {"ProjectA.SomeNamespace.Controllers"}
}
};
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}",
defaults: null,
constraints: null,
dataTokens: dataTokens
);
});
but it doesn't seem to do what I want. Is there a way to restrict the routing engine to a certain namespace?
Update
I just realized it may have to do something with the fact that I'm using attribute routing on each individual controller? Does attribute routing funk up the routes defined by app.UseMvc()?
Update 2
More details:
I've two completely independent Web API projects. Incidentally, a few of the routes are identical in both (ie. ~/api/ping). These projects are independent in Production, one is an endpoint for users, one is an endpoint for administrators.
I also have unit tests, using Microsoft.AspNet.TestHost. A few of these unit tests require functionality of both of these Web API projects (ie. need "admin" endpoint to fully setup a test case for "user"). But when I reference both API projects, the TestHost gets confused because of the identical routes and it complains about "multiple matching routes":
Microsoft.AspNet.Diagnostics.DeveloperExceptionPageMiddleware: Error: An unhandled exception has occurred while executing the request
Microsoft.AspNet.Mvc.Infrastructure.AmbiguousActionException: Multiple actions matched. The following actions matched route data and had all constraints satisfied:
ProjectA.SomeNamespace.Controllers.PingController.Ping
ProjectB.SomeNamespace.Controllers.PingController.Ping
at Microsoft.AspNet.Mvc.Infrastructure.DefaultActionSelector.SelectAsync(RouteContext context)
at Microsoft.AspNet.Mvc.Infrastructure.MvcRouteHandler.<RouteAsync>d__6.MoveNext()
Update:
I've found solution through using ActionConstraint. You have to add custom Action Constraint attribute about duplicate actions.
Example with duplicate Index methods.
First HomeController
namespace WebApplication.Controllers
{
public class HomeController : Controller
{
[NamespaceConstraint]
public IActionResult Index()
{
return View();
}
}
}
Second HomeController
namespace WebApplication
{
public class HomeController : Controller
{
[NamespaceConstraint]
public IActionResult Index()
{
return View();
}
}
}
Configure routing
app.UseMvc(cR =>
cR.MapRoute("default", "{controller}/{action}", null, null,
new { Namespace = "WebApplication.Controllers.HomeController" }));
Action constraint
namespace WebApplication
{
public class NamespaceConstraint : ActionMethodSelectorAttribute
{
public override bool IsValidForRequest(RouteContext routeContext, ActionDescriptor action)
{
var dataTokenNamespace = (string)routeContext.RouteData.DataTokens.FirstOrDefault(dt => dt.Key == "Namespace").Value;
var actionNamespace = ((ControllerActionDescriptor)action).MethodInfo.DeclaringType.FullName;
return dataTokenNamespace == actionNamespace;
}
}
}
First answer:
Does attribute routing funk up the routes defined by app.UseMvc()?
Attribute routing and Convention-based routing (routes.MapRoute(...) work independently. And attribute routes have advantage over convention routes.
but it doesn't seem to do what I want. Is there a way to restrict the routing engine to a certain namespace?
Answer from developers:
Instead of using a list of namespaces to group your controllers we recommend using Areas. You can attribute your controllers (regardless of which assembly they are in) with a specific Area and then create a route for that Area.
You can see a test website that shows an example of using Areas in MVC 6 here: https://github.com/aspnet/Mvc/tree/dev/test/WebSites/RoutingWebSite.
Example using Area with convention-based routing
Controller:
//Reached through /admin/users
//have to be located into: project_root/Areas/Admin/
[Area("Admin")]
public class UsersController : Controller
{
}
Configure convention-based routing:
app.UseMvc(routes =>
{
routes.MapRoute(
"areaRoute",
"{area:exists}/{controller}/{action}",
new { controller = "Home", action = "Index" });
}
Example using Area with attribute-based routing
//Reached through /admin/users
//have to be located into: project_root/Areas/Admin/
[Area("Admin")]
[Route("[area]/[controller]/[action]", Name = "[area]_[controller]_[action]")]
public class UsersController : Controller
{
}

Categories

Resources