Jak przekazać wiele parametrów do interfejsu API sieci Web ASP.Net GET?

136

Używam Web API .Net MVC4 do (miejmy nadzieję) zaimplementować RESTful api. Muszę przekazać kilka parametrów do systemu i zlecić mu wykonanie jakiejś czynności, a następnie zwrócić listę obiektów jako wyniki. Dokładniej mówiąc, podaję dwie daty i zwracam rekordy, które mieszczą się między nimi. Śledzę również zwracane rekordy, aby kolejne wywołania nie były ponownie przetwarzane w systemie.

Rozważyłem kilka podejść:

  1. Serializowanie parametrów w jeden ciąg JSON i wybieranie ich w interfejsie API. http://forums.asp.net/t/1807316.aspx/1

  2. Przekaż parametry w ciągu zapytania.
    Jaki jest najlepszy sposób przekazania wielu parametrów zapytania do spokojnego interfejsu API?

  3. Definiowanie parametrów w trasie: api / controller / date1 / date2

  4. Użycie POST, który z natury pozwala mi przekazać obiekt z parametrami.

  5. Badanie ODATA, ponieważ obsługuje je Web API (obecnie). Nie zrobiłem jeszcze wiele z tym, więc nie jestem z tym zaznajomiony.

Wygląda na to, że właściwe praktyki REST wskazują, kiedy pobierane są dane, należy użyć GET. Jednak GET powinien również być nieulotny (nie wywołuje skutków ubocznych) i zastanawiam się, czy moja konkretna implementacja to narusza, skoro zaznaczam rekordy w systemie API, stąd generuję efekty uboczne.

Doprowadziło mnie to również do pytania o obsługę zmiennych parametrów. Jeśli lista parametrów wejściowych ulegnie zmianie, ponowne zdefiniowanie trasy dla Wyboru 3 byłoby żmudne, gdyby zdarzało się to często. A co by się stało, gdyby parametry zostały zdefiniowane w czasie wykonywania ...

W każdym razie, w przypadku mojej konkretnej realizacji, który wybór (jeśli w ogóle) wydaje się najlepszy?

sig606
źródło

Odpowiedzi:

10

Co oznacza to oznaczenie rekordu? Jeśli jest to używane tylko do celów rejestrowania, użyłbym GET i wyłączyłbym buforowanie, ponieważ chcesz rejestrować każde zapytanie dotyczące tych zasobów. Jeśli oznaczanie rekordów ma inny cel, najlepszym rozwiązaniem jest POST. Użytkownik powinien wiedzieć, że jego działania mają wpływ na system, a metoda POST jest ostrzeżeniem.

LukLed
źródło
Przez zaznaczenie rozumiem po prostu śledzenie, które rekordy są przetwarzane i zwracane, aby kolejne wywołania ich nie powtarzały. W moim przypadku po prostu robię wstawkę do innej tabeli, aby śledzić, które są przetwarzane.
sig606
W tej chwili mam to zaimplementowane jako POST głównie z tego powodu, że powiedziałeś - akcje się zdarzają, a konsument jest ich świadomy. Poza tym wydaje się to łatwe i najbardziej elastyczne, jeśli chodzi o przekazywanie różnych danych.
sig606
@ sig606: POST jest właściwą drogą dla mnie, ale twój protokół nie wydaje się być bezpieczny. Co się stanie, jeśli coś się stanie i rekordy zostaną pobrane po stronie klienta, ale nie zostaną przetworzone z powodu błędu? Już ich nie zwrócisz, a klient zostanie z utraconymi danymi.
LukLed
W tej chwili moje API zwraca rekordy dopiero po ich przetworzeniu. Tak więc konsument przekazuje API dwie daty. Rekordy między tymi dwiema datami są przetwarzane i oznaczane. Następnie dane są zwracane dzwoniącemu. Przypuszczam, że jeśli coś się wydarzy podczas przetwarzania lub po przetworzeniu przed dotarciem do klienta, to mam problem.
sig606
141

Myślę, że najłatwiej jest po prostu użyć AttributeRouting.

Jest to oczywiste w twoim kontrolerze, dlaczego chcesz to mieć w WebApiConfigpliku globalnym ?

Przykład:

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

