【问题标题】:MVC routing a repeatable pattern?MVC路由一个可重复的模式?
【发布时间】:2015-12-19 23:23:31
【问题描述】:

我正在开发的网站的设计目标是让浏览器中的 URL 处于用户可以复制它的状态,并且可以从另一个浏览器/用户/机器使用该链接返回到该位置该网址已被复制。 (实际更改将通过 AJAX 发生,但 URL 会更改以反映它们所在的位置。)

示例:如果您在客户页面上查看客户 123,并在他们的订单 #456 中提取了详细信息,并且在该订单的第 6 行有完整的详细信息,那么您的网址可以简单地为 /customer/123/456/ 6

挑战来自第二个功能:用户可以添加 UI 列(类似于在选项卡视图中添加新选项卡,或在 MDI 应用程序中添加新文档)每个列都可以轻松生成可路由的 url,但我需要url 以反映 一个或多个 列。 (例如,用户在两个并排的列中同时拥有 /customer/123/456/6 和 /customer/333/55/2)

在一个完美的世界中,我希望上述场景的 url 为 /customer/123/456/6/customer/333/55/2,但我不知道 MVC 路由是否可以处理重复模式,或者,如果是的话,它是如何完成的。

这可以通过路由来完成吗?如果没有,有没有办法从 Url 获得这种类型的一个或多个功能?

【问题讨论】:

    标签: asp.net-mvc asp.net-core asp.net-core-mvc url-routing asp.net-mvc-routing


    【解决方案1】:

    您可以创建自定义路由处理程序 (see my previous answer) 或从 RouteBase 派生,例如 NightOwl888 suggested。另一种方法是简单地使用模型绑定器和模型绑定器属性。

    public class CustomerInvoiceLineAttribute : CustomModelBinderAttribute
    {
        public override IModelBinder GetBinder()
        {
            return new CustomerInvoiceLineModelBinder();
        }
    }
    
    public class CustomerInvoiceLineModelBinder : IModelBinder
    {
        public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
        {
            var path = (string)bindingContext.ValueProvider.GetValue(bindingContext.ModelName).AttemptedValue;
            var data = path.Split(new[] { "/customer/" }, StringSplitOptions.RemoveEmptyEntries);
    
            return data.Select(d =>
            {
                var rawInfo = d.Split('/');
                return new CustomerInvoiceLine
                {
                    CustomerId = int.Parse(rawInfo[0]),
                    InvoiceId = int.Parse(rawInfo[1]),
                    Line = int.Parse(rawInfo[2])
                };
            });
        }
    }
    

    您通过指定星形路线数据来定义路线。这意味着路由参数将包含动作之后的所有内容

    routes.MapRoute(
        name: "CustomerViewer",
        url: "customer/{*customerInfo}",
        defaults: new { controller = "Customer", action = "Index" });
    

    然后在您的控制器中,使用上面定义的自定义模型绑定器将您的参数绑定到与星形路由参数相同的名称:

    public ActionResult Index([CustomerInvoiceLine] IEnumerable<CustomerInvoiceLine> customerInfo)
    {
        return View();
    }
    

    您需要在解析过程中添加验证,可能还需要添加安全性,以便客户无法读取其他客户的发票。

    还知道该 URL 有一个maximum length of 2000 characters

    【讨论】:

    • 永远不要为此目的自定义MvcRouteHandlerMvcRouteHandler 只能提供从 URL 到资源的单向映射,但是您的 ActionLinksRouteLinks 将不起作用。最好将RouteBaseRoute 子类化(如this example),这样您就可以正确地考虑到路由的2-way 性质,尤其是如果您不想构建自己的系统来重构意见。
    【解决方案2】:

    您可以使用内置路由执行此操作,只要您不预期任何模式会重复或具有不会出现在 URL 的同一段中的可选参数作为其他可选参数。

    可以通过factoring out all of the permutations 使用带有可选参数的路由,但如果您问我,为此目的使用查询字符串要简单得多。

    注意:根据定义,URL 必须是唯一的。因此,您必须手动确保您的 URL 没有任何冲突。执行此操作的最简单方法是将页面与路径(路由)匹配并将这些额外信息添加为查询字符串值。这样您就不必担心不小心做出完全相同的路线。

    但是,如果您坚持为此目的使用路由,您可能应该将您的 URL 放入数据库中具有唯一约束的字段中,以确保它们是唯一的。

    对于路由的最高级自定义,subclass RouteBaseRoute。这允许您将任何 URL 映射到一组路由值并将路由值映射回相同的 URL,这样您就可以在 ActionLinkRouteLink 中使用它来构建 URL你的视图和控制器。

    public class CustomPageRoute : RouteBase
    {
        // This matches the incoming URL and translates it into RouteData
        // (typically a set of key value pairs in the RouteData.Values dictionary)
        public override RouteData GetRouteData(HttpContextBase httpContext)
        {
            RouteData result = null;
    
            // Trim the leading slash
            var path = httpContext.Request.Path.Substring(1);
    
            if (/* the path matches your route logic */)
            {
                result = new RouteData(this, new MvcRouteHandler());
    
                result.Values["controller"] = "MyController";
                result.Values["action"] = "MyAction";
    
                // Any other route values to match your action...
            }
    
            // IMPORTANT: Always return null if there is no match.
            // This tells .NET routing to check the next route that is registered.
            return result;
        }
    
        // This builds the URL for ActionLink and RouteLink
        public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
        {
            VirtualPathData result = null;
    
            if (/* all of the expected route values match the request (the values parameter) */)
            {
                result = new VirtualPathData(this, page.VirtualPath);
            }
    
            // IMPORTANT: Always return null if there is no match.
            // This tells .NET routing to check the next route that is registered.
            return result;
        }
    }
    

    用法

    routes.Add(
        name: "CustomPage", 
        item: new CustomPageRoute());
    
    routes.MapRoute(
        name: "Default",
        url: "{controller}/{action}/{id}",
        defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
    );
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-07-23
      • 1970-01-01
      • 1970-01-01
      • 2017-07-15
      • 1970-01-01
      • 1970-01-01
      • 2016-08-30
      相关资源
      最近更新 更多