Wie soll ich mehrere Parameter an ein ASP.Net Web API GET übergeben?

136

Ich verwende die .NET MVC4-Web-API, um (hoffentlich) eine RESTful-API zu implementieren. Ich muss ein paar Parameter an das System übergeben und es eine Aktion ausführen lassen, um dann eine Liste von Objekten als Ergebnis zurückzugeben. Insbesondere übergebe ich zwei Daten und gebe Aufzeichnungen zurück, die zwischen ihnen liegen. Ich verfolge auch die zurückgegebenen Datensätze, damit nachfolgende Anrufe im System nicht erneut verarbeitet werden.

Ich habe einige Ansätze in Betracht gezogen:

  1. Serialisieren der Parameter in eine einzelne JSON-Zeichenfolge und Auswählen in der API. http://forums.asp.net/t/1807316.aspx/1

  2. Übergeben Sie die Parameter in der Abfragezeichenfolge.
    Was ist der beste Weg, um mehrere Abfrageparameter an eine erholsame API zu übergeben?

  3. Definieren der Parameter in der Route: api / controller / date1 / date2

  4. Verwenden eines POST, mit dem ich von Natur aus ein Objekt mit Parametern übergeben kann.

  5. Erforschung von ODATA, da die Web-API dies (derzeit) unterstützt. Ich habe noch nicht viel damit gemacht, daher bin ich damit nicht sehr vertraut.

Es scheint, dass die richtigen REST-Praktiken anzeigen, wenn Daten abgerufen werden, sollten Sie ein GET verwenden. GET sollte jedoch auch nullipotent sein (erzeugt keine Nebenwirkungen), und ich frage mich, ob meine spezifische Implementierung dies verletzt, da ich Datensätze im API-System markiere und daher Nebenwirkungen hervorrufe.

Es führte mich auch zur Frage der Unterstützung variabler Parameter. Wenn sich die Eingabeparameterliste ändert, wäre es mühsam, Ihre Route für Auswahl 3 neu zu definieren, wenn dies häufig vorkommt. Und was kann passieren, wenn die Parameter zur Laufzeit definiert werden ...

Welche Auswahl (falls vorhanden) scheint für meine spezifische Implementierung auf jeden Fall am besten zu sein?

sig606
quelle

Antworten:

10

Was bedeutet diese Rekordkennzeichnung? Wenn dies nur zu Protokollierungszwecken verwendet wird, würde ich GET verwenden und das gesamte Caching deaktivieren, da Sie jede Abfrage für diese Ressourcen protokollieren möchten. Wenn die Datensatzmarkierung einen anderen Zweck hat, ist POST der richtige Weg. Der Benutzer sollte wissen, dass seine Aktionen das System beeinflussen und die POST-Methode eine Warnung ist.

LukLed
quelle
Mit Markieren meine ich einfach zu verfolgen, welche Datensätze verarbeitet und zurückgegeben werden, damit nachfolgende Anrufe sie nicht wiederholen. In meinem Fall füge ich nur eine Einfügung in eine andere Tabelle ein, um zu verfolgen, welche verarbeitet werden.
Sig606
Im Moment habe ich es als POST implementiert, hauptsächlich aus dem Grund, den Sie gesagt haben - Aktionen passieren und der Verbraucher ist sich dessen bewusst. Außerdem scheint es einfach und am flexibelsten zu sein,
wenn
@ sig606: POST ist der richtige Weg für mich, aber Ihr Protokoll scheint nicht sicher zu sein. Was passiert, wenn etwas passiert und Datensätze auf der Clientseite abgerufen, aber aufgrund eines Fehlers nicht verarbeitet werden? Sie werden sie nicht mehr zurückgeben und der Kunde hat verlorene Daten.
LukLed
Im Moment gibt meine API Datensätze erst zurück, nachdem sie verarbeitet wurden. Der Verbraucher übergibt der API also zwei Daten. Datensätze zwischen diesen beiden Daten werden verarbeitet und markiert. Dann werden die Daten an den Anrufer zurückgegeben. Ich nehme an, wenn während der Verarbeitung oder nach der Verarbeitung etwas passiert, bevor ich den Client erreiche, habe ich ein Problem.
Sig606
141

