NET 6 WebAPI - Lista w FromQuery nie jest deserializowana

0

Witam.
Mam klasy, które ogarniają mi filtrowanie danych po stronie mobilki

public class QueryParameters
{
    [JsonProperty(PropertyName = "startDate")]
    public DateTime? StartDate { get; set; }

    [JsonProperty(PropertyName = "endDate")]
    public DateTime? EndDate { get; set; }

    [JsonProperty(PropertyName = "group")]
    public string? Group { get; set; }

    [JsonProperty(PropertyName = "filters")]
    public List<FilterItem>? Filters { get; set; }

    [JsonProperty(PropertyName = "orderBy")]
    public string? OrderBy { get; set; }

    [JsonProperty(PropertyName = "search")]
    public string? Search { get; set; }

    [JsonProperty(PropertyName = "page")]
    public int Page { get; set; }

    [JsonProperty(PropertyName = "limit")]
    public int Limit { get; set; }
}

public class FilterItem
{
    [JsonProperty(PropertyName = "key")]
    public string Key { get; set; } = string.Empty;
    [JsonProperty(PropertyName = "value")]
    public string Value { get; set; } = string.Empty;
}

Z mobilki do API przychodzi to w formie

{group: , search: , filters: [{key: bufor, value: bufor = 1}], startDate: 2021-07-01T00:00:00.000, endDate: 2021-07-31T00:00:00.000, orderBy: , page: 0, limit: 20}

Problem w tym, że API nie radzi sobie z List<FilterItem> - pokazuje w debuggerze Count = 0. Czy taki format jest w ogóle wspierany przez NET 6 WebAPI? Wcześniej filters było List<string> (to działało) ale byłem zmuszony zmienić w celu identyfikacji konkretnego filtra po stronie mobilki.

CONTROLLER

[HttpGet("type/{type}")]
public async Task<IActionResult> Get([FromQuery] QueryParameters parameters, [FromRoute] int type)
{
    try
    {
        return Ok(await _reader.GetDocuments(parameters, type));
    }
    catch (Exception ex)
    {
        return BadRequest(ex.Message);
    }
}
2

dlaczego taki złożony typ wysyłasz jako QueryParameters (w url)? Nie lepiej jest to wysłać w body, po zmianie metody na POST?
Jak wygląda QueryString który wysyłasz z mobilki do API?

0
  1. POST nie służy do pobierania danych. Ja potrzebuje zwrócić z API pofiltrowane dane.
  2. Zrobiłem taki złożony typ, ponieważ w wartości przekazuje "sql" np: anulowane = 1 lub bufor = 1. Ciężko później wybrane (zapisane) załadować na formę w mobilce.
  3. QueryString = api/documents/type/302?group=&search=&filters[0][key]=bufor&filters[0][value]=bufor = 1&startDate&endDate&orderBy=&page=0&limit=20
0

A czy spacje w query params nie muszą być odpowiednio encodowane?

2
AdamWox napisał(a):
  1. POST nie służy do pobierania danych. Ja potrzebuje zwrócić z API pofiltrowane dane.
  2. Zrobiłem taki złożony typ, ponieważ w wartości przekazuje "sql" np: anulowane = 1 lub bufor = 1. Ciężko później wybrane (zapisane) załadować na formę w mobilce.
  3. QueryString = api/documents/type/302?group=&search=&filters[0][key]=bufor&filters[0][value]=bufor = 1&startDate&endDate&orderBy=&page=0&limit=20
  1. Bierzesz to zbyt dosłownie. To nie znaczy, że nie możesz go użyć. Właśnie w tego typu filtrowaniach używam POST. Łatwiej jest zrobić body w postaci obiektu niż kleic w URL takiego potworka.
    Dodatkowo weż pod uwagę, że get ma ograniczoną ilość znaków, do 2000 chyba
0

Użyć posta, albo napisać customowego bindera, co będzie potrafił zmapować querystringa na twój oczekiwany obiekt

1

@AdamWox: skoro tak bardzo się upierasz przy nie używaniu POSTa do odczytu danych to zrób to ładnie, bo ładowanie wszystkiego w query string to ohyda.

