[ASP.NET WebAPI] Como recibir tipos complejos en nuestros controladores por URL

Actualmente, me encuentro desarrollando una API, me he encontrado con que en  algunos controladores, mejor dicho, en los métodos o acciones de estos controladores necesito recibir un tipo complejos, algo como esto:

public JObject GetProductsByTag(string tag, QueryStringRequestCommand requestCommand)

Este tipo complejo tiene básicamente las siguientes propiedades:

public class QueryStringRequestCommand
{
    [Minimum(1)]
    public int PageIndex { get; set; }
    [Minimum(1)]
    [Maximun(50)]
    public int PageSize { get; set; }
    public string Sort { get; set; }
} 

¿Para que uso este tipo complejo?

Pues para agrupar los valores que me vienen del QueryString y no tener un controlador con muchos parámetros y poder validarlos con atributos (DataAnnotations), así pues cuando se llame al controlador de productos:

/api/products/tagged/harinas?sort=newest&pageIndex=1&pageSize=20

se deserialize en un objeto del tipo QueryStringRequestCommand, pero!!! si pruebas este código tal cual lo he puesto, te darás cuenta que dicho parámetro vendrá a null:

image

Esto es debido a que al tratarse de un tipo complejo, WebAPI intentará deserializar la información con un media type formatter, que por defecto miran en el cuerpo de la petición y en nuestro caso está vacío porque esta información viene en la Url.

¿Pero como le digo a mi controlador como deserializar esta información?

Pues de una manera muy sencilla, basta con decorar el parámetro con este atributo FromUri para que utilize el model binder

public JObject GetProductsByTag(string tag, [FromUri] QueryStringRequestCommand requestCommand)

Probamos de nuevo y funciona!!!

image

¿A partir de ahora en todos métodos de mi controlador tengo que añadir el atributo FromUri?

Esa es una muy buena pregunta y la respuesta es NO.

Para ello vamos a hacer buen uso de la extensibilidad de WebAPI y para ello lo primero es crear una interfaz vacía que nos sirva de contrato:

public interface IRequestCommand
{
}

En nuestro tipo complejo la implementamos

public class QueryStringRequestCommand : IRequestCommand
{
    [Minimum(1)]
    public int PageIndex { get; set; }
    [Minimum(1)]
    [Maximun(50)]
    public int PageSize { get; set; }
    public string Sort { get; set; }
}

Y por último, vamos a añadir una regla a la colección de reglas ParameterBindingRules de la configuración (HttpConfiguration) para que sepa como hacer bind de nuestro tipo complejo:

config.ParameterBindingRules.Insert(
    0,
    descriptor => typeof(IRequestCommand).IsAssignableFrom(descriptor.ParameterType) ?
        new FromUriAttribute().GetBinding(descriptor) : null);

Cuando el tipo complejo que recibimos en la petición, sea asignable a nuestra interfaz IRequestCommand, que en el caso de QueryStringRequestCommand lo es porque implementa esta interfaz usará una instancia del atributo FromUri y así nos evitamos tener que estar decorando todos muestro métodos con este atributo (DRY).

image

Un saludo y espero que os haya gustado.

2 comentarios en “[ASP.NET WebAPI] Como recibir tipos complejos en nuestros controladores por URL”

  1. Buen post, seguramente lo que más me guste de los desarrollos de microsoft en los últimos años es que por fin se han tomado en serio la extensibilidad.

    Una curiosidad, ¿te gustan más las interfaces marcadoras que los atributos para identificar los tipos, o hay algún motivo concreto?

    Juanma.

  2. Juanma, no hay ningún motivo concreto. Es que sino no hay forma de extender,concretamente esto.

    No sería más fácil decir todas estás rutas se resuelven con parametros de la urí. Vamos digo yo:)

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *