Parámetros de cadena de consulta opcionales en la API web ASP.NET

212

Necesito implementar el siguiente método WebAPI:

/api/books?author=XXX&title=XXX&isbn=XXX&somethingelse=XXX&date=XXX

Todos los parámetros de la cadena de consulta pueden ser nulos. Es decir, la persona que llama puede especificar de 0 a todos los 5 parámetros.

En MVC4 beta solía hacer lo siguiente:

public class BooksController : ApiController
{
    // GET /api/books?author=tolk&title=lord&isbn=91&somethingelse=ABC&date=1970-01-01
    public string GetFindBooks(string author, string title, string isbn, string somethingelse, DateTime? date) 
    {
        // ...
    }
}

MVC4 RC ya no se comporta así. Si especifico menos de 5 parámetros, responde con un 404dicho:

No se encontró ninguna acción en el controlador 'Libros' que coincida con la solicitud.

¿Cuál es la firma correcta del método para que se comporte como solía, sin tener que especificar el parámetro opcional en el enrutamiento de URL?

frapontillo
fuente
poner [httpget] en acción.
user960567
2
Si configuro todos los parámetros, se llama al método; Por otra parte se inicia con Getlo que se enlaza automáticamente con el HTTP GETmétodo ...
frapontillo
Así es como funciona el enrutamiento de la API web, asp.net/web-api/overview/web-api-routing-and-actions/…
user960567
44
Si. Se como funciona. Simplemente no puedo hacer que funcione bajo ESTA circunstancia particular.
frapontillo
¿Cómo se compiló esto? string?No es un tipo válido. No puede declarar stringcomo un tipo anulable ya que es un tipo de referencia.
EkoostikMartin

Respuestas:

307

Este problema se ha solucionado en la versión regular de MVC4. Ahora puedes hacer:

public string GetFindBooks(string author="", string title="", string isbn="", string  somethingelse="", DateTime? date= null) 
{
    // ...
}

y todo saldrá de la caja.

frapontillo
fuente
¿Puedo usar nulo aquí como predeterminado? Por ejemplo: string author = null?
Boris Zinchenko
2
Sí, nullse considera una expresión constante y, por lo tanto, un valor predeterminado válido .
JDawg
Me pregunto por qué tenemos que mencionar los valores predeterminados incluso para los parámetros opcionales como se dice aquí . Cualquier tipo en C # siempre tiene un valor predeterminado, por lo que el tiempo de ejecución de enrutamiento podría haber tomado el valor predeterminado del tipo si no lo recibió del URI. ¿Cuál es la razón técnica detrás de esto? Estoy seguro de que esto tiene algo que ver con el modelo de carpeta.
RBT
@RBT Para que la ruta coincida
James Westgate
Estaba usando parámetros de fecha y si solo los configuré como anulables no funcionaba. Así que tengo que configurarlo como nulo y el valor nulo como valor predeterminado, y usar la validación del lado del servidor en consecuencia y devolver los mensajes de error. Funcionó.
Atta H.
85

Es posible pasar múltiples parámetros como un solo modelo, como sugiere vijay. Esto funciona para GET cuando usa el atributo de parámetro FromUri. Esto le dice a WebAPI que complete el modelo a partir de los parámetros de consulta.

El resultado es una acción de controlador más limpia con un solo parámetro. Para obtener más información, consulte: http://www.asp.net/web-api/overview/formats-and-model-binding/parameter-binding-in-aspnet-web-api

public class BooksController : ApiController
  {
    // GET /api/books?author=tolk&title=lord&isbn=91&somethingelse=ABC&date=1970-01-01
    public string GetFindBooks([FromUri]BookQuery query)
    {
      // ...
    }
  }

  public class BookQuery
  {
    public string Author { get; set; }
    public string Title { get; set; }
    public string ISBN { get; set; }
    public string SomethingElse { get; set; }
    public DateTime? Date { get; set; }
  }

Incluso admite múltiples parámetros, siempre que las propiedades no entren en conflicto.

// GET /api/books?author=tolk&title=lord&isbn=91&somethingelse=ABC&date=1970-01-01
public string GetFindBooks([FromUri]BookQuery query, [FromUri]Paging paging)
{
  // ...
}

