Optionale Abfragezeichenfolgenparameter in der ASP.NET-Web-API

212

Ich muss die folgende WebAPI-Methode implementieren:

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

Alle Parameter der Abfragezeichenfolge können null sein. Das heißt, der Aufrufer kann von 0 bis zu allen 5 Parametern angeben.

In MVC4 Beta habe ich Folgendes gemacht:

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 verhält sich nicht mehr so. Wenn ich weniger als 5 Parameter spezifiziere, antwortet es mit einem 404Sprichwort:

Auf dem Controller 'Books' wurde keine Aktion gefunden, die der Anforderung entspricht.

Was ist die richtige Methodensignatur, damit sie sich wie früher verhält, ohne den optionalen Parameter im URL-Routing angeben zu müssen?

Frapontillo
quelle
Setzen Sie [httpget] in Aktion.
user960567
2
Wenn ich alle Parameter einstelle, wird die Methode aufgerufen. Außerdem beginnt es mit, Getso dass es automatisch an die HTTP GETMethode gebunden wird ...
Frapontillo
So funktioniert Web-API-Routing, asp.net/web-api/overview/web-api-routing-and-actions/…
user960567
4
Ja. Ich weiß, wie es funktioniert. Ich kann es unter DIESEN besonderen Umständen einfach nicht zum Laufen bringen.
Frapontillo
Wie wurde das überhaupt kompiliert? string?ist kein gültiger Typ. Sie können nicht stringals nullbaren Typ deklarieren, da es sich um einen Referenztyp handelt.
EkoostikMartin

Antworten:

307

Dieses Problem wurde in der regulären Version von MVC4 behoben. Jetzt können Sie tun:

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

und alles wird sofort funktionieren.

Frapontillo
quelle
Kann ich hier standardmäßig null verwenden? Zum Beispiel: string author = null?
Boris Zinchenko
2
Ja, nullwird als konstanter Ausdruck und daher als gültiger Standardwert betrachtet .
JDawg
Ich frage mich, warum wir Standardwerte auch für optionale Parameter erwähnen müssen, wie hier gesagt . Jeder Typ in C # hat immer einen Standardwert, sodass die Routing-Laufzeit den Standardwert des Typs hätte annehmen können, wenn er nicht vom URI empfangen worden wäre. Was ist der technische Grund dafür? Ich bin sicher, das hat etwas mit Modellbinder zu tun.
RBT
@ RBT Damit die Route angepasst werden kann
James Westgate
Ich habe Datumsparameter verwendet und wenn ich sie nur auf nullable gesetzt habe, hat das nicht funktioniert. Also muss ich es nullable und das Set null als Standardwert setzen und die serverseitige Validierung entsprechend verwenden und Fehlermeldungen zurückgeben. Es funktionierte.
Atta H.
85

Es ist möglich, mehrere Parameter als ein einziges Modell zu übergeben, wie von Vijay vorgeschlagen. Dies funktioniert für GET, wenn Sie das FromUri-Parameterattribut verwenden. Dadurch wird WebAPI angewiesen, das Modell aus den Abfrageparametern zu füllen.

Das Ergebnis ist eine sauberere Regleraktion mit nur einem einzigen Parameter. Weitere Informationen finden Sie unter: 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; }
  }

Es werden sogar mehrere Parameter unterstützt, solange die Eigenschaften nicht in Konflikt stehen.

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

Update :
Um sicherzustellen, dass die Werte optional sind, müssen Sie Referenztypen oder Nullables (z. B. int?) Für die Modelleigenschaften verwenden.

