web-dev-qa-db-ja.com

ASP.NET MVC Url.Actionは、生成されたURLに現在のルート値を追加します

SOでこの質問を数回見ましたが、受け入れられる答えのあるものはありません:

ASP.NET MVC @ Url.Actionには現在のルートデータが含まれます
ASP.NET MVCはルート値を暗黙的に追加します

基本的に、Groupというアクションメソッドを持つControllerがあります。パラメーターを受け取らず、要素のリストを表示するオーバーロードと、idを受け取り、そのグループの詳細を表示するオーバーロードがあります。

私がこのようなことをしたら:

Url.Action("Group", "Groups");

サイトのメインページ(/)から、次のようなURLが返されます。

"mysite.com/Groups/Group"

これで問題ありません。サイトの現在のアドレスが/ Groups/Group/1の場合、同じメソッドを呼び出します

Url.Action("Group", "Groups");

返されるURLは次のとおりです。

"mysite.com/Groups/Group/1"

URLを生成するときに、現在のページのルートの値を自動的に追加します。この方法でURLを生成しても:

Url.Action("Group", "Groups", null);

したがって、ルート値が必要ないことを明示的に指定すると、生成されるURLは同じになります。アドレスを取得するには、次のようにルート値を空の文字列に明示的に設定する必要があります。

Url.Action("Group", "Groups", new {id=""});

これにより、次のURLが生成されます。

"mysite.com/Groups/Group"

私の質問は、なぜこれが起こるのですか?ルート値を設定しない場合、生成されたURLにそれらを追加すべきではありません。

53
willvv

Url.Actionは、明示的に設定しない場合、現在の要求パラメーターを再利用します。これは、アウトバウンドURLマッチングアルゴリズムの仕様です。 URLを生成するプロセスでルートデータパラメーターを検索する場合、パラメーターは以下から取得されます。

1)明示的に提供された値

2)現在のリクエストの値

3)デフォルト

上記の順序で。

ルートのアウトバウンドマッチングアルゴリズムは複雑なので、例で行ったように、リクエストのすべてのパラメーターを明示的に設定することをお勧めします

50
objectbox

私のアプリケーションは、ルートの値を明示的に設定し、現在のリクエストから魔法の値を望んでいません。私は完全にコントロールしたいです。

ルートライブラリコレクションと共存する拡張機能を作成しました。 したがって、単一のRouteValueDictionaryパラメーターを使用します。 (下部のルートライブラリのコメントを参照)

ここでは、URLを生成する前に、リクエストからroutevaluesを削除します。

