メソッド属性を介したASP.NETMVCルーティング[クローズ]


80

StackOverflowのポッドキャスト#54、ジェフは、彼らがStackOverflowの中にそのURLルートを登録言及ハンドルルートという方法上記の属性を経由してコードベース。良いコンセプトのように聞こえます(ルートの優先順位に関してPhil Haackが提起した警告があります)。

誰かがこれを実現するためのサンプルを提供できますか?

また、このスタイルのルーティングを使用するための「ベストプラクティス」はありますか?

回答:


62

更新:これはcodeplexに投稿されました。完全なソースコードとコンパイル済みのアセンブリがダウンロードできます。私はまだサイトにドキュメントを投稿する時間がなかったので、このSO投稿で今のところ十分である必要があります。

更新:1)ルートの順序、2)ルートパラメーターの制約、および3)ルートパラメーターのデフォルト値を処理するために、いくつかの新しい属性を追加しました。以下のテキストは、この更新を反映しています。

私は実際にMVCプロジェクトに対してこのようなことをしました(Jeffがstackoverflowでどのようにそれを行っているのかわかりません)。カスタム属性のセットを定義しました:UrlRoute、UrlRouteParameterConstraint、UrlRouteParameterDefault。それらをMVCコントローラーのアクションメソッドにアタッチして、ルート、制約、およびデフォルトを自動的にバインドすることができます。

使用例:

(この例は多少工夫されていますが、機能を示していることに注意してください)

public class UsersController : Controller
{
    // Simple path.
    // Note you can have multiple UrlRoute attributes affixed to same method.
    [UrlRoute(Path = "users")]
    public ActionResult Index()
    {
        return View();
    }

    // Path with parameter plus constraint on parameter.
    // You can have multiple constraints.
    [UrlRoute(Path = "users/{userId}")]
    [UrlRouteParameterConstraint(Name = "userId", Regex = @"\d+")]
    public ActionResult UserProfile(int userId)
    {
        // ...code omitted

        return View();
    }

    // Path with Order specified, to ensure it is added before the previous
    // route.  Without this, the "users/admin" URL may match the previous
    // route before this route is even evaluated.
    [UrlRoute(Path = "users/admin", Order = -10)]
    public ActionResult AdminProfile()
    {
        // ...code omitted

        return View();
    }

    // Path with multiple parameters and default value for the last
    // parameter if its not specified.
    [UrlRoute(Path = "users/{userId}/posts/{dateRange}")]
    [UrlRouteParameterConstraint(Name = "userId", Regex = @"\d+")]
    [UrlRouteParameterDefault(Name = "dateRange", Value = "all")]
    public ActionResult UserPostsByTag(int userId, string dateRange)
    {
        // ...code omitted

        return View();
    }

UrlRouteAttributeの定義:

/// <summary>
/// Assigns a URL route to an MVC Controller class method.
/// </summary>
[AttributeUsage(AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
public class UrlRouteAttribute : Attribute
{
    /// <summary>
    /// Optional name of the route.  If not specified, the route name will
    /// be set to [controller name].[action name].
    /// </summary>
    public string Name { get; set; }

    /// <summary>
    /// Path of the URL route.  This is relative to the root of the web site.
    /// Do not append a "/" prefix.  Specify empty string for the root page.
    /// </summary>
    public string Path { get; set; }

    /// <summary>
    /// Optional order in which to add the route (default is 0).  Routes
    /// with lower order values will be added before those with higher.
    /// Routes that have the same order value will be added in undefined
    /// order with respect to each other.
    /// </summary>
    public int Order { get; set; }
}

UrlRouteParameterConstraintAttributeの定義:

/// <summary>
/// Assigns a constraint to a route parameter in a UrlRouteAttribute.
/// </summary>
[AttributeUsage(AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
public class UrlRouteParameterConstraintAttribute : Attribute
{
    /// <summary>
    /// Name of the route parameter on which to apply the constraint.
    /// </summary>
    public string Name { get; set; }

    /// <summary>
    /// Regular expression constraint to test on the route parameter value
    /// in the URL.
    /// </summary>
    public string Regex { get; set; }
}

UrlRouteParameterDefaultAttributeの定義:

/// <summary>
/// Assigns a default value to a route parameter in a UrlRouteAttribute
/// if not specified in the URL.
/// </summary>
[AttributeUsage(AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
public class UrlRouteParameterDefaultAttribute : Attribute
{
    /// <summary>
    /// Name of the route parameter for which to supply the default value.
    /// </summary>
    public string Name { get; set; }

    /// <summary>
    /// Default value to set on the route parameter if not specified in the URL.
    /// </summary>
    public object Value { get; set; }
}

Global.asax.csへの変更:

MapRouteへの呼び出しを、RouteUtility.RegisterUrlRoutesFromAttributes関数への1回の呼び出しに置き換えます。

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

        RouteUtility.RegisterUrlRoutesFromAttributes(routes);
    }

RouteUtility.RegisterUrlRoutesFromAttributesの定義:

完全なソースはcodeplexにあります。フィードバックやバグレポートがある場合は、サイトにアクセスしてください。


属性を使用してこれを行うと、ルートのデフォルトとルートの制約を使用できなくなると思います...
Nicolas Cadilhac

このアプローチでは、各ルートを特定のメソッドにバインドしているため、デフォルトルートは必要ありませんでした。あなたは制約について正しいです。属性プロパティとして制約を追加できるかどうかを調べましたが、MVC制約が匿名オブジェクトを使用して指定され、属性プロパティは単純な型にしかなり得ないという問題に遭遇しました。属性として(より多くのコーディングで)制約を行うことはまだ可能ですが、これまでMVC作業で制約を実際に必要としなかったため、まだ気にしませんでした(ルート値を検証する傾向があります)コントローラ内)。
DSO

3
非常に素晴らしい!RouteAttributeはこれと非常によく似ていますが、ヘルパー機能が少し追加されているだけです。違いを詳しく説明する回答を追加する必要があります。
JarrodDixon

1
これは素晴らしいです。私はそれを愛している。
BowserKingKoopa 2010年

1
これは素晴らしい!私はしばらくの間MvcContribを使用していて、これがそこにあるとは思いもしませんでした。元の投稿で、それを文書化する時間がなかったと述べました。それでもそうですか?少なくともMvcContribドキュメントでの言及は、開発者が少なくともそこにあることを知るのに非常に役立つようです。ありがとう!
Todd Menier 2010

44

githubまたはnugetから入手できるAttributeRoutingを試すこともできます。

私はプロジェクトの作者なので、これは恥知らずなプラグです。しかし、私がそれを使用することにあまり満足していない場合は、ダメです。あなたもそうかもしれません。githubリポジトリwikiにはたくさんのドキュメントとサンプルコードがあります

このライブラリを使用すると、多くのことができます。

  • アクションをGET、POST、PUT、およびDELETE属性で装飾します。
  • 複数のルートを1つのアクションにマップし、Orderプロパティを使用してそれらを並べ替えます。
  • 属性を使用してルートのデフォルトと制約を指定します。
  • 単純な?でオプションのパラメータを指定します パラメータ名の前のトークン。
  • 名前付きルートをサポートするためのルート名を指定します。
  • コントローラまたはベースコントローラでMVC領域を定義します。
  • コントローラまたはベースコントローラに適用されるルートプレフィックスを使用して、ルートをグループ化またはネストします。
  • 従来のURLをサポートします。
  • アクション用に定義されたルート間、コントローラー内、およびコントローラーとベースコントローラー間でルートの優先順位を設定します。
  • 小文字のアウトバウンドURLを自動的に生成します。
  • 独自のカスタムルート規則を定義し、それらをコントローラーに適用して、ボイラープレート属性なしでコントローラー内のアクションのルートを生成します(RESTfulスタイルを考えてください)。
  • 提供されているHttpHandlerを使用してルートをデバッグします。

私が忘れている他のいくつかのものがあると確信しています。見てみな。nuget経由でインストールするのは簡単です。

注:2012年4月16日の時点で、AttributeRoutingは新しいWebAPIインフラストラクチャもサポートしています。あなたがそれを処理できる何かを探している場合に備えて。subkamranに感謝します


10
このプロジェクトは、言及された他のオプションよりも成熟しているようです(より良いドキュメント、より多くの機能、完全なテストスイート)
David Laing 2011年

3
私は同意します、これはあなたが望むかもしれないすべてをするようです、そして良い例のドキュメントで。
マイクチェンバレン

3
どうもありがとうございました。私はこのソリューションを喜んで使用しており、ルーティングの競合、あいまいさ、混乱をすべて解決しました。
ヴァラマス2011

3
詳細については、探していたときにこのSO投稿を見つけたので、上記の箇条書きをgithubページに書き込む必要があります:)
GONeale 2011

2
悪魔の代弁者を演じるためだけに、ルートをすべて1か所で宣言することにメリットはありますか?何かを失うのか、それともこの方法への切り替えが制限されているのか。
GONeale 2011

9

1. RiaLibrary.Web.dllをダウンロードし、ASP.NET MVCWebサイトプロジェクトで参照します

2. [Url]属性でコントローラーメソッドを装飾します。

public SiteController : Controller
{
    [Url("")]
    public ActionResult Home()
    {
        return View();
    }

    [Url("about")]
    public ActionResult AboutUs()
    {
        return View();
    }

    [Url("store/{?category}")]
    public ActionResult Products(string category = null)
    {
        return View();
    }
}

ところで、「?」サインイン '{?category}'パラメータは、オプションであることを意味します。ルートのデフォルトでこれを明示的に指定する必要はありません。これは次のようになります。

routes.MapRoute("Store", "store/{category}",
new { controller = "Store", action = "Home", category = UrlParameter.Optional });

3.Global.asax.csファイルを更新します

public class MvcApplication : System.Web.HttpApplication
{
    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

        routes.MapRoutes(); // This does the trick
    }

    protected void Application_Start()
    {
        RegisterRoutes(RouteTable.Routes);
    }
}

デフォルトと制約を設定する方法は?例:

public SiteController : Controller
{
    [Url("admin/articles/edit/{id}", Constraints = @"id=\d+")]
    public ActionResult ArticlesEdit(int id)
    {
        return View();
    }

    [Url("articles/{category}/{date}_{title}", Constraints =
         "date=(19|20)\d\d-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])")]
    public ActionResult Article(string category, DateTime date, string title)
    {
        return View();
    }
}

順序を設定する方法は?例:

[Url("forums/{?category}", Order = 2)]
public ActionResult Threads(string category)
{
    return View();
}

[Url("forums/new", Order = 1)]
public ActionResult NewThread()
{
    return View();
}

1
非常に素晴らしい!私は特に{?param}オプションのパラメータの命名法が好きです。
JarrodDixon

3

この投稿は、DSOの回答を拡張するためのものです。

ルートを属性に変換するときに、ActionName属性を処理する必要がありました。したがって、GetRouteParamsFromAttributeでは:

ActionNameAttribute anAttr = methodInfo.GetCustomAttributes(typeof(ActionNameAttribute), false)
    .Cast<ActionNameAttribute>()
    .SingleOrDefault();

// Add to list of routes.
routeParams.Add(new MapRouteParams()
{
    RouteName = routeAttrib.Name,
    Path = routeAttrib.Path,
    ControllerName = controllerName,
    ActionName = (anAttr != null ? anAttr.Name : methodInfo.Name),
    Order = routeAttrib.Order,
    Constraints = GetConstraints(methodInfo),
    Defaults = GetDefaults(methodInfo),
});

また、ルートの名前が適切でないことがわかりました。名前は、controllerName.RouteNameを使用して動的に作成されます。しかし、私のルート名はコントローラークラスのconst文字列であり、それらのconstを使用してUrl.RouteUrlも呼び出します。そのため、属性のルート名を実際のルート名にする必要があります。

もう1つ行うことは、デフォルト属性と制約属性をAttributeTargets.Parameterに変換して、パラメーターに固定できるようにすることです。


ええ、私はルートの命名行動にちょっと振動しました。おそらく、属性の内容をそのまま使用するか、nullにするのが最善です。パラメータ自体にデフォルト/制約を設定することをお勧めします。変更をより適切に管理するために、おそらくいつかこれをcodeplexに投稿します。
DSO


0

AsyncControllerを使用してasp.netmvc 2でITCloudルーティングを機能させる必要がありました。そのためには、ソースのRouteUtility.csクラスを編集して再コンパイルするだけです。98行目のアクション名から「Completed」を削除する必要があります

// Add to list of routes.
routeParams.Add(new MapRouteParams()
{
    RouteName = String.IsNullOrEmpty(routeAttrib.Name) ? null : routeAttrib.Name,
    Path = routeAttrib.Path,
    ControllerName = controllerName,
    ActionName = methodInfo.Name.Replace("Completed", ""),
    Order = routeAttrib.Order,
    Constraints = GetConstraints(methodInfo),
    Defaults = GetDefaults(methodInfo),
    ControllerNamespace = controllerClass.Namespace,
});

その後、AsyncControllerで、おなじみとXXXXCompletedのActionResultを飾るUrlRouteUrlRouteParameterDefault属性:

[UrlRoute(Path = "ActionName/{title}")]
[UrlRouteParameterDefault(Name = "title", Value = "latest-post")]
public ActionResult ActionNameCompleted(string title)
{
    ...
}

同じ問題を抱えている人の助けになることを願っています。


参考までに、慣例として、ActionNameCompletedメソッドではなく、ActionNameAsyncメソッドにMVC関連の属性を設定します。
Erv Walter 2010

ありがとう-気づかなかった。
TimDog 2010
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.