Opcjonalne parametry ciągu zapytania w interfejsie API sieci Web ASP.NET

212

Muszę zaimplementować następującą metodę WebAPI:

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

Wszystkie parametry ciągu zapytania mogą mieć wartość NULL. Oznacza to, że dzwoniący może określić od 0 do wszystkich 5 parametrów.

W wersji beta MVC4 wykonałem następujące czynności:

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 już się tak nie zachowuje. Jeśli podam mniej niż 5 parametrów, odpowiada to 404powiedzeniem:

Nie znaleziono działania na kontrolerze „Books”, które pasowałoby do żądania.

Jaki jest prawidłowy podpis metody, aby działał tak, jak kiedyś, bez konieczności podawania opcjonalnego parametru w routingu adresu URL?

frapontillo
źródło
uruchom [httpget] do działania.
user960567,
2
Jeśli ustawię wszystkie parametry, metoda zostanie wywołana; ponadto zaczyna się od, Getwięc jest automatycznie związany HTTP GETmetodą ...
frapontillo
Tak działa routing interfejsu API, asp.net/web-api/overview/web-api-routing-and-actions/…
user960567
4
Tak. Wiem jak to działa. Po prostu nie mogę zmusić go do działania w TYCH szczególnych okolicznościach.
frapontillo,
Jak to się w ogóle skompilowało? string?nie jest prawidłowym typem. Nie można zadeklarować stringjako typu zerowalnego, ponieważ jest to typ odwołania.
EkoostikMartin

Odpowiedzi:

307

Ten problem został rozwiązany w regularnej wersji MVC4. Teraz możesz zrobić:

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

i wszystko zadziała po wyjęciu z pudełka.

frapontillo
źródło
Czy mogę użyć tutaj wartości null jako domyślnej? Na przykład: ciąg autor = null?
Boris Zinchenko
2
Tak, nulljest uważane za wyrażenie stałe i dlatego jest prawidłową wartością domyślną .
JDawg
Zastanawiam się, dlaczego musimy wspomnieć o wartościach domyślnych nawet dla parametrów opcjonalnych, jak powiedziano tutaj . Każdy typ w języku C # zawsze ma wartość domyślną, więc czas działania routingu mógł przyjąć domyślną wartość typu, jeśli nie otrzyma go z identyfikatora URI. Jaki jest tego techniczny powód ?. Jestem pewien, że ma to coś wspólnego z segregatorem modeli.
RBT
@RBT Tak, aby można było dopasować trasę
James Westgate
Używałem parametrów daty i jeśli ustawiłem je na wartość zerową, nie działało. Więc muszę ustawić wartość zerową i ustawić wartość null jako wartość domyślną, i odpowiednio użyć weryfikacji po stronie serwera i zwrócić komunikaty o błędach z powrotem. Zadziałało.
Atta H.,
85

Możliwe jest przekazanie wielu parametrów jako jednego modelu, jak sugeruje Vijay. Działa to dla GET, gdy używasz atrybutu parametru FromUri. Mówi to interfejsowi WebAPI o wypełnieniu modelu na podstawie parametrów zapytania.

Rezultatem jest czystsze działanie kontrolera z jednym parametrem. Aby uzyskać więcej informacji, zobacz: 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; }
  }

Obsługuje nawet wiele parametrów, o ile właściwości nie powodują konfliktu.

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

Aktualizacja :
Aby upewnić się, że wartości są opcjonalne, należy użyć typów referencyjnych lub wartości zerowych (np. Int?) Dla właściwości modeli.

Andrew C.
źródło
4
Tak, ale sam dekorator [FromUri] nie obsługuje parametrów opcjonalnych.
John Meyer
6
@JohnMeyer Masz rację, używając [FromUri] nie odpowiada bezpośrednio na pierwotne pytanie. Mówi w zasadzie, że zapełnij te modele wartościami z Uri. Właściwości modeli musiałyby mieć wartość zerową lub typ referencyjny, aby mogły być opcjonalne. Dodano dodatkowe informacje.
Andrew C,
@AndrewC - Czy możesz wyjaśnić, kiedy / dlaczego musisz użyć wartości zerowych, aby upewnić się, że wartości są opcjonalne? Jeśli nie nadasz wartości zerowej (na przykład, właściwość int Skip) i nie określono parametru zapytania dla tej właściwości, metoda kontrolera interfejsu API nadal pomyślnie dopasuje żądanie, a wartość dla Skipbędzie tylko wartością domyślną dla tego typu, lub 0 w tym przypadku
Clark
2
@Clark - bez użycia typu zerowalnego nie będziesz wiedział, czy użytkownik nie podał wartości i otrzymał niezainicjowaną wartość typu (0 dla int), czy też użytkownik określił 0. Korzystając z wartości zerowej, masz pewność, że użytkownik pozostawił ją niezdefiniowaną dlatego możesz bezpiecznie zastosować swoje ustawienia domyślne w akcji kontrolera. Jeśli spojrzysz na Take z powyższego przykładu, co powinna zrobić akcja, jeśli otrzymała 0 za Take? Czy użytkownik chciał zażądać 0 rekordów, czy nie określił go, dlatego należy wziąć wszystkie rekordy. Ogólnie rzecz biorąc, jeśli chcesz, aby typ wartości (int, bool itp.) Był opcjonalny, powinien on mieć wartość null.
Andrew C
70

Użyj początkowych wartości domyślnych dla wszystkich parametrów, takich jak poniżej

public string GetFindBooks(string author="", string title="", string isbn="", string  somethingelse="", DateTime? date= null) 
{
    // ...
}
Muhammad Amin
źródło
1
Jest to poprawna procedura, ale z jednej strony: DateTimenie ma wartości zerowej. Próbowałem już użyć DateTime?zamiast tego, ale MVC nie mapuje żądania na podaną metodę, jeśli ustawię tylko niektóre parametry w moim żądaniu HTTP.
frapontillo
możesz przekazać date jako ciąg znaków i parsować ją wewnątrz funkcji kontrolera za pomocą funkcji DateTime.Parse ().
Muhammad Amin,
1
@MuhammadAmin, DateTimenie jest dopuszczalnym typem danych. Kod nie powinien się kompilować, ponieważ nie można przypisać nullwartości do parametru typu DateTime. Być może powinieneś to zmienić DateTime?lub użyć innej wartości jako domyślnej, takiej jak DateTime.Now.
Ivaylo Slavov
1
@IvayloSlavov DateTime.Now nie jest stałą czasową kompilacji, więc nie można jej przypisać jako parametru domyślnego.
GiriB
@GiriB, masz rację. Datetime.Nownie można użyć przy domyślnej inicjalizacji parametru, mam poprawkę.
Ivaylo Slavov
1

jeśli chcesz przekazać wiele parametrów, możesz utworzyć model zamiast wielu parametrów.

w przypadku, gdy nie chcesz przekazać żadnego parametru, możesz w nim również pominąć, a kod będzie wyglądał schludnie i czysto.

Vijay
źródło
1
Jest to prawdą tylko w przypadku parametrów POST w treści żądania - parametry w adresie URL mogą nadal być odniesione indywidualnie jako argumenty.
Nathan
1

Nie można podać wartości domyślnych dla parametrów, które nie zostały zadeklarowane ' optional'

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

W Twoim 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
źródło
8
W rzeczywistości mogą. Używam C #, a nie VB.NET.
frapontillo