Allegro Client -> New Features
Pomoc w kwestii uporania się w powyższym kodzie z poprawnym budowaniem url’a (dodanie jako jej parametru wartości z parametru nraukcji przekazywanego do tej metody)
Nie do końca rozumiem w czym Pan tu potrzebuje pomocy. Primo, jeśli w zapytaniu nie potrzbujemy żadnych Query Params to polecałbym używać po prostu obiektu klasy Uri zamiast UriBuilder. Zamiast bawić się w nieczytelną konkatenacje stringów polecam skorzystać z magii jaką jest String Interpolation
Dodatkowo zrobiłem mały refactoring, w wielu miejscach powtarzany był kod mający na celu wyczyszczenie header'ów i dodanie header'a autoryzacyjnego oraz Accept. Przeniosłem to do metody SetDefaultHeaders(). Również powtarzany był kod odpowiedzialny za wysłanie requesta typu GET i sparsowanie wyników do JSON'a, teraz znajduję się on w funkcji GetRequestAndParseAsync().
Jeśli zamierzamy często operować na danych otrzymanych w responsach, warto zastanowić nad zmapowaniem ich do odpowiedniej klasy. Dla przykładu napisałem klase Offer, która opowiada za dane na temat oferty w allegro. W nagłówku takiej klasy w której będziemy mapowali otrzymanego jsona musimy dodać dwa nagłówki
[DataContract]
[Serializable]
Klasa ta musi mieć prywatny pusty konstruktor. Teraz możemy zdefiniować jakie pola chcemy zmapować poprzez dodawanie właściwości z nagłowkiem:
[DataMember(Name="nazwa_pola_w_json")]
public type chosen_name {get; set;}
Dla klasy Offer zmapowałem wszystkie nagłówki, poniewaz przy edycji musimy wysłać wszystkie pola. Jednak przy innych klasa nie musi być to konieczne. Możemy napotkać następujący problem niektóre właściwości w otrzymanym jsonie są zagnieżdzone. Dla takich pól mamy dwa wyjścia.
Możemy użyć typu dynamic, który dynamicznie dostosuje typ do otrzymanych danych. Wtedy jednak dla tej właściwości struktura nie będzie przejrzysta i wygodna do użycia
Przykład
JSON odpowiedzialny za descrition
"description": {
"sections": [
{
"items": [
{
"type": "TEXT",
"content": "<p>Nowy XIAOMI REDMI NOTE 5</p><p>Kod PLU: __4949__</p>"
}
]
}
]
}
[DataMember(Name = "description")]
private dynamic _description { get; set; }
[IgnoreDataMember]
public string Description => _description["sections"][0]["items"][0]["content"];
// use access, assume that offer is of type Offer
Console.WriteLine(offer.Description); // Print out text description of Offer
Moim celem było uzyskanie łatwego dostępu do opisu aukcji, a więc zmapowałem Description za pomocą dynamica i ukryłem to w prywatnej właściwości _description. Nastepnie dodałem pole Description z nagłówkiem IgnoreDataMember, ponieważ tego pola nie pobieram z jsona, tylko jest to nie jaki helper.
Zamiast używać dynamica mozemy zmapować całego jsona poprzez tworzenie klas, które odpowiadają za kolejne zagnieżdzenia.
Przykład
Otrzymany JSON
"sellingMode": {
"format": "BUY_NOW",
"price": {
"amount": "500.12",
"currency": "PLN"
},
"startingPrice": null,
"minimalPrice": null
},
Widać, że mamy tu zagnieżdzony klucz price.
startingPrice i minimalPrice również są zagnieżdzone, ale dla nich użyjemy typu dynamic, bo w tej chwili nie są nam potrzebne.
Pola w klasie SellingMode
[DataMember(Name = "format")]
public string Format { get; set; }
[DataMember(Name = "price")]
public Price Price { get; set; }
[DataMember(Name="startingPrice")]
public dynamic StartinPrice { get; set; }
[DataMember(Name="minimalPrice")]
public dynamic MinimalPrice { get; set; }
Tak więc, oprócz klasy SellingMode utworzyliśmy również klasę Price, w której mamy następujące pola:
[DataMember(Name = "amount")]
public string Amount { get; set; }
[DataMember(Name = "currency")]
public string Currency { get; set; }
[IgnoreDataMember]
public decimal Value => decimal.Parse(Amount);
Teraz gdy chcemy uzyskać dostęp do np. ceny:
offer.SellingMode.Price.Amount
Pomonicza funkcja do pobranie wszystkich detali na temat podanego numeru aukcji
private async Task<Offer> GetOffer(string nrAukcji)
{
// Prezetacja String Interpolation
UriBuilder builder = new UriBuilder($"{API_LINK}sale/offers/{nrAukcji}");
// Refactoring
SetDefaultHeaders(); // Ustawienie headerów
var json = await GetRequestAndParseAsync(builder.Uri); // Zwraca sparsowany wynik requesta
return json.ToObject<Offer>(); // Zmapownie sparsowanego jsona do klasy Offer
}
Funkcja odpowiadająca za edycję oferty
public async Task EditOffer(string noAuction, string price, string currency, int available, string unit)
{
Offer editedOffer = await GetOffer(noAuction); // otrzymanie detali na temat edytowanej aukcji
// edycja zmapowanego obiektu
editedOffer.SellingMode.Price.Amount = price; // cena w rest api podana jest jako string
editedOffer.SellingMode.Price.Currency = currency;
editedOffer.Stock.Available = available;
editedOffer.Stock.Unit = unit;
Uri uri = new Uri(API_LINK, $"/sale/offers/{noAuction}");
SetDefaultHeaders();
// Json przesyłany jako Payload/Body musimy użyć StringContent, edytowany obiekt serializujemy z powrotem do jsona (dla tego musielismy zmapować wszystkie pola)
var jsonContent = new StringContent(Utility.Serialize(editedOffer), Encoding.UTF8, "application/vnd.allegro.public.v1+json");
var response = await _client.PutAsync(uri, jsonContent);
var contents = await response.Content.ReadAsStringAsync();
Console.WriteLine("Results");
Console.WriteLine(JObject.Parse(contents));
}