ASP.Net MVC 使用路由处理段

ASP.Net MVC Handling Segments with Route

我是 ASP.Net MVC 的新手,遇到了一个问题。在这里。

routes.MapRoute(
    "SearchResults",// Route name
    "{controller}/{action}/{category}/{manufacturer}/{attribute}",
    new { 
        controller = "Home", 
        action = "CategoryProducts", 
        category = UrlParameter.Optional, 
        manufacturer = UrlParameter.Optional, 
        attribute = UrlParameter.Optional 
    }
);

这是我的控制器方法。

public ActionResult CategoryProducts(string category, string manufacturer, string attribute) 
{
    string[] categoryParameter = category.Split('_');
    .
    .
    .
    return View();
}

当我点击 url 时,我总是在类别参数中得到 null

http://localhost:50877/Home/CategoryProducts/c_50_ShowcasesDisplays

我收到这个错误

Object reference not set to an instance of an object

我该如何解决这个问题。我需要从段中提取 id 并使用它。同样,我也需要处理制造商和属性字符串。

还有一件事

如何让我的函数无论顺序如何至少获得一个参数?我的意思是我想制作这样的功能,我可以处理类别或制造商或属性或类别 + 制造商以及所有组合/

占位符(例如{category})就像一个变量——它可以包含任何值。框架必须能够理解 URL 中参数的含义。您可以通过以下三种方式之一执行此操作:

  1. 按特定顺序提供它们,并提供特定数量的细分
  2. 将它们放入查询字符串中,这样您就有 name/value 对来识别它们是什么
  3. 制作一系列带有文字段的路线,以提供名称来识别参数是什么

这是选项 #3 的示例。与使用查询字符串参数相比,它有点复杂,但只要您为每个路由段提供某种标识符,它肯定是可能的。

IEnumerable 扩展

这增加了 LINQ 支持,以便能够获得参数值的所有可能排列。

using System;
using System.Collections.Generic;
using System.Linq;

public static class IEnumerableExtensions
{
    // Can be used to get all permutations at a certain level
    // Source: 
    public static IEnumerable<IEnumerable<T>> Combinations<T>(this IEnumerable<T> elements, int k)
    {
        return k == 0 ? new[] { new T[0] } :
            elements.SelectMany((e, i) =>
            elements.Skip(i + 1).Combinations(k - 1).Select(c => (new[] { e }).Concat(c)));
    }

    // This one came from: 
    private static IEnumerable<TSource> Prepend<TSource>(this IEnumerable<TSource> source, TSource item)
    {
        if (source == null)
            throw new ArgumentNullException("source");

        yield return item;

        foreach (var element in source)
            yield return element;
    }

    public static IEnumerable<IEnumerable<TSource>> Permutations<TSource>(this IEnumerable<TSource> source)
    {
        if (source == null)
            throw new ArgumentNullException("source");

        var list = source.ToList();

        if (list.Count > 1)
            return from s in list
                   from p in Permutations(list.Take(list.IndexOf(s)).Concat(list.Skip(list.IndexOf(s) + 1)))
                   select p.Prepend(s);

        return new[] { list };
    }
}

RouteCollection 扩展

我们扩展了 MapRoute 扩展方法,增加了添加一组路由以匹配 URL.

所有可能排列的能力
using System;
using System.Collections.Generic;
using System.Web.Mvc;
using System.Web.Routing;

public static class RouteCollectionExtensions
{
    public static void MapRoute(this RouteCollection routes, string url, object defaults, string[] namespaces, string[] optionalParameters)
    {
        MapRoute(routes, url, defaults, null, namespaces, optionalParameters);
    }

    public static void MapRoute(this RouteCollection routes, string url, object defaults, object constraints, string[] namespaces, string[] optionalParameters)
    {
        if (routes == null)
        {
            throw new ArgumentNullException("routes");
        }
        if (url == null)
        {
            throw new ArgumentNullException("url");
        }
        AddAllRoutePermutations(routes, url, defaults, constraints, namespaces, optionalParameters);
    }

