简体   繁体   English

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

[英]How do I dynamically choose a controller in MVC Core

I have a situation where a site may need a link to redirect to certain controllers based on database results. 我有一种情况,网站可能需要一个链接,根据数据库结果重定向到某些控制器。

For example: 例如:

site.com/abcd site.com/abcd

needs to return the result from a Item Controller, which would normally be called as /item/view/123 需要从Item Controller返回结果,通常称为/ item / view / 123

The key here is that I can't hard code the abcd into the routing. 这里的关键是我不能将abcd硬编码到路由中。 And some links may go to an Item Controller, others may go to an Orders controller. 一些链接可能会转到项目控制器,其他链接可能会转到订单控制器。

I've tried a catchall route to a controller than then loads up the desired controller, but the environment is not set so it does not work properly (it can't find the views). 我尝试了一个到控制器的catchall路由,然后加载所需的控制器,但环境没有设置,所以它不能正常工作(它找不到视图)。

You can get whatever behavior you desire by implementing IRouter as in this answer , including basing your logic on data from an external source (such as a config file or database). 您可以通过实现IRouter获得您想要的任何行为,如本答案所示 ,包括将逻辑基于来自外部源(例如配置文件或数据库)的数据。

This is much more flexible than a catchall route because it lets you choose the controller on the fly. 这比捕获路线灵活得多,因为它可以让你动态选择控制器。

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;
    }
}

Usage 用法

// 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?}");
});

The GetVirtualPath should mirror what the RouteAsync does. GetVirtualPath应该反映RouteAsync作用。 RouteAsync converts a URL into route values, and the GetVirtualPath should convert the same route data back into the same URL. RouteAsync将URL转换为路由值, GetVirtualPath应将相同的路由数据转换回相同的URL。

The easiest way to accomplish this is to use a data structure to create a two-way mapping between these 2 data points (as in the linked answer) so you don't have to continually change the logic within these 2 methods. 最简单的方法是使用数据结构在这两个数据点之间创建双向映射(如链接的答案中所示),这样您就不必在这两种方法中不断更改逻辑。 This data structure should be cached and not do anything too resource intensive, since every request will use it to determine where to send each URL. 这个数据结构应该被缓存,而不是做太多资源密集的事情,因为每个请求都会使用它来确定每个URL的发送位置。

Alternatively, you could create a separate route for each of your individual pieces of logic and register them all at application startup. 或者,您可以为每个逻辑逻辑创建单独的路径,并在应用程序启动时将它们全部注册。 However, you need to ensure they are registered in the correct order and that each route will only match the correct set of URLs and correct set of RouteValues. 但是,您需要确保它们以正确的顺序注册,并且每条路由只匹配正确的URL集和正确的RouteValues集。

NOTE: For a scenario such as this you should almost never need to use RedirectToAction . 注意:对于此类场景,您几乎不需要使用RedirectToAction Keep in mind redirecting will send an HTTP 302 request to the browser, which tells it to lookup another location on your server. 请记住,重定向会向浏览器发送HTTP 302请求,告知它查找服务器上的其他位置。 This is unnecessary overhead in most cases because it is much more efficient just to route the initial request to the controller you want. 在大多数情况下,这是不必要的开销,因为将初始请求路由到所需的控制器会更有效。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何从 ASP.NET Core 6 MVC EF 中的 controller 中的另一个 controller 实例化服务? - How do I instantiate a service from another controller in a controller in ASP.NET Core 6 MVC EF? 如何动态制作2个数组并从jQuery发送到控制器MVC核心 - How to make 2 array dynamically and sent from jQuery to controller mvc core 如何在 ASP.NET 内核中为动态加载的程序集(控制器)设置请求超时 - How do I set the request timeout for dynamically loaded assembly (controller) in an ASP.NET Core 如何在MVC Core控制器引发异常时不丢弃的中间件中设置HTTP响应标头? - How do I set a HTTP response header in middleware which is not discarded when a MVC Core controller throws an exception? 如何将输入从 Blazor 组件传递到核心 MVC 控制器? - How do I pass input from a Blazor component to a Core MVC Controller? 我如何告诉 MVC 中间件我的 class 是 ASP.NET Core 中的一个 Controller? - How do I tell the MVC middleware that my class is a Controller in ASP.NET Core? 如何通过代码禁用MVC控制器? - How do I disable an MVC controller by code? 如何在 mvc 控制器中写入数据? - How do I write data in mvc controller? 我必须为管理员控制器mvc选择什么型号 - What model i have to choose for the admin controller mvc MVC 4,动态选择动态MapRoute - MVC 4, choose dynamically MapRoute on the fly
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM