25.03.20204 min

Bishoy Labibechnical Architect

Łatwiejsza praca z HTTP w .NET Core

Poznaj 3 rozwiązania ułatwiające pracę z HTTP w aplikacjach .NET Core, czyli, logowanie błędów bad request, logowanie żądań i odpowiedzi klienta HTTP oraz propagacja nagłówków między mirkoserwisami.

Łatwiejsza praca z HTTP w .NET Core

W tym artykule skupię się na kilku rozwiązaniach związanych z HTTP, które ułatwiają pracę z API oraz mikroserwisami. Stworzyłem też pakiet NuGet o nazwie CoreX.Extensions.Http, który zawiera w sobie te wszystkie rozwiązania. Możesz go dodać do swojego projektu oraz skonfigurować w pliku startup.cs, aby korzystać z tych rozwiązań. 


Lepsze logowanie dla bad requests (400)

Zacznijmy od najprostszej rzeczy. Zdarzyło się pewnie wiele razy, że ktoś wywołuje Twoje API i robi coś niepoprawnie, na przykład, zapomina o polu lub, nieświadomy tego, że Twoje API było aktualizowane, zapomina o wysłaniu nowego parametru.

Ten ktoś dostaje wtedy błąd, ale nie wie, dlaczego i prosi o pomoc. Pierwszą rzeczą, którą robisz, jest sprawdzenie logów API: 

Domyślne logi .NET Core dla bad request 400

Jedyną wskazówką, że coś poszło nie tak, jest ostatnia linijka (jako informacja):

Request finished in … 400 application/json; charset=utf-8


Jedynie 400 status code wskazuje na to, że coś poszło nie tak, ale wywołujący otrzymał też przydatną odpowiedź JSON-ie:

Odpowiedź JSON w związku z 400 bad request

Dlaczego więc właściciel API nie otrzymał jasnego logu tego błędu? Myślę, że dotnet powinien domyślnie zarejestrować ten błąd oraz szczegóły żądania, aby właściciel mógł powiedzieć wywołującemu, co poszło nie tak i, jak to naprawić.

Stworzyłem metodę rozszerzającą, która to zrobi. Należy ją dodać do startup.cs w metodzie ConfigureServices:

// Enable the logging for common 400 bad request errors
services.EnableLoggingForBadRequests();

Oto dodatkowy warning w logach:

Log 400 bad request po umożliwieniu lepszego logowania

Proste, prawda?


Automatyczne logowanie dla HTTPClient

Twoja aplikacja wywołuje teraz inne API. Musimy śledzić te wywołania w logach, żeby mieć świadomość tego, co wysyłamy i jaką odpowiedź otrzymujemy. Wydaje mi się, że to podstawa. Można sobie z tym łatwo poradzić, tak jak w tym wątku na StackOverflow. Rozwiązanie to jednak działa tylko dla jednego klienta HTTP, a ja chciałem, żeby tak zachowywały się wszystkie utworzone klienty HTTP. 

Jednym ze wspaniałych dodatków do dotnet core 2.1 był IHttpClientFactory, który jest interfejsem do tworzenia HttpClient. Ogólnie nie powinno się w ogóle używać HttpClient();.

Jeśli więc wszyscy klienci HTTP zostaną utworzeni przy użyciu IHttpClientFactory, istnieje sposób, aby sprecyzować, w jaki sposób Ci klienci mają być tworzeni, bez względu na to, ile razy robimy wywołania.

Problem polega na tym, że stworzenie takiego klienta przyszło mi naprawdę łatwo, ale trudno było znaleźć sposób na dopasowanie naszej polityki tworzenia klientów do wszystkich nazwanych lub nienazwanych klientów HTTP.

Dlatego dodałem taki rodzaj logowania do wszystkich klientów HTTP przez zarejestrowanie tego w metodzie ConfigureServices w startup.cs:

// Register HttpClientFactory
services.AddHttpClient();
// Register Logging for HttpClient
services.AddHttpClientLogging(Configuration);


Możesz także skonfigurować, to co ma być logowane, a także zalogować nagłówki, treść żądania i odpowiedzi czy też format logów (HTML/plaintext). Konfiguracja jest możliwa przez dodanie tej sekcji w appsettings.json:

"HttpClientLogging": {
  "Enabled": true,
  "Html": true,
  "Headers": true,
  "Body":  false
},

W zamian dostajesz fajne logi:

Logowanie żądania i odpowiedzi klienta HTTP


Propagacja nagłówków