    private static void AddAllRoutePermutations(RouteCollection routes, string url, object defaults, object constraints, string[] namespaces, string[] optionalParameters)
    {
        // Start with the longest routes, then add the shorter ones
        for (int length = optionalParameters.Length; length > 0; length--)
        {
            foreach (var route in GetRoutePermutations(url, defaults, constraints, namespaces, optionalParameters, length))
            {
                routes.Add(route);
            }
        }
    }

    private static IEnumerable<Route> GetRoutePermutations(string url, object defaults, object constraints, string[] namespaces, string[] optionalParameters, int length)
    {
        foreach (var combination in optionalParameters.Combinations(length))
        {
            foreach (var permutation in combination.Permutations())
            {
                yield return GenerateRoute(url, permutation, defaults, constraints, namespaces);
            }
        }
    }

    private static Route GenerateRoute(string url, IEnumerable<string> permutation, object defaults, object constraints, string[] namespaces)
    {
        var newUrl = GenerateUrlPattern(url, permutation);
        var result = new Route(newUrl, new MvcRouteHandler())
        {
            Defaults = CreateRouteValueDictionary(defaults),
            Constraints = CreateRouteValueDictionary(constraints),
            DataTokens = new RouteValueDictionary()
        };
        if ((namespaces != null) && (namespaces.Length > 0))
        {
            result.DataTokens["Namespaces"] = namespaces;
        }

        return result;
    }

    private static string GenerateUrlPattern(string url, IEnumerable<string> permutation)
    {
        string result = url;
        foreach (string param in permutation)
        {
            result += "/" + param + "/{" + param + "}";
        }

        System.Diagnostics.Debug.WriteLine(result);

        return result;
    }

    private static RouteValueDictionary CreateRouteValueDictionary(object values)
    {
        IDictionary<string, object> dictionary = values as IDictionary<string, object>;
        if (dictionary != null)
        {
            return new RouteValueDictionary(dictionary);
        }
        return new RouteValueDictionary(values);
    }
}

用法

public class RouteConfig
{
    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

        routes.MapRoute(
            url: "Home/CategoryProducts", 
            defaults: new { controller = "Home", action = "CategoryProducts" }, 
            namespaces: null, 
            optionalParameters: new string[] { "category", "manufacturer", "attribute" });

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

这添加了一组完整的路由以匹配 URL 模式:

Home/CategoryProducts/category/{category}/manufacturer/{manufacturer}/attribute/{attribute}
Home/CategoryProducts/category/{category}/attribute/{attribute}/manufacturer/{manufacturer}
Home/CategoryProducts/manufacturer/{manufacturer}/category/{category}/attribute/{attribute}
Home/CategoryProducts/manufacturer/{manufacturer}/attribute/{attribute}/category/{category}
Home/CategoryProducts/attribute/{attribute}/category/{category}/manufacturer/{manufacturer}
Home/CategoryProducts/attribute/{attribute}/manufacturer/{manufacturer}/category/{category}
Home/CategoryProducts/category/{category}/manufacturer/{manufacturer}
Home/CategoryProducts/manufacturer/{manufacturer}/category/{category}
Home/CategoryProducts/category/{category}/attribute/{attribute}
Home/CategoryProducts/attribute/{attribute}/category/{category}
Home/CategoryProducts/manufacturer/{manufacturer}/attribute/{attribute}
Home/CategoryProducts/attribute/{attribute}/manufacturer/{manufacturer}
Home/CategoryProducts/category/{category}
Home/CategoryProducts/manufacturer/{manufacturer}
Home/CategoryProducts/attribute/{attribute}

现在当您使用以下 URL:

Home/CategoryProducts/category/c_50_ShowcasesDisplays

将调用 HomeController 上的操作 CategoryProducts。您的类别参数值为 c_50_ShowcasesDisplays.

当你使用ActionLinkRouteLinkUrl.ActionUrlHelper

时,也会生成相应的URL
@Html.ActionLink("ShowcasesDisplays", "CategoryProducts", "Home", 
    new { category = "c_50_ShowcasesDisplays" }, null)

// Generates URL /Home/CategoryProducts/category/c_50_ShowcasesDisplays