Andrew C.
quelle
4
Ja, aber der Dekorator [FromUri] allein scheint keine optionalen Parameter zu unterstützen.
John Meyer
6
@JohnMeyer Sie haben Recht, wenn Sie [FromUri] verwenden, wird die ursprüngliche Frage nicht direkt beantwortet. Grundsätzlich heißt es, diese Modelle mit den Werten aus dem Uri zu füllen. Die Modelleigenschaften müssten nullwertfähig oder ein Referenztyp sein, damit sie optional sind. Zusätzliche Informationen hinzugefügt.
Andrew C
@AndrewC - Können Sie erläutern, wann / warum Sie Nullables verwenden müssen, um sicherzustellen, dass Werte optional sind? Wenn Sie die Werte int Skipnicht nullbar machen (z. B. Eigenschaft ) und für diese Eigenschaft kein Abfrageparameter angegeben ist, stimmt die API-Controller-Methode weiterhin erfolgreich mit der Anforderung überein, und der Wert für Skipist nur der Standardwert für diesen Typ. oder 0 in diesem Fall
Clark
2
@Clark - Ohne Verwendung eines nullbaren Typs wissen Sie nicht, ob der Benutzer keinen Wert angegeben und den nicht initialisierten Typwert (0 für int) erhalten hat oder ob der Benutzer 0 angegeben hat. Durch die Verwendung von nullable ist sicher, dass der Benutzer ihn undefiniert gelassen hat Daher können Sie Ihre Standardeinstellung sicher in der Controller-Aktion anwenden. Wenn Sie sich Take aus dem obigen Beispiel ansehen, was sollte die Aktion tun, wenn sie eine 0 für Take erhalten hat? Wollte der Benutzer 0 Datensätze anfordern oder hat er diese nicht angegeben, und deshalb sollten Sie alle Datensätze nehmen. Wenn Sie möchten, dass ein Wertetyp (int, bool usw.) optional ist, sollte er im Allgemeinen nullwertfähig sein.
Andrew C
70

Verwenden Sie die anfänglichen Standardwerte für alle Parameter wie unten

public string GetFindBooks(string author="", string title="", string isbn="", string  somethingelse="", DateTime? date= null) 
{
    // ...
}
Muhammad Amin
quelle
1
Dies ist das richtige Verfahren, aber zum einen: DateTimeist nicht nullbar. Ich habe bereits versucht, DateTime?stattdessen zu verwenden , aber dann ordnet MVC die Anforderung nicht der angegebenen Methode zu, wenn ich nur einige der Parameter in meiner HTTP-Anforderung festlege.
Frapontillo
Sie können das Datum als Zeichenfolge übergeben und es mit der Funktion DateTime.Parse () in Ihrer Controller-Funktion analysieren.
Muhammad Amin
1
@MuhammadAmin DateTimeist kein nullbarer Datentyp. Ihr Code sollte nicht kompiliert werden, da Sie nulleinem Parameter vom Typ keinen Wert zuweisen können DateTime. Vielleicht sollten Sie es ändern DateTime?oder einen anderen Wert für einen Standardwert wie verwenden DateTime.Now.
Ivaylo Slavov
1
@IvayloSlavov DateTime.Now ist keine Kompilierungszeitkonstante und kann daher nicht als Standardparameter zugewiesen werden.
GiriB
@ GiriB, du hast in der Tat recht. Datetime.Nowkann nicht in der Standardparameterinitialisierung verwendet werden, ich stehe korrigiert.
Ivaylo Slavov
1

Wenn Sie mehrere Parameter übergeben möchten, können Sie ein Modell erstellen, anstatt mehrere Parameter zu übergeben.

Falls Sie keinen Parameter übergeben möchten, können Sie diesen auch überspringen, und Ihr Code sieht ordentlich und sauber aus.

Vijay
quelle
1
Dies gilt nur für die POST-Parameter im Anforderungshauptteil - Parameter in der URL können weiterhin einzeln als Argumente referenziert werden.
Nathan
1

Für Parameter, die nicht als ' optional' deklariert sind, können keine Standardwerte angegeben werden.

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

In deinem 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
quelle
8
Eigentlich können sie. Ich verwende C #, nicht VB.NET.
Frapontillo