3

にアクションを持ってDetail(int id)いるProductController、私はによってアクセスすることができます

/Product/Detail/32

しかし、私がそうするなら

Product/Detail

idまた、パスなしで同じコントローラーにアクセスします。どうすればパラメーターを必須にすることができますか、そうでない場合は404を返します(ルートが一致しないなど、コントローラーアクションをまったく実行しないでください)

public ActionResult Detail(int id) {
    // some fancy code that get the product by id
    return View()
}

ルート:

routes.MapRoute(
    "Default", // Route name
    "{controller}/{action}/{id}", // URL with parameters
    new { controller = "Application", action = "Index", id = UrlParameter.Optional } defaults);

(製品が見つからない)戻ってきたらできることはわかっていますがHttpNotFound()、ほとんどのシナリオで機能しますが、引数を渡さずにコントローラーのアクションに到達しない方法があるかどうか疑問に思っていました

編集:

/ <-- homepage
/Product/List <-- List of products
/Product/Detail <-- return 404
/Product/Detail/10 <-- Product Details id 10

今、この種の「単純な」シナリオをサポートする方法があるかどうか知りたいです。コントローラでのアクションは次のとおりです。

ApplicationController{
    public ActionResult Index() {}
}

ProductController {
    public ActionResult List(){}
    public ActionResult Detail(int id){}
}

現在のルーティングはデフォルトのみです。

routes.MapRoute(
                "Default", // Route name
                "{controller}/{action}/{id}", // URL with parameters
                new { controller = "Application", action = "Index", id = UrlParameter.Optional } // Parameter defaults
            );

提案されたルーティングの追加は期待どおりに機能しません。どちらかが404オンを返しますが、と/Product/Detailも返し404ます。/Product/List/

または(その他の提案)

ActionResult Detail(int id)/Product/Detailは、リクエストでパラメータを送信せずに呼び出されます。これは、 idパラメータなしでURLを一致させないことが可能かどうかを知るためのこの質問の目的です。

4

2 に答える 2

2

ルーティングを変更する

ルーティングに対応し、idデフォルトを削除してオプションのパラメータにするだけです。

routes.MapRoute(
    "Default", // Route name
    "{controller}/{action}/{id}", // URL with parameters
    new { controller = "Home", action = "Index" } // defaults
);

ただし、これらのデフォルトが使用されることはありません。

routes.MapRoute(
    "Default", // Route name
    "{controller}/{action}/{id}" // URL with parameters
);

これは、定義する必要がある実際のルートです。

あなたのcontrollerとは、必要に応じactionてURLから省略されることはなくid、URL定義の最後のものです。つまり、最初のペアもそこにある必要があります。

それがまさにあなたが必要としているものであるかどうかはわかりませんが、あなたの質問の現在の状態に基づいて、これはあなたのためにトリックをするはずです。ただし、事前定義された値を持つためにIDが必要な場合は、ルート定義で別の値を指定できます。

routes.MapRoute(
    "Default", // Route name
    "{controller}/{action}/{id}", // URL with parameters
    new { controller = "Home", action = "Index", id = 1 } // defaults
);

これにより、3つすべてをURLから省略でき、それらすべてに特定の値を設定できます。

idフォーマットを定義するためのルート制約

ルート制約を使用して、URLパラメータがどのように見えるかをルーティングに指示することもできます。あなたidは数値でなければならないように見えるので、これも可能性の1つです

routes.MapRoute(
    "Default", // Route name
    "{controller}/{action}/{id}", // URL with parameters
    new { controller = "Home", action = "Index" }, // defaults
    new { id = "\d+" } // constraints
);

編集後

実際には、問題には2つの解決策があります。

  1. id一部のルートに必要なものを定義するための適切なルーティング
  2. 特定のパラメーターを必要とするアクションを宣言的にマークするアクションメソッドセレクターフィルター

解決策1:ルーティング

これはいくつかのルーティング定義を定義しますが、idパラメーターを必要とするアクションをハードコーディングします。

routes.MapRoute(
    "RequiresId",
    "{controller}/{action}/{id}", // URL with parameters
    null,
    new { action = "Detail" }
);
routes.MapRoute(
    "Default", // Route name
    "{controller}/{action}/{id}" // URL with parameters
    new { controller = "Home", action = "Index", id = UrlParameter.Optional },
    new { action = "(?!Detail).+" } // any action except "Detail"
);

最初のルートは、アクションメソッドを持つすべてのコントローラーがパラメーターをDetail必要とすることを定義しidます。これらのアクションを持つすべてのコントローラーが同じ要件を持っている限り、これは簡単です(あなたの場合はそうかもしれません)。ただし、これが当てはまらない場合は、コントローラーごとに制約を指定する必要があるため、より複雑になります。

解決策2:アクションメソッドセレクターフィルター