A:

  1. POST tworzący nowy obiekt w /query -> zwracasz 201, z id np. 105
  2. GET pobierający wynik zapytania pod /query/105
    Możesz przesłać POSTem co chcesz, użytkownik nie widzi tego całego syfu, pobierasz dane dopiero GETem, jesteś zgodny ze wszystkim. Jedyny problem to nie dodasz takiego linka do zakładki, ale wtedy jak chcesz już być super RESTowcem to pozwolisz użytkownikowi zapisać filtr i robić geta z id filtru :)

B:
Możesz sobie to samemu sparsować bezpośrednio z Request.QueryString

C:
Jeżeli chcesz zachować MODEL a zmienić QUERY STRING to użyj poprawnego zapisu: group=asd&filters[0].key=a

D:
Jeżeli chcesz zachować QUERY STRING a zmienić model, to użyj poprawnego modelu: public IEnumerable<Dictionary<string,string>> Filters { get; set; } Bo tak by wyglądała definicja czegoś co wywołuje się: filters[0][key] i zwraca string.

Osobiście sugeruje A.

0

Ja się nie upieram przy niczym. Widzę zwyczajnie, że zdania są podzielone i to bardzo. Jakiś czas temu był podobny post na tym forum, gdzie 2-3 osoby mocno zniechęcały do używania POST w celu "pobierania" danych z API i w jakimś stopniu miało to sens. Zmiana requesta na POST jest zdecydowanie najprostszym i najszybszym wyjściem, które na 100% zadziała. Ciekawi mnie tylko, czy to się kiedyś gdzieś nie odbije przeciwko mnie w dalszej części projektu.

0

Myślę że prędzej czy później i tak by droga GET i Query params by była bardziej wyboista. Załóżmy, że dojdzie kolejny propertis do filtrowania danych, wtedy Query params znów by się skomplikował.

0

Zmiana requesta na POST jest zdecydowanie najprostszym i najszybszym wyjściem, które na 100% zadziała. Ciekawi mnie tylko, czy to się kiedyś gdzieś nie odbije przeciwko mnie w dalszej części projektu.

@AdamWox: w wielu API działa to właśnie w ten sposób. API, które jest full REST (przykład A) to czasami sztuka dla sztuki. Jeżeli nie wiesz jak coś zrobić to popatrz jak inne firmy to robią.

JIRA pozwala wyszukiwać przez API na oba sposoby: POST /rest/api/3/search oraz GET /rest/api/3/search. Skoro JIRA się nie boi mieć wyszukiwania po POSTcie, Ty też nie musisz :)

Jeżeli chodzi o ugryzienie w dupę to bardziej bym się obawiał kombinowania z GETem i QueryStringiem niż użyciem POSTa.

Przy używaniu POSTa jest tylko ten jeden problem o którym wspomniałem: dodanie linku z gotowymi filtrami do zakładek w przeglądarce jest niemożliwe. Jeżeli jest to wymagane przez klienta to trzeba zbudować w około tego problemu rozwiązanie np. zapisywanie zestawów filtrów i osoby GET z samym id zapisanego zestawu filtrów. Jeżeli chcemy aby taki link mógł być współdzielony przez użytkowników to znowu dochodzi ustawianie publicznych i prywatnych zestawów. Ale to już rozwiązywanie problemu biznesowego. Technologicznie search w POST nie rozwali projektu.

0
AdamWox napisał(a):

Jakiś czas temu był podobny post na tym forum, gdzie 2-3 osoby mocno zniechęcały do używania POST w celu "pobierania" danych z API i w jakimś stopniu miało to sens. Z

Jest różnica między pobraniem danych z konkretnego zasobu (czyli elementu w jakiejś hierarchii), do czego użycie GETa jest oczywiste, a tworzeniem wyszukiwarki.
Niemniej jednak przy tak banalnym wyszukiwaniu jak to, ja bym został przy GET. Trzeba po prostu query stringa odpowiednio zakodować.

0

wg. mnie POST nadaje sie do wyszukiwania, bo samo POST jest przeznaczone do tworzenia jakiegos zasobu - a my tu tworzymy wyszukiwanie ;)
ale to do jakis rzeczywiscie bardziej skomplikowanych wyszukan, gdzie sa zagniezdzone obiekty, itd

1 użytkowników online, w tym zalogowanych: 0, gości: 1