26

ASP.NET MVC Preview 5 を使用すると (これはベータ版でも試行されています)、ルートのクエリ文字列の既定値が、クエリ文字列で渡される値をオーバーライドするようです。再現は、次のようなコントローラーを作成することです。

public class TestController : Controller
{
    public ActionResult Foo(int x)
    {
        Trace.WriteLine(x);
        Trace.WriteLine(this.HttpContext.Request.QueryString["x"]);
        return new EmptyResult();
    }
}

ルートは次のようにマッピングされます。

routes.MapRoute(
    "test",
    "Test/Foo",
    new { controller = "Test", action = "Foo", x = 1 });

次に、この相対 URI で呼び出します。

/Test/Foo?x=5

私が見るトレース出力は次のとおりです。

1
5

つまり、ルートに設定されたデフォルト値は、クエリ文字列で実際に指定されたかどうかに関係なく、常にメソッドに渡されます。クエリ文字列のデフォルトが削除された場合、つまりルートは次のようにマッピングされることに注意してください。

routes.MapRoute(
    "test",
    "Test/Foo",
    new { controller = "Test", action = "Foo" });

その後、コントローラーは期待どおりに動作し、値がパラメーター値として渡され、トレース出力が得られます。

5
5

これはバグのように見えますが、このようなバグが ASP.NET MVC フレームワークのベータ リリースにまだ残っている可能性があることは非常に驚くべきことです。デフォルトのクエリ文字列は厳密には難解な機能でもエッジ ケースの機能でもないためです。 、だからほぼ間違いなく私のせいです。私が間違っていることはありますか?

4

4 に答える 4

31

QueryStrings を使用した ASP.NET MVC を見る最良の方法は、それらをルートが認識していない値と考えることです。お気づきのように、QueryString は RouteData の一部ではないため、ルート値とは別のクエリ文字列として渡すものを保持する必要があります。

それらを回避する方法は、QueryString から渡された値が null の場合、アクションでデフォルト値を自分で作成することです。

あなたの例では、ルートは x について知っているため、URL は実際には次のようになります。

/Test/Foo or /Test/Foo/5

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

routes.MapRoute("test", "Test/Foo/{x}", new {controller = "Test", action = "Foo", x = 1});

あなたが探していた行動を得るために。

ページ番号などの QueryString 値を渡したい場合は、次のようにします。

/Test/Foo/5?page=1

そして、あなたの行動は次のように変わるはずです:

public ActionResult Foo(int x, int? page)
{
    Trace.WriteLine(x);
    Trace.WriteLine(page.HasValue ? page.Value : 1);
    return new EmptyResult();
}

今テスト:

Url:  /Test/Foo
Trace:
1
1

Url:  /Test/Foo/5
Trace:
5
1

Url:  /Test/Foo/5?page=2
Trace:
5
2

Url:  /Test/Foo?page=2
Trace:
1
2

これがいくつかのことを明確にするのに役立つことを願っています。

于 2009-01-19T18:36:09.193 に答える
15

私の同僚の1人が、これが仕様によるものであることを示すリンクを見つけました。その記事の作成者は、これが以前のリリースからの変更であるとMVCチームに問題を提起したようです。彼らからの回答は以下のとおりです(「ページ」については、「x」を読んで上記の質問に関連させることができます):

これは仕様によるものです。ルーティングは、クエリ文字列値とは関係ありません。RouteDataからの値のみに関係します。代わりに、デフォルトディクショナリから「ページ」のエントリを削除する必要があります。アクションメソッド自体またはフィルタで、「ページ」のデフォルト値がまだ設定されていない場合は設定します。

将来的には、パラメータをRouteData、クエリ文字列、またはフォームから明示的に取得されたものとしてマークする簡単な方法があることを望んでいます。それが実装されるまで、上記のソリューションは機能するはずです。そうでない場合はお知らせください。

したがって、この動作は「正しい」ように見えますが、驚き最小の原則に非常に直交しているため、私はまだそれを完全に信じることができません。


編集#1:投稿にはデフォルト値を提供する方法が詳しく説明されていActionMethodますが、アクセスに使用するプロパティMethodInfoが最新バージョンのASP.NET MVCで削除されているため、これは機能しなくなりました。私は現在代替案に取り組んでおり、完了したら投稿します。


編集#2:リンクされた投稿のアイデアをASP.NET MVCのプレビュー5リリースで動作するように更新しました。移動していないため保証できませんが、ベータリリースでも動作するはずです。まだそのリリースに。とてもシンプルなので、ここにインラインで投稿しました。

DefaultValueAttributeまず、デフォルトの属性があります(既存の.NETは、から継承する必要があるため、使用できませんCustomModelBinderAttribute)。

[AttributeUsage(AttributeTargets.Parameter)]
public sealed class DefaultAttribute : CustomModelBinderAttribute
{
    private readonly object value;

    public DefaultAttribute(object value)
    {
        this.value = value;
    }

    public DefaultAttribute(string value, Type conversionType)
    {
        this.value = Convert.ChangeType(value, conversionType);
    }

    public override IModelBinder GetBinder()
    {
        return new DefaultValueModelBinder(this.value);
    }
}

カスタムバインダー:

public sealed class DefaultValueModelBinder : IModelBinder
{
    private readonly object value;

    public DefaultValueModelBinder(object value)
    {
        this.value = value;
    }

    public ModelBinderResult BindModel(ModelBindingContext bindingContext)
    {
        var request = bindingContext.HttpContext.Request;
        var queryValue = request .QueryString[bindingContext.ModelName];
        return string.IsNullOrEmpty(queryValue) 
            ? new ModelBinderResult(this.value) 
            : new DefaultModelBinder().BindModel(bindingContext);
    }
}

そして、クエリ文字列に含まれるメソッドパラメータに簡単に適用できます。

public ActionResult Foo([Default(1)] int x)
{
    // implementation
}

チャームのように機能します!

于 2009-01-19T17:34:49.910 に答える
0

クエリ文字列パラメータがデフォルトを上書きしない理由は、人々がURLをハッキングするのを防ぐためだと思います。

誰かが、クエリ文字列に変更したくないコントローラー、アクション、またはその他のデフォルトが含まれているURLを使用する可能性があります。

@ Dale-Raganが提案したことを実行し、アクションメソッドで処理することで、この問題に対処しました。私のために働きます。

于 2009-10-01T10:06:29.773 に答える
-3

MVC でのルーティングのポイントは、クエリ文字列を取り除くことだと思いました。このような:

routes.MapRoute(
    "test",
    "Test/Foo/{x}",
    new { controller = "Test", action = "Foo", x = 1 });
于 2009-01-19T17:27:00.707 に答える