Fallback route in ASP MVC if no action exists

The default route in MVC {controller}/{action}/{id} for the most part quite useful, as it can set a default value if the incoming URL does not include a parameter, but there is also a way to specify a default action when actions does not exist on the controller?

What I want to achieve is to have controllers with a few specific actions, and then my own catchall, which uses a url to grab content from the underlying CMS.

For example, a product controller would look something like this:

 public class ProductsController: Controller{ public ActionResult ProductInfo(int id){...} public ActionResult AddProduct(){...} public ActionResult ContentFromCms(string url){...} } 

If the default route would handle /Products/ProductInfo/54 , etc., but the request URL /Products/Suppliers/Acme would return ContentFromCms("Suppliers/Acme"); (sending the URL as a parameter would be nicer, but not needed, and a method without parameters, in which I get it from the request, is good).

Currently, I can imagine two possible ways to achieve this:

Create a new constraint that reflects over the controller to see if it has an action with the given name and use it in the {controller}/{action}/{id} route, which allows me to have a more general look, like {controller}/{*url} .

HandleUnknownAction on the controller.

The first approach seems to be a pretty workaround for checking this out, and for the second I don't know the internal components of MVC and Routing well enough to know how to proceed.

Update

There were no answers, but I thought I would leave my decision if someone finds this in the future or offers improvements / better ways for people

For the controllers that I wanted to have my own, I gave them an interface

 interface IHasDefaultController { public string DefaultRouteName { get; } System.Web.Mvc.ActionResult DefaultAction(); } 

Then I got the result from ControllerActionInvoker and redefined FindAction. This calls the underlying FindAction function, then if the base returns null and the controller enters the interface, I again call FindAction with the default action name.

 protected override ActionDescriptor FindAction(ControllerContext controllerContext, ControllerDescriptor controllerDescriptor, string actionName) { ActionDescriptor foundAction = base.FindAction(controllerContext, controllerDescriptor, actionName); if (foundAction == null && controllerDescriptor.ControllerType.GetInterface("Kingsweb.Controllers.IWikiController") != null) { foundAction = base.FindAction(controllerContext, controllerDescriptor, "WikiPage"); } return foundAction; } 

Since I also need parameters from routing, I also replace RouteData at the beginning of the default ActionResult on the controller

 ControllerContext.RouteData = Url.RouteCollection[DefaultRouteName].GetRouteData(HttpContext); 
+4
source share
1 answer

You fit very well. As a note:

replace

 controllerDescriptor.ControllerType.GetInterface("Kingsweb.Controllers.IWikiController") != null 

with

 typeof(Kingsweb.Controllers.IWikiController).IsAssignableFrom(controllerDescriptor.ControllerType) 

this is a more strongly typed way, and then passing the interface name through a string: what if you change the namespace tomorrow?

+2
source

Source: https://habr.com/ru/post/1332752/


All Articles