Flurl – łatwiejsze budowanie adresów url

Wprowadzenie

W dobie mikroserwisów, nanoserwisów, czy serwerlessów kwestią czasu jest konieczność budowania adresów url do innych zasobów w sieci. Można się męczyć i robić to ręcznie albo też skorzystać z gotowej biblioteki, jaką jest tytułowy Flurl.

W tym wpisie dowiesz się, czym jest Flurl, jak z niego skorzystać, jakie daje możliwości oraz gdzie mogą wystąpić problemy.

Flurl

Flurl (https://flurl.dev/) z założenia ma pomóc nam w budowaniu adresów url (fluent api) oraz wykonywaniu żądań pod tak zbudowany adres. A to wszystko dodatkowo z fajnym wsparcie dla testów.

Budowanie adresów url na początku wydaje się dość proste, ale gdy dojdziemy do jakichś bardziej rozbudowanych scenariuszy, to pojawiają się problemy. Na przykład w przypadku query stringa musimy sprawdzić, czy już jakiś parametr został dodany – jeśli nie, to wstawić na początku znak zapytania (?), a jak już jakiś jest, to dopisać znak and (&). Do tego dochodzi jeszcze kodowanie wartości, gdy adres zawiera specyficzne znaki.

Z drugiej strony szkoda tracić na to czas, gdy mamy gotową bibliotekę, która nam to wszystko ułatwi.

Darmowy kurs Visual Studio

Pracując z setkami programistów, zauważyłem, że większość osób nie pracuje efektywnie w Visual Studio. W skrajnych przypadkach korzystali z kopiowania z wykorzystaniem menu Edit. Wiem, że to dziwne, ale naprawdę niektórzy tak pracują. Dlatego postanowiłem stworzyć kurs Visual Studio – aby pomóc koleżankom i kolegom w efektywniejszej pracy.

Przygotowałem 30 lekcji e-mail, w których pokażę Ci, w jaki sposób pracować efektywniej i szybciej w Visual Studio. Poznasz dodatki, bez których nie wyobrażam sobie pracy w tym IDE.

Po więcej informacji zapraszam na dedykowaną stronę kursu: Darmowy Kurs Visual Studio.

Quiz C#

Ostatnio przygotowałem również quiz C#, w którym możesz sprawdzić swoją wiedzę. Podejmiesz wyzwanie?

Budowanie adresów

Myślę, że działanie biblioteki najlepiej zobaczyć w praktyce. Załóżmy, że chcemy zbudować taki oto adres url:

https://flurltestservice.azurewebsites.net/v2/users?AccessToken=token&ResultsCounts=20&q=Tomasz%20%C5%BByrek#Results

Kod tworzący adres wygląda tak:

var url = "https://flurltestservice.azurewebsites.net/"
.AppendPathSegment("v2")
.AppendPathSegment("users")
.SetQueryParams(new
{
AccessToken = "token",
ResultsCounts = 20,
q = "Tomasz Żyrek"
})
.SetFragment("Results");
view raw BuildUrl.cs hosted with ❤ by GitHub

Budowanie zaczynamy od bazowego adresu (na przykład sama domena), który jest przechowywany w normalnym stringu. W realnej aplikacji adres ten odczytalibyśmy z ustawień zapisanych w pliku konfiguracyjnym lub bazie danych. Co fajne, nie musimy tutaj tworzyć jakichś dodatkowych obiektów do budowania adresu.

Kolejne elementy adresu dodajemy za pomocą szeregu extension method (fluent api). Metoda AppendPathSegment służy do dodawania segmentów w adresie, czyli wartości znajdujących się między znakiem ukośnika (/). W przykładzie dodaję dwa segmenty: v2 określa drugą wersję aplikacji, natomiast users konkretny widok.

Metoda SetQueryParams służy do budowania query stringa. Przekazujemy do niej obiekt (w tym przypadku anonimowy) i ona na podstawie właściwości obiektu zbuduje odpowiedni query string.

Ostatnia metoda SetFragment umożliwia nam określenie wartości, która będzie znajdować się po znaku hash (#) w adresie url.

Co ważne, metody automatycznie enkodują przekazane wartości i nie musimy sami się tym zajmować. Widać to na przykładzie parametru q w query stringu, gdzie wartość „Tomasz Żyrek” zostanie odpowiednio zamieniona na „Tomasz%20%C5%BByrek”. Czyli spacja oraz litera Ż zostaną odpowiednio enkodowane.

Poniżej znajduje się osadzony przykład (z portalu dotnetfiddle.net) działania powyższego kodu. Możesz sam/sama się pobawić działaniem biblioteki bez konieczności odpalania Visual Studio:

Prawda, że budowanie adresów jest całkiem proste?

Wykonywanie żądań

Poza samym budowaniem adresu w Flurl możemy jeszcze wykonywać żądania pod tak zbudowane adresy (również w wykorzystaniem fluent api).

Na potrzeby tego wpisu przygotowałem prostą aplikację, która jest hostowana w Azure App Service i zwraca informacje o adresie url. Posłuży nam ona do testów.

Aby wykonywać żądania musimy zainstalować drugi pakiet z nugeta Flurl.Http – (https://www.nuget.org/packages/Flurl.Http/). Pakiet ten dostarcza zestaw kolejnych extensions method, które możemy wykorzystać.

Rozszerzmy nasz wcześniejszy przykład o wykonanie żądania typu get pod wcześniej zbudowany adres:

var result = await "https://flurltestservice.azurewebsites.net/"
.AppendPathSegment("v2")
.AppendPathSegment("users")
.SetQueryParams(new
{
AccessToken = "token",
ResultsCounts = 20,
q = "Tomasz Żyrek"
})
.SetFragment("Results")
.GetAsync()
.ReceiveString();
view raw GetData.cs hosted with ❤ by GitHub

Początek kodu jest taki sam jak wcześniej. Na końcu dodałem dwie nowe metody. Pierwsza GetAsync powoduje wykonanie żądania typu get. Natomiast ostatnia zwraca odpowiedź z serwera w formie stringa. Obie metody są asynchroniczne.

Najlepiej zobaczyć działanie tego fragmentu kodu w praktyce:

Biblioteka oczywiście udostępnia szereg innych metod. Możemy wykonać inne typy żądań, parsować odpowiedz z jsona na obiekt, dodawać nagłówki, pliki cookie i tak dalej. Najlepiej przejrzeć stronę dokumentacji poświęconą Flurl.Http – https://flurl.dev/docs/fluent-http/

Testowanie

Kolejną fajną funkcjonalnością biblioteki jest możliwość łatwego testowania wysyłanych żądań. Możemy po pierwsze zamockować odpowiedź z serwera, a po drugie zweryfikować, czy nasz kod wykonał odpowiednie żądanie.

Żądanie z poprzedniego przykładu opakowałem w metodę i dodałem dla niej prosty test, pokazujący, jak testować Flurl:

public static async Task TestGetData()
{
using (var httpTest = new HttpTest())
{
httpTest.RespondWith("json result", 200);
var result = await GetData();
httpTest.ShouldHaveCalled("https://flurltestservice.azurewebsites.net/v2/users?AccessToken=token&ResultsCounts=20&q=Tomasz%20%C5%BByrek#Results")
.WithVerb(HttpMethod.Get)
.Times(1);
result
.Should()
.Be("json result");
}
}
private static async Task<string> GetData()
{
var result = await "https://flurltestservice.azurewebsites.net/"
.AppendPathSegment("v2")
.AppendPathSegment("users")
.SetQueryParams(new
{
AccessToken = "token",
ResultsCounts = 20,
q = "Tomasz Żyrek"
})
.SetFragment("Results")
.GetAsync()
.ReceiveString();
return result;
}
view raw TestGetData.cs hosted with ❤ by GitHub

Tworzymy obiekt HttpTest, który następnie konfigurujemy z wykorzystaniem metody RespondWith. Określam tutaj treść odpowiedzi oraz jej status.

Następnie wykonuje metodę GetData, która zwraca odpowiedź z serwera. Później mogę wykonać asserty na obiekcie HttpTest.

Metoda ShouldHaveCalled weryfikuje, czy było wysłane żądanie pod wskazany adres. WithVerb sprawdza tym żądania (tutaj get). Natomiast metoda Times służy do sprawdzenia ilości wykonanych żądań z daną konfiguracją.

Na końcu znajduje się prosty test sprawdzający wynik metody GetData. Tutaj wykorzystałem Fluent Assertions.

Działanie testu można sprawdzić poniżej w dotnetfiddle. Nie jest to prawdziwy test. W momencie, gdy on nie przejdzie, zostanie wyrzucony wyjątek, który w normalnej sytuacji obsłuży biblioteka do testów.

Problem z testowaniem

Powyższy test ma jeden dość istotny problem. Jest nim sposób działania metody ShouldHaveCalled. Niestety w aktualnej wersji biblioteki Flurl, nie sprawdza ona, czy rzeczywisty adres pod jaki zostało wysłane żądanie, jest taki sam, jak adres przekazany do metody. Metoda sprawdza, czy początek rzeczywistego adresu jest taki jak przekazana wartość.

W efekcie test przejdzie nam również w sytuacji, gdy na końcu faktycznego adresu są jakieś dodatkowe dane. Na przykład kolejny parametr w query stringu. Co nie zawsze może być dobrą sytuacją i może powodować błąd w działaniu aplikacji.

Możesz sprawdzić to zachowanie poprzez skrócenie adresu (np. usunięcie całego query stringu) z ostatniego przykładu. Wtedy test dalej będzie przechodzić.

Aby rozwiązać ten problem, możemy dodać własną metodę, które będzie weryfikować, czy rzeczywisty adres jest identyczny jak przekazana wartość. Taka metoda może wyglądać tak:

public static class HttpCallAssertionExtensions
{
public static HttpCallAssertion ShouldHaveExactCall(this HttpTest test, string exactUrl)
{
test.CallLog.First().FlurlRequest.Url.ToString().Should().Be(exactUrl);
return new HttpCallAssertion(test.CallLog);
}
}

Z obiektu HttpTest wyciągamy pierwsze wywołanie, a następnie za pomocą Fluent Assertions sprawdzamy, czy adres jest taki sam jak przekazana wartość. Metoda nie jest idealna, ale w większości przypadków dobrze się sprawdza.

Poniżej zmieniony przykład z wykorzystanie nowej metody ShouldHaveExactCall:

Teraz ładnie widać, że jest wyjątek, które zostanie obsłużony przez bibliotekę do testów.

Przykład

Tradycyjnie cały przykład do tego wpisu znajdziesz na githubie: https://github.com/danielplawgo/FlurlTests

Dodatkowo od tego wpisu staram się na blogu osadzać fragmenty kodu z dotnetfiddle.net. Zachęcam do zabawy kodem z poziomu samego wpisu.

Podsumowanie

Flurl jest bardzo fajną i prostą biblioteką, którą myślę, że warto znać i używać, gdy przyjdzie taka potrzeba. Świetnie sprawdza się w projektach, mimo tego drobnego problemu z testami.

Gorąco polecam!

Szkolenie C# i .NET 5

Szkolenie C# i .NET 5

Zainteresował Ciebie ten temat? A może chcesz więcej? Jak tak to zapraszam na moje autorskie szkolenie o C# oraz .NET.

1 thought on “Flurl – łatwiejsze budowanie adresów url

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany.