Parâmetros de cadeia de consulta opcionais na API da Web do ASP.NET

212

Preciso implementar o seguinte método WebAPI:

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

Todos os parâmetros da string de consulta podem ser nulos. Ou seja, o chamador pode especificar de 0 a todos os 5 parâmetros.

No MVC4 beta, eu costumava fazer o seguinte:

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) 
    {
        // ...
    }
}

O MVC4 RC não se comporta mais assim. Se eu especificar menos de 5 parâmetros, ele responderá com um 404ditado:

Nenhuma ação foi encontrada no controlador 'Books' que corresponde à solicitação.

Qual é a assinatura de método correta para se comportar como costumava ser, sem ter que especificar o parâmetro opcional no roteamento de URL?

frapontillo
fonte
coloque [httpget] em ação.
User960567 08/08/2012
2
Se eu definir todos os parâmetros, o método será chamado; além disso, começa com Getisso, é automaticamente ligado com o HTTP GETmétodo ...
frapontillo
Isto é como web api roteamento obras, asp.net/web-api/overview/web-api-routing-and-actions/...
user960567
4
Sim. Eu sei como isso funciona. Eu simplesmente não consigo fazê-lo funcionar sob ESTA circunstância específica.
Frapontillo 9/08
Como isso foi compilado? string?não é um tipo válido. Você não pode declarar stringcomo um tipo anulável, pois é um tipo de referência.
EkoostikMartin

Respostas:

307

Esse problema foi corrigido na versão regular do MVC4. Agora você pode fazer:

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

e tudo funcionará imediatamente.

frapontillo
fonte
Posso usar nulo aqui como padrão? Por exemplo: string author = null?
Boris Zinchenko
2
Sim, nullé considerada uma expressão constante e, portanto, um valor padrão válido .
JDawg
Eu me pergunto por que temos que mencionar valores padrão, mesmo para parâmetros opcionais, como dito aqui . Qualquer tipo em C # sempre tem um valor padrão, portanto, o tempo de execução do roteamento poderia ter assumido o valor padrão do tipo se não o recebesse do URI. Qual é a razão técnica por trás disso? Tenho certeza que isso tem algo a ver com o fichário do modelo.
RBT 02/02
@RBT Assim que a rota pode ser combinado
James Westgate
Eu estava usando parâmetros de data e se eu apenas defini-los como nulo não estava funcionando. Então, eu tenho que configurá-lo anulável e o conjunto nulo como valor padrão e usar a validação do lado do servidor adequadamente e retornar as mensagens de erro. Funcionou.
Atta H.
85

É possível passar vários parâmetros como um único modelo, conforme sugerido pelo vijay. Isso funciona para GET quando você usa o atributo de parâmetro FromUri. Isso informa ao WebAPI para preencher o modelo a partir dos parâmetros de consulta.

O resultado é uma ação mais limpa do controlador com apenas um único parâmetro. Para obter mais informações, 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; }
  }

Ele ainda suporta vários parâmetros, desde que as propriedades não entrem em conflito.

// 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; }
}

Atualização :
para garantir que os valores sejam opcionais, use tipos de referência ou anuláveis ​​(por exemplo, int?) Nas propriedades do modelo.

Andrew C
fonte
4
Sim, mas o decorador [FromUri] sozinho não parece suportar parâmetros opcionais.
John Meyer
6
@JohnMeyer Você está correto ao usar o [FromUri] não responde diretamente à pergunta original. Basicamente, diz preencher esses modelos com os valores do Uri. As propriedades dos modelos precisariam ser anuláveis ​​ou um tipo de referência para que eles suportassem ser opcional. Adicionadas informações adicionais.
Andrew C
@ AndrewndC - Você poderia explicar quando / por que você precisa usar valores nulos para garantir que os valores sejam opcionais? Se você não tornar os valores anuláveis ​​(por exemplo, propriedade int Skip) e não houver parâmetros de consulta para essa propriedade especificada, o método API Controller ainda corresponderá à solicitação com êxito e o valor para Skipserá apenas o valor padrão para esse tipo, ou 0 neste caso
Clark
2
@Clark - Sem usar um tipo anulável, você não saberá se o usuário não forneceu um valor e obteve o valor do tipo não inicializado (0 para int) ou se o usuário especificou 0. Ao usar anulável, você tem certeza que o usuário o deixou indefinido portanto, você pode aplicar com segurança seu padrão na ação do controlador. Se você olhar para o Take do exemplo acima, o que a ação deve fazer se receber um 0 para o Take? O usuário pretendeu solicitar 0 registros ou não o especificou e, portanto, você deve obter todos os registros. Geralmente, se você deseja que um tipo de valor (int, bool, etc.) seja opcional, deve ser anulável.
Andrew C
70

Use valores padrão iniciais para todos os parâmetros como abaixo

public string GetFindBooks(string author="", string title="", string isbn="", string  somethingelse="", DateTime? date= null) 
{
    // ...
}
Muhammad Amin
fonte
1
Este é o procedimento correto, mas por um lado: DateTimenão é anulável. Eu já tentei usar DateTime?, mas o MVC não mapeia a solicitação para o método especificado se eu definir apenas alguns dos parâmetros na minha solicitação HTTP.
Frapontillo 8/08
você pode passar a data como uma string e analisá-la dentro da função do controlador usando a função DateTime.Parse ().
Muhammad Amin
1
@MuhammadAmin, DateTimenão é um tipo de dados anulável . Seu código não deve compilar, como você não seria capaz de atribuir um nullvalor a um parâmetro do tipo DateTime. Talvez você deva alterá-lo para DateTime?ou usar um valor diferente para um padrão DateTime.Now.
Ivaylo Slavov
1
@IvayloSlavov DateTime.Now não é uma constante de tempo de compilação, portanto não pode ser atribuída como o parâmetro padrão.
GiriB
@GiriB, você está certo mesmo. Datetime.Nownão pode ser usado na inicialização de parâmetros padrão, permaneço corrigido.
Ivaylo Slavov
1

se você quiser passar vários parâmetros, poderá criar um modelo em vez de passar vários parâmetros.

caso você não queira passar nenhum parâmetro, também poderá pular nele, e seu código parecerá limpo e em boas condições.

vijay
fonte
1
Isso é verdadeiro apenas para os parâmetros POST no corpo da solicitação - os parâmetros no URL ainda podem ser referenciados individualmente como argumentos.
Nathan
1

Os valores padrão não podem ser fornecidos para parâmetros que não são declarados ' optional'

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

Na tua 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
fonte
8
Na verdade, eles podem. Estou usando c #, não VB.NET.
Frapontillo