Te {}nazwy muszą dopasować parametry.

To proste, teraz masz oddzielny, GETktóry obsługuje wiele parametrów w tym przypadku.

Mark Pieszak - Trilon.io
źródło
12
To jest świetne. Większość ludzi zaleca ustawienie trasy w WebApiConfigpliku, ale jest to rzeczywiście ładniejsze.
rhyek
4
Rzeczywiście, zalecamy (większość ludzi) posiadanie scentralizowanego obszaru zarządzania konfiguracją. W przypadku interfejsów API sieci Web (firmy Microsoft lub innych) kluczem są scentralizowane wzorce REST. Routing atrybutów jest ładny, ale sprawia, że ​​jednorazowe wyjątki są zbyt kuszące.
David Betz,
3
Zgoda, właściwie muszę zaktualizować swoją odpowiedź. Jest znacznie lepszy sposób wykonywania wielu parametrów za pomocą GET. Opublikowałem to, gdy byłem nowszy w WebAPI, teraz nie używam AttributeRouting (chyba że po prostu nie chcę tworzyć nowego kontrolera) i przekazuję wszystkie parametry w QueryString, mapują one automatycznie. Aktualizuję, kiedy mam szansę, żeby ludzie nie używali tej starszej metody
Mark Pieszak - Trilon.io
Czy istnieje sposób ustawienia Routeparametrów nazwanych (np. Parametrów zapytania)?
Shimmy Weitzhandler,
1
Jeśli nazwa metody akcji jest wymagana, można ją zmodyfikować, aby to uwzględnić. [Route ("api / YOURCONTROLLER / Get / {paramOne} / {paramTwo}")] public string Get (int paramOne, int paramTwo) {return "something"; }
Dash
49

Po prostu dodaj nową trasę do WebApiConfigwpisów.

Na przykład, aby zadzwonić:

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

Dodaj:

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

Następnie dodaj parametry do wywołania HTTP:

GET //<service address>/Api/Data/2/10 
Graham Wright
źródło
10
Wydaje się, że jest to jedyna odpowiedź, która zawiera listę wszystkich części. Chciałbym, żeby ktoś lepiej opisał, jak używać api/controller?start=date1&end=date2identyfikatora URI stylu.
Hot Licks
@Hot Licks Odpowiedź Andrew Verigi działa dobrze z argumentami ciągu zapytania. Zasadniczo należy powiązać nazwy ciągów zapytania z właściwościami klasy i przekazać je do metody. Twoja metoda pobierze pojedynczy argument klasy oznaczony atrybutem [FromUri] i będzie miał argumenty ciągu zapytania jako właściwości.
David Peterson,
Świetne rzeczy. Dzięki!
Hugo Nava Kopp
cześć @HotLicks i GrahamWright, czy myślisz, że możesz odpowiedzieć na to pytanie? Dzięki, stackoverflow.com/questions/57565318/ ...
45

Po prostu musiałem zaimplementować RESTfull API, w którym muszę przekazać parametry. Zrobiłem to, przekazując parametry w ciągu zapytania w tym samym stylu, co opisano w pierwszym przykładzie Marka „api / controller? Start = date1 & end = date2”

W kontrolerze użyłem wskazówki z podziału adresu URL w C #?

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

W moim przypadku dzwoniłem do WebApi przez Ajax, wyglądając tak:

$.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');
                       }
        }
   });

Mam nadzieję, że to pomoże...

Nigel Findlater
źródło
2
Wydaje mi się, że nie używasz WebApi, ponieważ ParameterBinding zmapuje twój querystring na parametry metody API automagicznie ...
emp
1
Tak, lepszym sposobem byłoby użycie atrybutu takiego jak [Route ("api / DbMetaData / {system} / {searchString}")] i dodanie tam parametrów do Get (system ciągów, string searchString), a następnie wywołanie z " ... api / DbMetaData / mysystem / mysearchstring "
Nigel Findlater
Użyłem jego przykładu w moim C # MVC WebApi i działało dobrze. +1 na przykład
Si8
38