Ich denke, der einfachste Weg ist, einfach zu benutzen AttributeRouting.

In Ihrem Controller ist klar, warum Sie dies in Ihrer globalen WebApiConfigDatei wünschen .

Beispiel:

    [Route("api/YOURCONTROLLER/{paramOne}/{paramTwo}")]
    public string Get(int paramOne, int paramTwo)
    {
        return "The [Route] with multiple params worked";
    }

Die {}Namen müssen Ihren Parametern entsprechen.

So einfach ist das. Jetzt haben Sie eine separate GETDatei, die in dieser Instanz mehrere Parameter verarbeitet.

Mark Pieszak - Trilon.io
quelle
12
Das ist toll. Die meisten Leute empfehlen, die Route in der WebApiConfigDatei einzurichten , aber das ist in der Tat schöner.
Rhyek
4
In der Tat empfehlen wir (die meisten Leute), einen zentralen Verwaltungsbereich für Ihre Konfiguration zu haben. Bei Web-APIs (Microsoft oder andere) sind zentralisierte Muster für REST der Schlüssel. Attribut-Routing ist niedlich, aber es macht einmalige Ausnahmen viel zu verlockend.
David Betz
3
Einverstanden, ich muss meine Antwort tatsächlich aktualisieren. Es gibt eine viel bessere Möglichkeit, mit GETs mehrere Parameter zu erstellen. Als ich neu in WebAPI war, verwende ich AttributeRouting nicht (es sei denn, ich möchte keinen neuen Controller erstellen) und übergebe alle Parameter im QueryString. Sie werden automatisch zugeordnet. Aktualisierung , wenn ich eine Chance bekommen , damit die Menschen diese ältere Methode nicht verwenden
Mark Pieszak - Trilon.io
Gibt es eine Möglichkeit, einen Routefür benannte Parameter festzulegen (z. B. Abfrageparameter)?
Shimmy Weitzhandler
1
Wenn der Name der Aktionsmethode erforderlich ist, kann dieser geändert werden, um dies zu berücksichtigen. [Route ("api / YOURCONTROLLER / Get / {paramOne} / {paramTwo}")] öffentlicher String Get (int paramOne, int paramTwo) {return "Something"; }
Dash
49

Fügen Sie einfach eine neue Route zu den WebApiConfigEinträgen hinzu.

Zum Beispiel, um anzurufen:

