2

現在、次のようなコントローラーがあります。

public class MyController : Controller
{
    public ActionResult Action1 (int id1, int id2)
    {

    }

    public ActionResult Action2 (int id3, int id4)
    {

    }
}

ご覧のとおり、両方のコントローラーに同じパラメーター「パターン」、つまり 2 つの null 非許容の符号付き整数があります。

私のルート設定は次のようになります。

routes.MapRoute(
    name: "Action2",
    url: "My/Action2/{id3}-{id4}",
    defaults: new { controller = "My", action = "Action2", id3 = 0, id4 = 0 }
);

routes.MapRoute(
    name: "Action1",
    url: "My/Action1/{id1}-{id2}",
    defaults: new { controller = "My", action = "Action1", id1 = 0, id2 = 0 }
);

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

しかし、私はこの 2 つだけでなく、コントローラーのアクションもたくさん持っているので、基本的にはそれぞれに別々のルートをマッピングしてきましたが、これはかなり面倒だと思います。

私が疑問に思っているのは、次のように、1 つではなく 2 つのパラメーターを使用してデフォルト ルートのようなことができるかどうかです。

routes.MapRoute(
    name: "Default2",
    url: "{controller}/{action}/{id1}-{id2}",
    defaults: new { controller = "Home", action = "Index", id1 = 0, id2 = 0 }
);

