2009-01-19 15 views
25

Usando ASP.NET MVC Preview 5 (aunque esto también se ha intentado con Beta), parece que la cadena de consulta predeterminada en una ruta anula el valor que se pasa en la cadena de consulta. Un repro es escribir un controlador de la siguiente manera:ASP.NET MVC QueryString se predetermina reemplazando los valores suministrados?

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

con la ruta asignada de la siguiente manera:

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

e invocarla con este relativa URI:

/Test/Foo?x=5 

La traza de salida I vea 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 está asignada como sigue:

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

continuación, el controlador se comporta como se esperaba y el valor se pasa como el valor del parámetro, dando a la salida de rastreo:

5 
5 

Esto me parece un error, pero me resulta muy sorprendente que un error como este todavía pueda estar en la versión beta de la estructura ASP.NET MVC, ya que las cadenas con valores predeterminados no son exactamente esotéricas o función de borde de la caja, por lo que es casi seguro que es mi culpa. ¿Alguna idea de lo que estoy haciendo mal?

+0

El desbordamiento de pila necesita un botón de "retraso del proveedor de votos" para esos casos. – John

Respuesta

30

La mejor manera 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 ​​de QueryString son nulos.

En su ejemplo, la ruta sabe de x, por lo tanto, su URL debe realmente tener este aspecto:

/Test/Foo or /Test/Foo/5 

y la ruta debe tener este aspecto:

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

Para obtener el comportamiento que estaban buscando.

Si desea pasar un valor de cadena de consulta, por ejemplo, como un número de página, entonces podría hacer esto:

/Test/Foo/5?page=1 

y su acción debe 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.

+0

Sí, gracias, eso tiene sentido. Creo que lo que confundió el problema es que la ruta pone los valores predeterminados en los valores de los parámetros, aunque en realidad no debería conocerlos. Pero mantendré las cosas bien separadas ahora entiendo la diferencia semántica del motor de enrutamiento. –

+0

¿Podemos cambiar la lógica de enrutamiento para que el parámetro de serie de consulta pasado se trate como un valor de ruta. Esto se debe a que los valores de cadena de consulta no se transfieren en el mapa del sitio. – TrustyCoder

+0

Esto muestra que los desarrolladores siguen confundidos por la forma en que las rutas y las cadenas de consulta interactúan y funcionan en Controladores. Mi queja es que el sistema de parámetros del Método de acción es insuficiente. Está diseñado para buscar formularios, rutas y listas de cadenas de consulta coincidentes para valores que coincidan con los nombres de parámetros del controlador, lo cual es una tontería. Un mejor sistema sería proporcionar a los controladores una lista genérica masiva de todas las claves: valores pasados ​​de todas las fuentes, incluidas las rutas, y permitir a los desarrolladores extraer lo que se necesita. – Stokely

-3

Creo que el punto con Routing en MVC es deshacerse de las cadenas de caracteres. De esta manera:

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

Querystrings son apropiados cuando se trata de una consulta, p. puede tener params opcionales como sort, page, etc. Independientemente, esto no responde la pregunta de ninguna manera. –

+0

@Greg Beech: La pregunta debería al menos proporcionar un ejemplo que ilustre esto. – Spoike

15

Uno de mis colegas encontraron a link which indicates that this is by design y parece que el autor de ese artículo raised an issue with the MVC team diciendo que era un cambio de versiones anteriores. La respuesta de ellos estaba debajo (para "página" puede leer "x" para relacionarla con la pregunta anterior):

Esto es por diseño. El enrutamiento no se refiere a con la cadena de consulta valores; solo se refiere a los valores de RouteData. Debería eliminar la entrada para "página" del diccionario Predeterminado, y en ya sea el método de acción o en un filtro establecer el valor predeterminado para "página" si aún no se ha establecido.

Esperamos que en el futuro tienen una forma más fácil para marcar un parámetro como viene explícitamente de RouteData, la cadena consulta, o una forma. Hasta que sea implementado la solución anterior debe funcionar . Por favor, háganos saber si no!

Parece que este comportamiento es "correcto", sin embargo, es tan ortogonal al principle of least astonishment que todavía no puedo creerlo.


Edición # 1: Tenga en cuenta que el puesto detalla un método de cómo proporcionar valores por defecto, sin embargo, esto ya no funciona como la propiedad ActionMethod que utiliza para acceder al MethodInfo ha sido eliminado en la última versión de ASP. NET MVC. Actualmente estoy trabajando en una alternativa y la publicaré cuando termine.


Edición # 2: He actualizado la idea en el post relacionado con el trabajo con la versión preliminar 5 de ASP.NET MVC y yo creo que también debería funcionar con la versión Beta aunque no puede garantizar ya que aún no nos hemos mudado a esa versión. Es tan simple que acabo de publicarlo en línea aquí.

En primer lugar está el atributo predeterminado (que no puede utilizar el .NET existentes DefaultValueAttribute ya que tiene que 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); 
    } 
} 

El aglutinante la costumbre:

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 simplemente puede 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 
} 

¡Trabaja como un encanto!

+0

grr gracias por la solución, ¡esto me molestaba! –

+0

Greg es una solución genial, pero el problema que tengo es el hecho de que Value and Model Binding no forma cómo los encabezados HTTP y múltiples cadenas de consulta entran a través de GET o POST. No puedes predecir qué cadenas de consulta locas estarán presentes. Por lo tanto, deberían haber creado una lista dinámica simple en los controladores de todos los valores de Post/Get y luego subconjilarlos en modelos según sea necesario. Debido a que aún no entienden el valor de las cadenas de consulta y cómo se usan en la web, creo que se equivocaron una vez más en el uso de MVC. Solo una opinión ..... – Stokely

0

Creo que los parámetros de la cadena de consulta de razón no anulan los valores predeterminados para 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 deseaba 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.

Cuestiones relacionadas