c# - 如何在 MVC Core 中动态选择 Controller

标签 c# asp.net-mvc asp.net-core-mvc

我遇到这样一种情况,站点可能需要一个链接以根据数据库结果重定向到某些 Controller 。

例如:

site.com/abcd

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

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

我已经尝试了一个到 Controller 的包罗万象的路由,然后加载了所需的 Controller ,但是环境没有设置,所以它不能正常工作(它找不到 View )。

最佳答案

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

这比 catchall 路由灵活得多,因为它允许您即时选择 Controller 。

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 应该反射(reflect) RouteAsync 的作用。 RouteAsync 将 URL 转换为路由值,GetVirtualPath 应将相同的路由数据转换回相同的 URL。

实现此目的的最简单方法是使用数据结构在这 2 个数据点之间创建双向映射(如链接的答案中所示),这样您就不必不断更改这 2 个方法中的逻辑。这个数据结构应该被缓存并且不要做任何过于占用资源的事情,因为每个请求都会使用它来确定将每个 URL 发送到哪里。

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

NOTE: For a scenario such as this you should almost never need to use RedirectToAction. Keep in mind redirecting will send an HTTP 302 request to the browser, which tells it to lookup another location on your server. This is unnecessary overhead in most cases because it is much more efficient just to route the initial request to the controller you want.

关于c# - 如何在 MVC Core 中动态选择 Controller ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35097141/

相关文章:

C# 与 VB.NET - 空结构的处理

c# - setExpressCheckout 和 SSL/TLS 错误

c# - 使用 NetworkStream 接收文件随机失败,但始终与 Thread.Sleep() 一起使用

c# - 如何在 Visual Studio 中查找 MVC Controller 方法的引用

c# - MVC 6 多个获取方法

c# - mvc核心中没有引用基础设施的配置服务

c# - 如何编辑 asp.net 图表 X 轴标签并只显示日期而不显示时间

javascript - Knockout JS - 修改 observableArray 中项目的属性

asp.net - Azure 网站上脚本/样式的长时间等待 (TTFB) 时间

c# - 将 IHttpContextAccessor 注入(inject) ApplicationDbContext ASP.NET Core 1.0