Znalazłem doskonałe rozwiązanie na http://habrahabr.ru/post/164945/

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
źródło
5
Wskazówką tutaj jest [FromUri]
tranceporter
2
Chociaż artykuł jest po rosyjsku, @tranceporter ma rację. „FromUri” wygląda na świetny sposób na pobranie parametrów z adresu URL. Kolejny artykuł, który może być pomocny: asp.net/web-api/overview/formats-and-model-binding/ ...
Greg
To jest to, co robię już od jakiegoś czasu i działa świetnie! Poleciłbym również to rozwiązanie.
David Peterson
Jeśli wywołasz inną metodę pomocniczą (nie Get), czy nadal możesz jej używać [FromUri]? Nie wydaje mi się, żeby to działało.
wesoły
8

Używanie GET lub POST jest jasno wyjaśnione przez @LukLed . Jeśli chodzi o sposoby przekazywania parametrów, sugerowałbym skorzystanie z drugiego podejścia (też nie wiem zbyt wiele o ODATA ).

1.Serializowanie parametrów w jeden ciąg JSON i rozbieranie go w interfejsie API. http://forums.asp.net/t/1807316.aspx/1

To nie jest przyjazne dla użytkownika i przyjazne dla SEO

2. podaj parametry w ciągu zapytania. Jaki jest najlepszy sposób przekazania wielu parametrów zapytania do spokojnego interfejsu API?

Jest to zwykle preferowane podejście.

3. Definiowanie parametrów w trasie: api / controller / date1 / date2

To zdecydowanie nie jest dobre podejście. To sprawia, że ​​ktoś date2jest zasobem podrzędnym, date1a tak nie jest. Oba parametry zapytania date1i date2są parametrami zapytania i znajdują się na tym samym poziomie.

W prostym przypadku zasugerowałbym taki URI,

api/controller?start=date1&end=date2

Ale osobiście podoba mi się poniższy wzorzec URI, ale w tym przypadku musimy napisać niestandardowy kod, aby zmapować parametry.

api/controller/date1,date2
VJAI
źródło
Właściwie to były moje wyjaśnienia pochodzenia. Myślę, że LukLed wyróżnił moje tagi i link do adresu URL.
sig606
Jeśli chodzi o SEO, w tym przypadku nie ma to zastosowania. Ten kod będzie „serwer-serwer”, więc nie obchodzi mnie, czy świat zewnętrzny kiedykolwiek go odkryje. W rzeczywistości muszę upewnić się, że zostały podjęte odpowiednie kroki bezpieczeństwa, aby uniknąć przypadkowego dostępu. Musiałem zrobić serializację JSON dla innej części systemu (wydaje się, że jest to błąd podczas próby POST dużych list obiektów, więc musiałem serializować do łańcucha), więc w tym przypadku nie byłoby to zbyt trudne .
sig606
1
Mam nadzieję, że masz już odpowiedzi, więc dlaczego zadajesz pytanie?
VJAI
2
Przepraszam za tak późną odpowiedź, Mark. Wypróbowałem kilka rozwiązań, ale nie byłem pewien, które z nich jest najlepsze i starałem się trzymać podejście zgodne ze standardami branżowymi, więc opublikowałem tutaj, w SO.
sig606
1
@Mark Coś jak do następnego: 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 ";
    }

Oba parametry linku głównego ({jeden}, {dwa}) i parametry funkcji Get (jeden, dwa) powinny być takie same

ashwath hegde
źródło
2

Wiem, że to jest naprawdę stare, ale ostatnio chciałem tego samego i oto co znalazłem ...

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

Więc teraz w twoim adresie / URI / ...

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

Wynik: „Znaleziono test”


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

Wynik: „Nie udało się znaleźć tego testu”

Rick Riggs
źródło
Osobiście podoba mi się ten styl w C #, ponieważ mogę zmienić sygnaturę oryginalnej metody i przeciążać dokładnie to, co próbuję osiągnąć, bez modyfikowania konfiguracji routingu. Mam nadzieję, że pomoże to innym, którzy są przyzwyczajeni do tego (być może przestarzałego) podejścia do składania żądania GET.
Rick Riggs
1
Musiałem utworzyć interfejs API wydarzeń, który jest używany przez aplikację kalendarza innej firmy, która używa tego podejścia. Cieszę się, że znalazłem tę odpowiedź!
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
źródło