如何在MVC Core中动态选择控制器 [英] How do I dynamically choose a controller in MVC Core

查看:80
本文介绍了如何在MVC Core中动态选择控制器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a situation where a site may need a link to redirect to certain controllers based on database results.

例如:

site.com/abcd

site.com/abcd

需要从项目控制器(通常称为/item/view/123

needs to return the result from a Item Controller, which would normally be called as /item/view/123

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

The key here is that I can't hard code the abcd into the routing. 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).

推荐答案

您可以通过实现IRouter来获得所需的任何行为,如

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).

这比包罗万象的路线要灵活得多,因为它可以让您即时选择控制器.

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

用法

// 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.

The GetVirtualPath should mirror what the RouteAsync does. RouteAsync converts a URL into route values, and the GetVirtualPath should convert the same route data back into the same URL.

最简单的方法是使用数据结构在这两个数据点之间创建双向映射(如链接的答案中所示),因此您不必在这两种方法中不断更改逻辑.该数据结构应该被缓存,并且不要做任何占用大量资源的事情,因为每个请求都将使用它来确定将每个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集和正确的RouteValues集.

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.

注意::对于这种情况,您几乎永远不需要使用RedirectToAction.请注意,重定向会将HTTP 302请求发送到浏览器,该请求告诉浏览器在服务器上查找其他位置.在大多数情况下,这是不必要的开销,因为仅将初始请求路由到所需的控制器会更加高效.

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.

这篇关于如何在MVC Core中动态选择控制器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