public IEnumerable<SampleObject> Get(int pageNumber, int pageSize) { ..

hinzufügen:

config.Routes.MapHttpRoute(
    name: "GetPagedData",
    routeTemplate: "api/{controller}/{pageNumber}/{pageSize}"
);

Fügen Sie dann die Parameter zum HTTP-Aufruf hinzu:

GET //<service address>/Api/Data/2/10 
Graham Wright
quelle
10
Dies scheint die einzige Antwort zu sein, die alle Teile auflistet. Ich wünschte, jemand hätte besser beschrieben, wie man den api/controller?start=date1&end=date2Stil-URI verwendet.
Hot Licks
@Hot Licks Die Antwort von Andrew Veriga funktioniert gut mit Argumenten für Abfragezeichenfolgen. Im Wesentlichen binden Sie die Namen der Abfragezeichenfolgen an Klasseneigenschaften und übergeben sie an Ihre Methode. Ihre Methode verwendet ein einzelnes Klassenargument, das mit dem Attribut [FromUri] gekennzeichnet ist, und hat Ihre Abfragezeichenfolgenargumente als Eigenschaften.
David Peterson
Tolles Zeug. Vielen Dank!
Hugo Nava Kopp
hi @HotLicks und GrahamWright denkst du, du kannst diese Frage beantworten? Vielen Dank, stackoverflow.com/questions/57565318/…
45

Ich musste nur eine RESTfull-API implementieren, in der ich Parameter übergeben muss. Dazu habe ich die Parameter in der Abfragezeichenfolge im gleichen Stil übergeben, wie er in Marks erstem Beispiel "api / controller? Start = date1 & end = date2" beschrieben wurde.

Im Controller habe ich einen Tipp von URL verwendet, der in C # aufgeteilt ist.

// uri: /api/courses
public IEnumerable<Course> Get()
{
    NameValueCollection nvc = HttpUtility.ParseQueryString(Request.RequestUri.Query);
    var system = nvc["System"];
    // BL comes here
    return _courses;
}

In meinem Fall rief ich das WebApi über Ajax an und sah aus wie folgt:

$.ajax({
        url: '/api/DbMetaData',
        type: 'GET',
        data: { system : 'My System',
                searchString: '123' },
        dataType: 'json',
        success: function (data) {
                  $.each(data, function (index, v) {
                  alert(index + ': ' + v.name);
                  });
         },
         statusCode: {
                  404: function () {
                       alert('Failed');
                       }
        }
   });

Ich hoffe das hilft...

Nigel Findlater
quelle
2
Ich denke, Sie verwenden WebApi nicht, weil ParameterBinding Ihren Querystring automatisch auf die Parameter Ihrer API-Methode abbildet ...
emp
1
Ja, ein besserer Weg wäre, [Route ("api / DbMetaData / {system} / {searchString}")] zu verwenden und zuzuschreiben und dann dem Get (String-System, String-SearchString) dort Parameter hinzuzufügen und dann mit " ... api / DbMetaData / mysystem / mysearchstring "
Nigel Findlater
Ich habe sein Beispiel in meinem C # MVC WebApi verwendet und es hat gut funktioniert. +1 zum Beispiel
Si8
38

Ich habe auf http://habrahabr.ru/post/164945/ eine hervorragende Lösung gefunden.

public class ResourceQuery
{
   public string Param1 { get; set; }
   public int OptionalParam2 { get; set; }
}

public class SampleResourceController : ApiController
{
    public SampleResourceModel Get([FromUri] ResourceQuery query)
    {
        // action
    }
}
Andrew Veriga
quelle
5
Der Hinweis hier ist der [FromUri]
Tranceporter
2
Obwohl der Artikel auf Russisch ist, ist @tranceporter richtig. Das "FromUri" scheint eine großartige Möglichkeit zu sein, die Parameter aus der URL abzurufen. Ein weiterer Artikel, der hilfreich sein könnte: asp.net/web-api/overview/formats-and-model-binding/…
Greg
Das ist es, was ich jetzt schon eine ganze Weile mache und es hat großartig funktioniert! Ich würde diese Lösung auch empfehlen.
David Peterson
Wenn Sie eine andere Hilfsmethode (nicht die Get) aufrufen , können Sie diese trotzdem verwenden [FromUri]? Ich kann das scheinbar nicht zum Laufen bringen.
Jocull
8

Die Verwendung von GET oder POST wird durch @LukLed klar erklärt . In Bezug auf die Art und Weise, wie Sie die Parameter übergeben können, würde ich vorschlagen, den zweiten Ansatz zu wählen (ich weiß auch nicht viel über ODATA ).

1.Serialisieren Sie die Parameter in eine einzelne JSON-Zeichenfolge und nehmen Sie sie in der API auseinander. http://forums.asp.net/t/1807316.aspx/1

Dies ist nicht benutzerfreundlich und SEO-freundlich

2. Übergeben Sie die Parameter in der Abfragezeichenfolge. Was ist der beste Weg, um mehrere Abfrageparameter an eine erholsame API zu übergeben?

Dies ist der übliche bevorzugte Ansatz.

3.Definieren der Parameter in der Route: api / controller / date1 / date2

Dies ist definitiv kein guter Ansatz. Dies gibt dem Gefühl, dass jemand date2eine Unterressource von ist, date1und das ist nicht der Fall. Sowohl die date1als auch date2sind Abfrageparameter und befinden sich auf derselben Ebene.

Im einfachen Fall würde ich eine URI wie diese vorschlagen,

api/controller?start=date1&end=date2

Ich persönlich mag das folgende URI-Muster, aber in diesem Fall müssen wir einen benutzerdefinierten Code schreiben, um die Parameter abzubilden.

api/controller/date1,date2
VJAI
quelle
Eigentlich waren das meine ursprünglichen Erklärungen. Ich denke, LukLed hat meine Tags und meinen URL-Link verbessert.
Sig606
Was SEO betrifft, würde dies in diesem Fall nicht zutreffen. Dieser Code wird "Server-zu-Server" sein, daher wäre es mir egal, ob die Außenwelt ihn jemals entdeckt hätte. Tatsächlich muss ich sicherstellen, dass die richtigen Sicherheitsmaßnahmen ergriffen werden, um einen wahlfreien Zugriff zu vermeiden. Ich musste die JSON-Serialisierung für einen anderen Teil des Systems durchführen (scheint ein Fehler zu sein, der versucht, große Listen von obj zu POSTEN, also musste ich in Zeichenfolgen serialisieren), daher wäre dies in diesem Fall kein großer Aufwand .
Sig606
1
Ich hoffe, Sie haben bereits Antworten, warum Sie Fragen stellen?
VJAI
2
Entschuldigung für diese späte Antwort, Mark. Ich hatte einige Lösungen ausprobiert, war mir aber nicht sicher, welche die beste war, und versuchte, mich an branchenübliche Ansätze zu halten. Deshalb habe ich hier bei SO gepostet.
Sig606
1
@Mark So etwas wie das nächste: stackoverflow.com/questions/9681658/… ?
RredCat
3
 [Route("api/controller/{one}/{two}")]
    public string Get(int One, int Two)
    {
        return "both params of the root link({one},{two}) and Get function parameters (one, two)  should be same ";
    }

Beide Parameter des Root-Links ({eins}, {zwei}) und der Get-Funktionsparameter (eins, zwei) sollten identisch sein

Ashwath Hegde
quelle
2

Ich weiß, dass dies wirklich alt ist, aber ich wollte das Gleiche in letzter Zeit und hier ist, was ich gefunden habe ...

    public HttpResponseMessage Get([FromUri] string var, [FromUri] string test) {
        var retStr = new HttpResponseMessage(HttpStatusCode.OK);
        if (var.ToLower() == "getnew" && test.ToLower() == "test") {
            retStr.Content = new StringContent("Found Test", System.Text.Encoding.UTF8, "text/plain");
        } else {
            retStr.Content = new StringContent("Couldn't Find that test", System.Text.Encoding.UTF8, "text/plain");
        }

        return retStr;
    }

Also jetzt in deiner Adresse / URI / ...

http (s): // myURL / api / myController /? var = getnew & test = test

Ergebnis: "Gefundener Test"


http (s): // myURL / api / myController /? var = getnew & test = irgendetwas

Ergebnis: "Dieser Test konnte nicht gefunden werden"

Rick Riggs
quelle
Ich persönlich mag diesen Stil in C #, weil ich die Signatur der ursprünglichen Methode ändern und genau das überladen kann, was ich erreichen möchte, ohne die Routing-Konfigurationen zu optimieren. Ich hoffe, es hilft anderen, die an diesen (möglicherweise veralteten) Ansatz gewöhnt sind, eine GET-Anfrage zu stellen.
Rick Riggs
1
Ich musste eine Ereignis-API erstellen, die von einer Kalender-App eines Drittanbieters verwendet wird, die diesen Ansatz verwendet. Ich bin froh, dass ich diese Antwort gefunden habe!
ClayRay
0
    public HttpResponseMessage Get(int id,string numb)
    {
        //this will differ according to your entity name
        using (MarketEntities entities = new MarketEntities())
        {
          var ent=  entities.Api_For_Test.FirstOrDefault(e => e.ID == id && e.IDNO.ToString()== numb);
            if (ent != null)
            {
                return Request.CreateResponse(HttpStatusCode.OK, ent);
            }
            else
            {
                return Request.CreateErrorResponse(HttpStatusCode.NotFound, "Applicant with ID " + id.ToString() + " not found in the system");
            }
        }
    }
Jesse Mwangi
quelle