如何在MVC Core中动态选择控制器

我有一种情况,网站可能需要一个链接,根据数据库结果重定向到某些控制器。

例如:

site.com/abcd

需要从Item Controller返回结果,通常称为/ item / view / 123

这里的关键是我不能将abcd硬编码到路由中。 一些链接可能会转到项目控制器,其他链接可能会转到订单控制器。

我已经尝试了一个到控制器的catchall路由,然后加载所需的控制器,但环境没有设置,所以它不能正常工作(它找不到视图)。

您可以通过实现IRouter获得您想要的任何行为,如本答案所示 ,包括将逻辑基于来自外部源(例如配置文件或数据库)的数据。

这比捕捉路线灵活得多,因为它可以让你动态选择控制器。

 public class MyRoute : IRouter { private readonly IRouter innerRouter; public MyRoute(IRouter innerRouter) { if (innerRouter == null) throw new ArgumentNullException("innerRouter"); this.innerRouter = innerRouter; } public async Task RouteAsync(RouteContext context) { var requestPath = context.HttpContext.Request.Path.Value; if (!string.IsNullOrEmpty(requestPath) && requestPath[0] == '/') { // Trim the leading slash requestPath = requestPath.Substring(1); } if (!requestPath.StartsWith("abcd")) { return; } //Invoke MVC controller/action var oldRouteData = context.RouteData; var newRouteData = new RouteData(oldRouteData); newRouteData.Routers.Add(this.innerRouter); newRouteData.Values["controller"] = "Item"; newRouteData.Values["action"] = "View"; newRouteData.Values["id"] = 123; try { context.RouteData = newRouteData; await this.innerRouter.RouteAsync(context); } finally { // Restore the original values to prevent polluting the route data. if (!context.IsHandled) { context.RouteData = oldRouteData; } } } public VirtualPathData GetVirtualPath(VirtualPathContext context) { VirtualPathData result = null; var values = context.Values; var controller = Convert.ToString(values["controller"]); var action = Convert.ToString(values["action"]); var id = Convert.ToString(values["id"]); if ("Item".Equals(controller) && "View".Equals(action)) { result = new VirtualPathData(this, "abcd?id=" + id); context.IsBound = true; } // IMPORTANT: Always return null if there is no match. // This tells .NET routing to check the next route that is registered. return result; } } 

用法

 // Add MVC to the request pipeline. app.UseMvc(routes => { routes.Routes.Add(new MyRoute( innerRouter: routes.DefaultHandler) ); routes.MapRoute( name: "default", template: "{controller=Home}/{action=Index}/{id?}"); // Uncomment the following line to add a route for porting Web API 2 controllers. // routes.MapWebApiRoute("DefaultApi", "api/{controller}/{id?}"); }); 

GetVirtualPath应该反映RouteAsync作用。 RouteAsync将URL转换为路由值, GetVirtualPath应将相同的路由数据转换回相同的URL。

实现此目的的最简单方法是使用数据结构在这两个数据点之间创建双向映射(如链接的答案中所示),这样您就不必在这两种方法中不断更改逻辑。 这个数据结构应该被缓存,而不是做太多资源密集的事情,因为每个请求都会使用它来确定每个URL的发送位置。

或者,您可以为每个逻辑逻辑创建单独的路径,并在应用程序启动时将它们全部注册。 但是,您需要确保它们以正确的顺序注册,并且每条路由仅匹配正确的URL集和正确的RouteValues集。

注意:对于此类场景,您几乎不需要使用RedirectToAction 。 请记住,重定向会向浏览器发送HTTP 302请求,告知它查找服务器上的其他位置。 在大多数情况下,这是不必要的开销,因为将初始请求路由到所需的控制器会更有效。