ただし、私のパラメーターは常に名前が付けられているとは限らずid1id2これは機能しません (エラーは「パラメーター辞書にパラメーターの null エントリが含まれています」です。これを行う方法はありますか? (または、まったく別の方法の方が優れていますか? )

助けてくれてありがとう!

編集:これまでの回答に基づいて、私の質問は少し誤解を招くようでした。特に汎用パラメーターを含むルートが必要なため、特定のパラメーター名に関連付けられていません。

ルートマネージャーに「もしパターンのリクエストが来たら、{controller}/{action}/{parameter}-{parameter}その2つのパラメータをコントローラとアクションに指定して、その型や名前に関係なく渡してほしい! 」と伝えられるようにしたいです。

4

4 に答える 4

2

デフォルトルートを見てください:

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

キービットはUrlParameter.Optionalです。これにより、次のようなアクションを実行できます。

public ActionResult Action1()

と:

public ActionResult Action2(int id)

あるいは:

public ActionResult Action3(int? id)

(後者では、パラメーターにnull値を使用できます)

義務的な警告

一般的なルートを使用する場合は、いくつかの注意点があることに注意してください。MVCのモデルバインダーは非常に寛容です。情報を収集できるソースからできるだけ多くのパラメーターを入力しようとするだけです。したがって、特定のルートが通過するだけid1であっても、悪意のあるユーザーがそれに?id2=something沿って攻撃するのを妨げるものは何もありません。モデルバインダーが使用できる場合は使用します。

于 2012-11-14T18:52:27.330 に答える
1

別のアプローチを紹介したいと思います。あなたが望むほど簡単ではないかもしれません。しかし、重要な部分は宣言からコードに移されています。そのため、設定を調整するためにさらに多くのことができます。

以前の回答と比較して、このソリューションは機能しており、必要なことを行っています。私はあなたの意図について議論しているわけではないので、あくまで実用的な解決策として受け取ってください。

まず、制約を使用して宣言を拡張し、idパラメーターに少し風変わりな名前を付けます。

routes.MapRoute(
  name: "Default2",
  url: "{controller}/{action}/{theId1}-{theId2}",
  defaults: new { controller = "Home", action = "Index", theId1 = 0, theId2 = 0 },
  constraints: new { lang = new MyRouteConstraint() }
);

そして、制約で値のコレクションを拡張できます (パラメーターを繰り返します)

public class MyRouteConstraint : IRouteConstraint
{
    public bool Match(System.Web.HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
    {
        // for now skip the Url generation
        if (routeDirection.Equals(RouteDirection.UrlGeneration))
        {
            return false;
        }

        // try to find out our parameters
        object theId1 = null;
        object theId2 = null;

        var parametersExist = values.TryGetValue("theId1", out theId1)
                            && values.TryGetValue("theId2", out theId2);

        // not our case
        if( ! parametersExist)
        {
            return false;
        }

        // first argument re-inserted
        values["id1"] = theId1;
        values["id3"] = theId1;
        // TODO add other, remove theId1

        // second argument re-inserted
        values["id2"] = theId2;
        values["id4"] = theId2;
        // TODO add other, remove theId2


        return true;
    }
}

このソリューションでは、URL ルーティングの設計決定の複雑さが解消されません。しかし、必要に応じてコード、拡張可能な方法 (egreflection) で問題を解決することができます。

于 2012-11-15T04:50:38.677 に答える
1

さて、私は Radim のアイデアを拡張し、独自のカスタム ルート制約を設計することにしました (この質問.

基本的に、多くの異なるルートを用意する代わりに、2 つの整数パラメーターを持つ任意のものに一致するルートを 1 つだけ用意しました。

routes.MapRoute(
    name: "Default2",
    url: "{controller}/{action}/{param1}-{param2}",
    defaults: new { controller = "Admin", action = "Index" },
    constraints: new { lang = new CustomRouteConstraint(new RoutePatternCollection( new List<ParamType> { ParamType.INT, ParamType.INT })) }
);

以下Application_Start()Global.asaxは、制約のコントローラー名前空間を設定します (これは、毎回それを把握しようとするよりも効率的です)。

protected void Application_Start()
{
    AreaRegistration.RegisterAllAreas();

    //should be called before RegisterRoutes
    CustomRouteConstraint.SetControllerNamespace("********.Controllers");

    WebApiConfig.Register(GlobalConfiguration.Configuration);
    FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
    RouteConfig.RegisterRoutes(RouteTable.Routes);            
}

そして最後に、カスタム ルートの制約 (大量のコードであり、おそらく過度に複雑であることはわかっていますが、かなり自明だと思います):

using System;
using System.Collections.Generic;
using System.Web.Mvc;
using System.Web.Routing;
using System.Web;
using System.Linq;
using System.Reflection;

namespace ********.Code
{
    public class CustomRouteConstraint : IRouteConstraint
    {
        private static string controllerNamespace;

        RoutePatternCollection patternCollection { get; set; }

        /// <summary>
        /// Initializes a new instance of the <see cref="CustomRouteConstraint"/> class.
        /// </summary>
        /// <param name="rPC">The route pattern collection to match.</param>
        public CustomRouteConstraint(RoutePatternCollection rPC)
        {
            this.patternCollection = rPC;

            if (string.IsNullOrWhiteSpace(controllerNamespace)) {
                controllerNamespace = Assembly.GetCallingAssembly().FullName.Split(new string[1] {","}, StringSplitOptions.None)
                    .FirstOrDefault().Trim().ToString();
            }
        }

        /// <summary>
        /// Sets the controller namespace. Should be called before RegisterRoutes.
        /// </summary>
        /// <param name="_namespace">The namespace.</param>
        public static void SetControllerNamespace(string _namespace)
        {
            controllerNamespace = _namespace;
        }

        /// <summary>
        /// Attempts to match the current request to an action with the constraint pattern.
        /// </summary>
        /// <param name="httpContext">The current HTTPContext of the request.</param>
        /// <param name="route">The route to which the constraint belongs.</param>
        /// <param name="paramName">Name of the parameter (irrelevant).</param>
        /// <param name="values">The url values to attempt to match.</param>
        /// <param name="routeDirection">The route direction (this method will ignore URL Generations).</param>
        /// <returns>True if a match has been found, false otherwise.</returns>
        public bool Match(HttpContextBase httpContext, Route route, string paramName, RouteValueDictionary values, RouteDirection routeDirection)
        {
            if (routeDirection.Equals(RouteDirection.UrlGeneration)) {
                return false;
            }

            Dictionary<string, object> unMappedList = values.Where(x => x.Key.Contains("param")).OrderBy(xi => xi.Key).ToDictionary(
                kvp => kvp.Key, kvp => kvp.Value);

            string controller = values["controller"] as string;
            string action = values["action"] as string;

            Type cont = TryFindController(controller);

            if (cont != null) {
                MethodInfo actionMethod = cont.GetMethod(action);

                if (actionMethod != null) {
                    ParameterInfo[] methodParameters = actionMethod.GetParameters();

                    if (validateParameters(methodParameters, unMappedList)) {
                        for (int i = 0; i < methodParameters.Length; i++) {                            
                            var key = unMappedList.ElementAt(i).Key;
                            var value = values[key];

                            values.Remove(key);
                            values.Add(methodParameters.ElementAt(i).Name, value);
                        }

                        return true;
                    }
                }
            }

            return false;
        }

        /// <summary>
        /// Validates the parameter lists.
        /// </summary>
        /// <param name="methodParameters">The method parameters for the found action.</param>
        /// <param name="values">The parameters from the RouteValueDictionary.</param>
        /// <returns>True if the parameters all match, false if otherwise.</returns>
        private bool validateParameters(ParameterInfo[] methodParameters, Dictionary<string, object> values)
        {
            //@TODO add flexibility for optional parameters
            if (methodParameters.Count() != patternCollection.parameters.Count()) {
                return false;
            }

            for (int i = 0; i < methodParameters.Length; i++) {
                if (!matchType(methodParameters[i], patternCollection.parameters.ElementAt(i), values.ElementAt(i).Value)) {
                    return false;
                }
            }

            return true;
        }

        /// <summary>
        /// Matches the type of the found action parameter to the expected parameter, also attempts data conversion.
        /// </summary>
        /// <param name="actualParam">The actual parameter of the found action.</param>
        /// <param name="expectedParam">The expected parameter.</param>
        /// <param name="value">The value of the RouteValueDictionary corresponding to that parameter.</param>
        /// <returns>True if the parameters match, false if otherwise.</returns>
        private bool matchType(ParameterInfo actualParam, ParamType expectedParam, object value)
        {
            try {
                switch (expectedParam) {
                    case ParamType.BOOL:
                        switch (actualParam.ParameterType.ToString()) {
                            case "System.Boolean":
                                Convert.ToBoolean(value);
                                return true;
                                break;
                            default:
                                return false;
                                break;
                        }
                        break;
                    case ParamType.DOUBLE:
                        switch (actualParam.ParameterType.ToString()) {
                            case "System.Double":
                                Convert.ToDouble(value);
                                return true;
                                break;
                            case "System.Decimal":
                                Convert.ToDecimal(value);
                                return true;
                                break;
                            default:
                                return false;
                                break;
                        }
                        break;
                    case ParamType.INT:
                        switch (actualParam.ParameterType.ToString()) {
                            case "System.Int32":
                                Convert.ToInt32(value);
                                return true;
                                break;
                            case "System.Int16":
                                Convert.ToInt16(value);                                
                                return true;
                                break;
                            default:
                                return false;
                                break;
                        }
                        break;
                    case ParamType.LONG:
                        switch (actualParam.ParameterType.ToString()) {
                            case "System.Int64":
                                Convert.ToInt64(value);
                                return true;
                                break;
                            default:
                                return false;
                                break;
                        }
                        break;
                    case ParamType.STRING:
                        switch (actualParam.ParameterType.ToString()) {
                            case "System.String":
                                Convert.ToString(value);
                                return true;
                                break;
                            default:
                                return false;
                                break;
                        }
                        break;
                    case ParamType.UINT:
                        switch (actualParam.ParameterType.ToString()) {
                            case "System.UInt32":
                                Convert.ToUInt32(value);
                                return true;
                                break;
                            case "System.UInt16":
                                Convert.ToUInt16(value);
                                return true;
                                break;
                            default:
                                return false;
                                break;
                        }
                        break;
                    case ParamType.ULONG:
                        switch (actualParam.ParameterType.ToString()) {
                            case "System.UInt64":
                                Convert.ToUInt64(value);
                                return true;
                                break;
                            default:
                                return false;
                                break;
                        }
                        break;
                    default:
                        return false;
                }
            } catch (Exception) {
                return false;
            }
        }

        /// <summary>
        /// Attempts to discover a controller matching the one specified in the route.
        /// </summary>
        /// <param name="_controllerName">Name of the controller.</param>
        /// <returns>A System.Type containing the found controller, or null if the contoller cannot be discovered.</returns>
        private Type TryFindController(string _controllerName)
        {
            string controllerFullName;
            Assembly executingAssembly = Assembly.GetExecutingAssembly();

            if (!string.IsNullOrWhiteSpace(controllerNamespace)) {
                controllerFullName = string.Format(controllerNamespace + ".Controllers.{0}Controller", _controllerName);

                Type controller = executingAssembly.GetType(controllerFullName);

                if (controller == null) {
                    if (controllerNamespace.Contains("Controllers")) {
                        controllerFullName = string.Format(controllerNamespace + ".{0}Controller", _controllerName);

                        if ((controller = executingAssembly.GetType(controllerFullName)) == null) {
                            controllerFullName = string.Format(controllerNamespace + ".{0}", _controllerName);

                            controller = executingAssembly.GetType(controllerFullName);
                        }
                    } else {
                        controllerFullName = string.Format(controllerNamespace + "Controllers.{0}", _controllerName);

                        controller = executingAssembly.GetType(controllerFullName);
                    }
                }

                return controller;
            } else {
                controllerFullName = string.Format(Assembly.GetExecutingAssembly().FullName.Split(new string[1] {","}, StringSplitOptions.None)
                    .FirstOrDefault().Trim().ToString() + ".Controllers.{0}Controller", _controllerName);

                return Assembly.GetExecutingAssembly().GetType(controllerFullName);
            }            
        }
    }

    /// <summary>
    /// A list of the exepected parameters in the route.
    /// </summary>
    public struct RoutePatternCollection
    {
        public List<ParamType> parameters { get; set; }

        public RoutePatternCollection(List<ParamType> _params) : this()
        {
            this.parameters = _params;
        }
    }

    /// <summary>
    /// The valid parameter types for a Custom Route Constraint.
    /// </summary>
    public enum ParamType
    {
        STRING,
        INT,
        UINT,
        LONG,
        ULONG,
        BOOL,
        DOUBLE
    }
}

改善点を見つけたら、遠慮なく提案してください。

于 2012-11-19T14:04:45.653 に答える
0

コントローラー メソッドのパラメーター名について心配する必要はありません。たとえば、「id」は、ある方法では会社 ID を表し、別の方法ではアカウント ID を表す場合があります。次のようなルーティングを持つアプリケーションがあります。

private const string IdRouteConstraint = @"^\d+$";

routes.MapRoute("TwoParameters",
   "{controller}/{action}/{categoryId}/{id}",
   new {controller = "Contents", action = "Index"},
   new {categoryId = IdRouteConstraint, id = IdRouteConstraint});

routes.MapRoute("OneParameter",
   "{controller}/{action}/{id}",
   new {controller = "Contents", action = "Index"},
   new {id = IdRouteConstraint});
于 2012-11-16T21:42:38.470 に答える