Innym bardzo częstym problemem jest to, że użytkownik wysyła jedno żądanie, które powoduje wiele wywołań API za pośrednictwem różnych mikroserwisów. Zwykle chcemy śledzić to w logach jako część pojedynczego żądania. Musimy więc wiedzieć, jakie inne wywołania i zależności zostały wykonane na podstawie początkowego żądania tego użytkownika, bez względu na ich liczbę. Robimy to, tworząc correlation ID (losowy unikalny identyfikator) dla pierwszego żądania i za każdym razem, gdy wywołujemy HTTP do innych API, umieszczamy ten sam correlation ID jako nagłówek we wszystkich żądaniach. W taki sposób ten sam identyfikator będzie propagowany we wszystkich kolejnych żądaniach i ich logach.

Kolejnym częstym scenariuszem jest to, że gdy użytkownik wysyła wywołanie API ze swoim tokenem w nagłówku Authorization, to za każdym razem, gdy wykonujemy inne wywołania API do wielu usług, to powinniśmy używać tego samego nagłówka, aby wszystkie usługi wiedziały, kto wykonał dane połączenie. To również odbywa się poprzez propagowanie nagłówka autoryzacji.

Problem polega na tym, że istnieje kilka nagłówków (które mogą różnić się w zależności od aplikacji), które musimy propagować podczas wykonywania jakichkolwiek wywołań HTTP. Rozwiązaniem jest tutaj używanie IHttpClientFactory do automatycznego dodawania tych nagłówków do każdego nowego klienta.

Użyłem tutaj rozwiązania Davida Fowlera. Jego implementację umieściłem w metodzie rozszerzającej, żeby było mi łatwiej. Wykorzystałem pakiet NuGet o nazwie CorrelationId, aby dodać specjalną obsługę generowania nowego ID, jeśli nie został wysłany.

Dodajemy to w startup.cs metody ConfigureServices:

// Register global header propagation for any HttpClient that comes from HttpClientFactory
services.AddHeaderPropagation(options => { options.HeaderNames.Add("X-My-Header"); });


Domyślne nagłówki są już zawarte w x-correlation-id. Pełną listę można sprawdzić tutaj.


Podsumowanie

Staraliśmy się tutaj znaleźć rozwiązania czasochłonnych problemów, z którymi często spotykamy się podczas pracy z HTTP. Są to rzeczy, które powinny być domyślnie włączone, żeby programiści nie musieli się nimi przejmować podczas budowania mikroserwisów z .NET.

A czy Wy natknęliście się na podobne problemy?

<p>Loading...</p>

Powiązane artykuły

Dziel się wiedzą ze 160 tysiącami naszych czytelników

Zostań autorem Readme

Ubezpieczeniowy Fundusz Gwarancyjny

Specjalista ds. testów

medium

Znamy widełki

Kontrakt B2BUmowa o pracę

Warszawa

Ważna do 27.02.2022

Dobrze
JIRASoapUIOracle Database
Początkujący
FitNesseJMeter

Sii Polska

DevOps Engineer

medium

17 000 - 24 000 PLN

Kontrakt B2B

Warszawa

Ważna do 27.02.2022

Dobrze
AWSGCPGitHub
Bardzo dobrze
Linux

Accenture Polska

Agile Project Manager

medium

Brak widełek

Kontrakt B2BUmowa o pracę

Warszawa

Praca zdalna 100%

Ważna do 27.02.2022

Bardzo dobrze
Agile Methodologies (Scrum, Kanban, Kaizen)
Dobrze
Team-building & people management
Początkujący
cloud fundamentals

DB Schenker Technology Center Warsaw

Business Analyst

medium

Brak widełek

Kontrakt B2BUmowa o pracę

Warszawa

Ważna do 27.02.2022

Accenture Polska

PHP/Magento Developer

medium

Brak widełek

Kontrakt B2BUmowa o pracę

Praca zdalna 100%

Ważna do 27.02.2022

Bardzo dobrze
PHPGIT
Dobrze
Magento 2SOAP/REST HTML/CSS
Początkujący
JSONJavaScript jQuery

T-Mobile Polska S. A.

Inżynier Sieciowy

medium

Brak widełek

Kontrakt B2B

Warszawa

Ważna do 27.02.2022

Asseco Poland S.A.

Młodszy Analityk Finansowy

junior

Brak widełek

Umowa o pracę

Rzeszów

Ważna do 27.02.2022

Netguru

Junior iOS Developer

junior

4 200 - 6 000 PLN

Kontrakt B2BUmowa o pracę

Praca zdalna 100%

Ważna do 27.02.2022

Dobrze
iOS

7N

Data Engineer

medium

15 100 - 18 100 PLN

Kontrakt B2B

Praca zdalna 100%

Ważna do 27.02.2022

Dobrze
MS SQLETLSpark

7N

Senior Business Analyst

senior

16 800 - 21 800 PLN

Kontrakt B2B

Praca zdalna 100%

Ważna do 27.02.2022