このソリューションでは、オプションのデフォルトルートのみが必要ですid。カスタムアクションメソッドセレクターフィルター(ほとんど知られておらず、ほとんど使用されていません)は、次のようなコードを作成するのに役立ちます。

[RequiresRouteValues("id, name")]
public ActionResult Detail(int id, string name)
{
    ...
}

あなたはそれを必要とするそれらのメソッドにこれを置くでしょう。その特定のパラメーターが存在しない場合、コントローラーアクションの呼び出し元は適切なメソッドを見つけることができないため、404が返されます。

これについては、ブログで詳しく説明しました。また、次のようなフィルターのコードも含まれています。

/// <summary>
/// Represents an attribute that is used to restrict action method selection based on route values.
/// </summary>
[SuppressMessage("Microsoft.Design", "CA1019:DefineAccessorsForAttributeArguments")]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
public sealed class RequiresRouteValuesAttribute : ActionMethodSelectorAttribute
{
    #region Properties
 
    /// <summary>
    /// Gets required route value names.
    /// </summary>
    public ReadOnlyCollection<string> Names { get; private set; }
 
    /// <summary>
    /// Gets or sets a value indicating whether to include form fields in the check.
    /// </summary>
    /// <value><c>true</c> if form fields should be included; otherwise, <c>false</c>.</value>
    public bool IncludeFormFields { get; set; }
 
    /// <summary>
    /// Gets or sets a value indicating whether to include query variables in the check.
    /// </summary>
    /// <value>
    ///     <c>true</c> if query variables should be included; otherwise, <c>false</c>.
    /// </value>
    public bool IncludeQueryVariables { get; set; }
 
    #endregion
 
    #region Constructors
 
    /// <summary>
    /// Initializes a new instance of the <see cref="RequiresRouteValuesAttribute"/> class.
    /// </summary>
    private RequiresRouteValuesAttribute()
    {
        this.IncludeFormFields = true;
        this.IncludeQueryVariables = true;
    }
 
    /// <summary>
    /// Initializes a new instance of the <see cref="RequiresRouteValuesAttribute"/> class.
    /// </summary>
    /// <param name="commaSeparatedNames">Comma separated required route values names.</param>
    public RequiresRouteValuesAttribute(string commaSeparatedNames)
        : this((commaSeparatedNames ?? string.Empty).Split(','))
    {
        // does nothing
    }
 
    /// <summary>
    /// Initializes a new instance of the <see cref="RequiresRouteValuesAttribute"/> class.
    /// </summary>
    /// <param name="names">Required route value names.</param>
    public RequiresRouteValuesAttribute(IEnumerable<string> names)
        : this()
    {
        if (names == null || names.Count().Equals(0))
        {
            throw new ArgumentNullException("names");
        }
 
        // store names
        this.Names = new ReadOnlyCollection<string>(names.Select(val => val.Trim()).ToList());
    }
 
    #endregion
 
    #region ActionMethodSelectorAttribute implementation
 
    /// <summary>
    /// Determines whether the action method selection is valid for the specified controller context.
    /// </summary>
    /// <param name="controllerContext">The controller context.</param>
    /// <param name="methodInfo">Information about the action method.</param>
    /// <returns>
    /// true if the action method selection is valid for the specified controller context; otherwise, false.
    /// </returns>
    public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo)
    {
        if (controllerContext == null)
        {
            throw new ArgumentNullException("controllerContext");
        }
 
        // always include route values
        HashSet<string> uniques = new HashSet<string>(controllerContext.RouteData.Values.Keys);
 
        // include form fields if required
        if (this.IncludeFormFields)
        {
            uniques.UnionWith(controllerContext.HttpContext.Request.Form.AllKeys);
        }
 
        // include query string variables if required
        if (this.IncludeQueryVariables)
        {
            uniques.UnionWith(controllerContext.HttpContext.Request.QueryString.AllKeys);
        }
 
        // determine whether all route values are present
        return this.Names.All(val => uniques.Contains(val));
    }
 
    #endregion
}

1つ目は、複数のコントローラーとそれらに関連するさまざまな制約があるアプリケーションでは、事態を複雑にします。2つ目はエレガントで、単純なシナリオと複雑なシナリオに適用されます。

もちろん、ソリューション2を選択します。しかし、この場合、私を偏った開発者と見なしてください。

于 2012-12-11T12:58:55.943 に答える
0

IDをオプションとして使用しないでください

routes.MapRoute(
    "Default", // Route name
    "{controller}/{action}/{id}", // URL with parameters
    new { controller = "Application", action = "Index"} );

idをオプションのままにしておきたい場合は、製品の詳細に別のルートを定義することができます

routes.MapRoute("ProductDetails", "product/details/{id}");

これにはIDが必要であり、元のデフォルトルートはそのままにしておきます。

于 2012-12-11T12:59:27.973 に答える