(注:array.contains ignorecase部分については、以下を参照してください: 文字列配列でArray.Containsの大文字と小文字を区別しないようにするにはどうすればよいですか?

public static string Action(this UrlHelper helper, 
                            RouteValueDictionary routeValues)
{
    RemoveRoutes(helper.RequestContext.RouteData.Values);

    string url = helper.Action(routeValues["Action"].ToString(), routeValues);
    return url;
}

public static void RemoveRoutes(RouteValueDictionary currentRouteData)
{
    List<string> keyList = new List<string>(currentRouteData.Keys);

    string[] ignore = new[] { "Area", "Controller", "Action" };
    foreach (string key in keyList)
    {
        if (!ignore.Contains(key, StringComparer.CurrentCultureIgnoreCase))
            currentRouteData.Remove(key);
    }
}

RemoveRoutesメソッドを使用するFormおよびActionLink拡張メソッドがあります。私のmvcライブラリのヘルパーは、私が作成した拡張メソッドではないメソッドを使用しません。これにより、URLを生成する前にすべてのルートデータがクリーンアップされます。

参考のために、AttributeRoutingを使用します。ルートライブラリの1つのルートの例を次に示します。

public static RouteValueDictionary DisplayNews(int newsId)
{
    RouteValueDictionary route = new RouteValueDictionary();
    route["Area"] = _area;
    route["Controller"] = _controller;
    route["Action"] = "DisplayNews";
    route["newsId"] = newsId;
    return route;
}
2
Valamas

そのため、objectboxの答えを読んだとき、コード内のいくつかのリンクを変更する必要があると思いました。次に、デフォルトのパラメーターを省略してデフォルトのルートを追加してみて、問題を解決しました。

routes.MapRoute(
    "ArtistArtworkDefPage",
    "Artist/{username}/Artwork",
    new
    {
        controller = "Artist",
        action = "Artwork",
        page = 1
    }
);

routes.MapRoute(
    "ArtistArtwork",
    "Artist/{username}/Artwork/{page}",
    new
    {
        controller = "Artist",
        action = "Artwork",
        page = 1
    },
    new { page = @"\d+" }
);
2
tmorell

簡単な例:

_public class ProductController : Controller
{
  public ActionResult Edit(int id)
  {
    return View();
  }

  [Route("Product/Detail/{id:int}")]
  public ActionResult Detail(int id)
  {
    return View();
  }
}
_

編集ビューにはこれのみが含まれます。

_@{ Layout = null;}
@Url.Action("Detail", "Cmr")
_

だからあなたのサイトを実行するとき_localhost:randomPort/Product/Edit/123_次の応答があります:_/Product/Detail/123_

どうして? Route属性には必須パラメーターidがあるためです。 Idパラメーターはurlから読み取られますが、パラメーターを指定せずにUrl.Action(methodName, controller)のみを記述しました。また、IDなしでメソッドの詳細を持つことは意味がありません。

属性が機能するためには、次の行を_RouteConfig.cs_に追加する必要があります。

_public static void RegisterRoutes(RouteCollection routes)
{
  ...
  routes.MapMvcAttributeRoutes();
  ...
}
_
2
broadband

属性ルーティングを必要とせず、リクエスト内の現在のルート値を変更しない回避策があります。このアイデアは http://notherdev.blogspot.ca/2013/10/aspnet-mvc-current-values-used-by-url-action.html に由来します。 MvcFutures。

UrlHelperに独自のAction拡張を追加し、オプションで現在のルート値を無視できるboolを追加しました(UrlHelperの既存のActionメソッドと衝突しないように)。

現在のルート値を使用してnot現在のルートから完全に新しいRequestContextを構築します。次に、それをベースヘルパーに渡します。このようにして、ベースヘルパーがリクエストコンテキスト内からルート値が何であるかを調べようとすると、何も見つからないため、URLを生成するときにそれらを使用しません。

public static string Action(this UrlHelper urlHelper, string actionName, string controllerName, object routeValues, bool ignoreCurrentRouteValues=false) {
    var routeValueDictionary = new RouteValueDictionary(routeValues);
    var requestContext = urlHelper.RequestContext;
    if (ignoreCurrentRouteValues) {
        var currentRouteData = requestContext.RouteData;
        var newRouteData = new RouteData(currentRouteData.Route, currentRouteData.RouteHandler);
        requestContext = new RequestContext(requestContext.HttpContext, newRouteData);
    }

    return UrlHelper.GenerateUrl(null, actionName, controllerName, routeValueDictionary,
        urlHelper.RouteCollection, requestContext, includeImplicitMvcValues: false);
}
1
Ber'Zophus

あなたの好みに応じてページパラメータを変更するなど、現在のページルーティングデータを処理するために働く愚かな回避策

@{
    var current_route_1 = new RouteValueDictionary(Url.RequestContext.RouteData.Values);
    var current_route_2 = new RouteValueDictionary(Url.RequestContext.RouteData.Values);

    //If you want to customize the routing values
    current_route_1["controller"] = "Controller1";
    current_route_2["controller"] = "Controller2";
}

@Url.RouteUrl(current_route_1);
@Url.RouteUrl(current_route_2);
0
Mahdi Fathalla

簡単な回避策は、最初に呼び出すことです

Url.Action("dummy", new { ... }) 

次に、結果の文字列のダミーの名前を正しいアクション名に変更します。

0
Martin Staufcik