public class Paging
{
  public string Sort { get; set; }
  public int Skip { get; set; }
  public int Take { get; set; }
}

Actualización :
para garantizar que los valores sean opcionales, asegúrese de utilizar tipos de referencia o valores nulos (ej. Int?) Para las propiedades de los modelos.

Andrew C
fuente
44
Sí, pero el decorador [FromUri] solo no parece admitir parámetros opcionales.
John Meyer
66
@JohnMeyer Tienes razón al usar [FromUri] no responde directamente a la pregunta original. Básicamente dice poblar estos modelos con los valores del Uri. Las propiedades de los modelos tendrían que ser anulables o un tipo de referencia para que admitan ser opcionales. Se agregó información adicional.
Andrew C
@ AndrewC: ¿Podría explicar cuándo / por qué necesita usar valores nulos para garantizar que los valores sean opcionales? Si no hace que los valores sean anulables (por ejemplo, propiedad int Skip) y no hay ningún parámetro de consulta para esa propiedad especificada, el método del Controlador API seguirá coincidiendo con éxito con la solicitud y el valor de Skipsolo será el valor predeterminado para ese tipo, o 0 en este caso
Clark
2
@Clark: sin usar un tipo anulable, no sabrá si el usuario no proporcionó un valor y obtuvo el valor de tipo no inicializado (0 para int) o si el usuario especificó 0. Al usar nullable, está seguro de que el usuario lo dejó indefinido por lo tanto, puede aplicar de manera segura su valor predeterminado en la acción del controlador. Si observa Take del ejemplo anterior, ¿qué debe hacer la acción si recibe un 0 para Take? ¿El usuario quiso solicitar 0 registros o no lo especificó y, por lo tanto, debe tomar todos los registros? En general, si desea que un tipo de valor (int, bool, etc.) sea opcional, entonces debe ser anulable.
Andrew C
70

Utilice los valores predeterminados iniciales para todos los parámetros como a continuación

public string GetFindBooks(string author="", string title="", string isbn="", string  somethingelse="", DateTime? date= null) 
{
    // ...
}
Muhammad Amin
fuente
1
Este es el procedimiento correcto, pero para empezar: DateTimeno es anulable. Ya he intentado usar DateTime?en su lugar, pero MVC no asigna la solicitud al método dado si configuro solo algunos de los parámetros en mi solicitud HTTP.
frapontillo
puede pasar la fecha como una cadena y analizarla dentro de la función del controlador utilizando la función DateTime.Parse ().
Muhammad Amin
1
@MuhammadAmin, DateTimeno es un tipo de datos anulable . Su código no debe compilarse, ya que no podría asignar un nullvalor a un parámetro de tipo DateTime. Tal vez, debería cambiarlo DateTime?o usar un valor diferente para un valor predeterminado como DateTime.Now.
Ivaylo Slavov
1
@IvayloSlavov DateTime.Now no es una constante de tiempo de compilación, por lo que no se puede asignar como parámetro predeterminado.
GiriB
@GiriB, tienes razón. Datetime.Nowno se puede utilizar en la inicialización de parámetros predeterminada, estoy corregido.
Ivaylo Slavov
1

Si desea pasar varios parámetros, puede crear un modelo en lugar de pasar varios parámetros.

en caso de que no desee pasar ningún parámetro, también puede omitirlo y su código se verá ordenado y limpio.

vijay
fuente
1
Esto solo es cierto para los parámetros POST en el cuerpo de la solicitud: los parámetros en la URL aún pueden ser referenciados individualmente como argumentos.
Nathan
1

No se pueden proporcionar valores predeterminados para parámetros que no están declarados ' optional'

 Function GetFindBooks(id As Integer, ByVal pid As Integer, Optional sort As String = "DESC", Optional limit As Integer = 99)

En tus WebApiConfig

 config.Routes.MapHttpRoute( _
          name:="books", _
          routeTemplate:="api/{controller}/{action}/{id}/{pid}/{sort}/{limit}", _
          defaults:=New With {.id = RouteParameter.Optional, .pid = RouteParameter.Optional, .sort = UrlParameter.Optional, .limit = UrlParameter.Optional} _
      )
Rizwan Mumtaz
fuente
8
En realidad pueden. Estoy usando C #, no VB.NET.
frapontillo