ASP.NET MVC QueryString se predetermina sobreescribiendo los valores suministrados?
asp.net-mvc routing (4)
La mejor forma de ver ASP.NET MVC con QueryStrings es considerarlos como valores que la ruta desconoce. Como descubrió, QueryString no es parte de RouteData, por lo tanto, debe mantener lo que está pasando como una cadena de consulta separada de los valores de ruta.
Una forma de evitarlos es crear valores predeterminados en la acción si los valores pasados desde QueryString son nulos.
En su ejemplo, la ruta sabe acerca de x, por lo tanto su url realmente debería verse así:
/Test/Foo or /Test/Foo/5
y la ruta debería verse así:
routes.MapRoute("test", "Test/Foo/{x}", new {controller = "Test", action = "Foo", x = 1});
Para obtener el comportamiento que estabas buscando.
Si quiere pasar un valor de QueryString, digamos como un número de página, entonces haría esto:
/Test/Foo/5?page=1
Y tu acción debería cambiar así:
public ActionResult Foo(int x, int? page)
{
Trace.WriteLine(x);
Trace.WriteLine(page.HasValue ? page.Value : 1);
return new EmptyResult();
}
Ahora la prueba:
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
Espero que esto ayude a aclarar algunas cosas.
Al utilizar ASP.NET MVC Preview 5 (aunque esto también se ha intentado con Beta), parece que los valores predeterminados de la cadena de consulta en una ruta anulan el valor que se transfiere en la cadena de consulta. Una reprografía es escribir un controlador como este:
public class TestController : Controller
{
public ActionResult Foo(int x)
{
Trace.WriteLine(x);
Trace.WriteLine(this.HttpContext.Request.QueryString["x"]);
return new EmptyResult();
}
}
Con ruta asignada de la siguiente manera:
routes.MapRoute(
"test",
"Test/Foo",
new { controller = "Test", action = "Foo", x = 1 });
Y luego invocarlo con este URI relativo:
/Test/Foo?x=5
La salida de rastreo que veo es:
1
5
Por lo tanto, en otras palabras, el valor predeterminado que se configuró para la ruta se pasa siempre al método, independientemente de si realmente se suministró en la cadena de consulta. Tenga en cuenta que si se elimina el valor predeterminado para la cadena de consulta, es decir, la ruta se asigna de la siguiente manera:
routes.MapRoute(
"test",
"Test/Foo",
new { controller = "Test", action = "Foo" });
Luego, el controlador se comporta como se esperaba y el valor se transfiere como el valor del parámetro, dando el resultado del rastreo:
5
5
Esto me parece un error, pero me resulta muy sorprendente que un error como este pueda seguir apareciendo en la versión beta de la estructura MVC de ASP.NET, ya que las cadenas de caracteres con valores predeterminados no son exactamente una característica esotérica o de borde marginal , por lo que es casi seguro que es mi culpa. ¿Alguna idea de lo que estoy haciendo mal?
Pensé que el punto con Routing en MVC es deshacerse de cadenas de querys. Me gusta esto:
routes.MapRoute(
"test",
"Test/Foo/{x}",
new { controller = "Test", action = "Foo", x = 1 });
Uno de mis colegas encontró un enlace que indica que esto es por diseño y parece que el autor de ese artículo planteó un problema con el equipo de MVC diciendo que esto era un cambio con respecto a lanzamientos anteriores. La respuesta de ellos estaba debajo (para la "página" puede leer "x" para que se relacione con la pregunta anterior):
Esto es por diseño. El enrutamiento no se ocupa de los valores de cadena de consulta; solo se refiere a los valores de RouteData. En su lugar, debe eliminar la entrada para "página" del diccionario Predeterminado, y ya sea en el método de acción mismo o en un filtro, establezca el valor predeterminado para "página" si aún no se ha establecido.
Esperamos que en el futuro tengamos una manera más fácil de marcar un parámetro como explícitamente proveniente de RouteData, la cadena de consulta o un formulario. Hasta que eso se implemente, la solución anterior debería funcionar. Por favor, háganos saber si no lo hace!
Entonces parece que este comportamiento es "correcto", sin embargo, es tan ortogonal al principio de menos asombro que todavía no puedo creerlo.
Edición n.º 1: tenga en cuenta que la publicación detalla un método de cómo proporcionar valores predeterminados; sin embargo, esto ya no funciona, ya que la propiedad ActionMethod
que utiliza para acceder a MethodInfo
se ha eliminado en la última versión de ASP.NET MVC. Actualmente estoy trabajando en una alternativa y la publicaré cuando termine.
Edición n.º 2: actualicé la idea en la publicación vinculada para trabajar con la versión Preview 5 de ASP.NET MVC y creo que también debería funcionar con la versión Beta, aunque no puedo garantizarlo ya que no nos hemos mudado a ese lanzamiento todavía. Es tan simple que acabo de publicarlo en línea aquí.
Primero está el atributo predeterminado (no podemos usar el .NET DefaultValueAttribute
existente ya que necesita heredar de 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);
}
}
La carpeta personalizada
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);
}
}
Y luego puede simplemente aplicarlo a los parámetros del método que vienen en la cadena de consulta, por ejemplo
public ActionResult Foo([Default(1)] int x)
{
// implementation
}
¡Funciona de maravilla!
Creo que la razón por la que los parámetros de la cadena de consulta no anulan los valores predeterminados es evitar que las personas pirateen la URL.
Alguien podría usar una URL cuya cadena de consulta incluía controlador, acción u otros valores predeterminados que no quisiera que cambiaran.
He resuelto este problema haciendo lo que @ Dale-Ragan sugirió y tratando con él en el método de acción. Funciona para mi.