Wprowadzenie do ASP.NET Core Web API i architektury REST

Skrót API (Application Programming Interface) oznacza interfejs programowania aplikacji. Jest to zbiór reguł ściśle opisujący sposób komunikacji między programami. ASP.NET Core Web API to platforma do tworzenia aplikacji internetowych wykorzystujących do komunikacji protokół HTTP. HTTP (Hypertext Transfer Protocol) to protokół określający reguły przesyłania zasobów i zasady komunikacji pomiędzy serwerem, a klientem. ASP.NET Core Web API umożliwia tworzenie interfejsów API opartych właśnie na protokole HTTP. Oznacza to, że aplikacja typu Web API, może docelowo zostać skonsumowana przez aplikację mobilną, aplikację webową, aplikację destopową, SPA lub IoT, czy też – szerzej – dowolną aplikację, której komunikacja odbywa się za pomocą protokołu HTTP. Interfejsy API tworzy się zgodnie z architekturą REST (Representational State Transfer), która określa zbiór zasad i dobrych praktyk dotyczących projektowania API. Dużo trudnych pojęć, które postaram się lepiej wytłumaczyć w dalszej części artykułu.

Na początek najlepiej zacząć od definicji interfejsu. Interfejs jest to rodzaj połączenie umożliwiającego komunikację. Wyróżniamy trzy rodzaje interfejsów: interfejs użytkownika (User Interface), interfejs programistyczny (Application Programming Interface) oraz fizyczny interfejs w postaci układu umożliwiającego połączenie np. urządzeń. Przykładem interfejsu użytkownika jest np. klawiatura komputera, ekran smartfona, czy interfejs graficzny aplikacji uruchamianej na komputerze. Przykładem fizycznego interfejsu może być złącze USB. Pozostała kwestia interfejsu programistycznego API. API to prostu sposób komunikacji pomiędzy różnymi elementami oprogramowania . Web API jest to zestaw odpowiednio przygotowanych metod zwykle dostępnych w postaci adresów URI (endpointów).

Przykładowy endpoint i odpowiedź

W powyższym przykładzie zostało wysłane żądanie typu GET na adres localhost:5000/api/Products. W odpowiedzi otrzymano tablicę obiektów zawierającą listę produktów. W tym konkretnym przypadku są to trzy produkty. Zaprojektowanie logicznego i wygodnego w użyciu API nie jest łatwym zadaniem. Brak standaryzacji powoduje dodatkowe wyzwania w jego wykorzystaniu, utrudniając tym samym komunikację. Struktura endpoint’u zwracającego listę produktów mogłaby wyglądać następująco:

GET /api/products/all

GET /api/getallproducts

GET /api/books?all=true

Powyżej znajdują się trzy endpoint’y, które zasadniczo robią to samo. Zauważ, że nie ma żadnej konkretnej zasady opisującej jak ma wyglądać struktura danego endpoint’u. Praca z tak przygotowanym API zmusza nas do korzystania z dokumentacji za każdym razem, gdy wchodzimy z nim w interakcję. Rozwiązaniem tego problemu jest REST. REST jest standardem określającym zasady projektowania API. W przypadku Web API opiera się o protokół HTTP. Podstawowe funkcje w aplikacjach takie jak create, read, update, delete (CRUD), odpowiadają metodom POST, GET, PUT i DELETE. Przykład REST API wygląda następująco:

W powyższym przykładzie dokładnie widać co robią konkretne endpoint’y. Żądanie typu GET na adres /api/products zwraca listę produktów. Żądanie typu GET na adres /api/products/id zwraca produkt o podanym identyfikatorze. Żądanie typu POST na adres /api/products dodaje nowy produkt. Żądanie typu PUT na adres /api/products/id aktualizuje produkt o podanym identyfikatorze. I na koniec żądanie typu DELETE na adres /api/products/id, które usuwa produkt o podanym identyfikatorze.

REST definiuje kilka prostych zasad, których należy się trzymać projektując API. Nie będę ich w tym miejscu szczegółowo omawiał, a jedynie wymienię.

  1. Uniform Interface – Interfejs powinien zapewniać ustandaryzowaną komunikację pomiędzy klientem, a serwerem.
  2. Client-Server – Wyraźnie zaznaczony podział pomiędzy aplikacją działającą po stronie klienta i serwera.
  3. Stateless – Każde zapytanie musi posiadać komplet informacji koniecznych do jego poprawnego zakończenia.
  4. Cacheable – API powinno wspierać cache’owanie danych w celu zwiększenia wydajności.
  5. Layered System – System powinien być zaprojektowany w taki sposób, aby klient wysyłający zapytanie mógł uzyskać odpowiedź bez konieczności posiadania wiedzy o tym, co dzieje się po drugiej stronie.

Podczas interakcji z API wykorzystujemy zasoby. Zasób to obiekt (dowolna informacja), do której uzyskujemy dostęp. Odpowiednie nazewnictwo i struktura zasobów pozytywnie wpływają na użyteczność oraz elastyczność, ułatwiając rozbudowę i modyfikację aplikacji. Zasób można przedstawić za pomocą danych. Najpopularniejszym formatem danych wykorzystywanym w REST API jest JSON. Można również spotkać się ze wsparciem formatów XML oraz YAML.

Przykład zasobu w formacie JSON, XML oraz YAML.

Zasób może być pojedynczym obiektem lub kolekcją. Dla przykładu lista produktów „Products” to kolekcja zawierająca pojedyncze zasoby „Product”.

Web API opiera się o protokół HTTP. Istotnym elementem protokołu HTTP są metody. Metoda HTTP jest odpowiedzialna za określenie akcji, która ma zostać podjęta w wyniku żądania HTTP.

Na powyższym przykładzie po lewej stronie znajduje się pięć endpointów, a po prawej stronie pięć akcji. Każdy z endpointów powiązany jest z konkretną akcją. O powiązaniach decydują struktury adresów URL oraz metody HTTP. W przykładzie wykorzystano cztery najpopularniejsze metody HTTP:

  • GET – Odczytuje reprezentację zasobu. Zapytanie GET powinno wyłącznie zwracać dane.
  • POST – Dodaje zasób z wykorzystaniem przesyłanych danych.
  • PUT – Aktualizuje całą reprezentację zasobu z wykorzystaniem przesyłanych danych.
  • DELETE– Usuwa wskazany zasób lub kolekcję zasobów.

Ostatnią zagadaniem dotyczącym protokołu HTTP, na który chciałem zwrócić uwagę są statusy HTTP. Status HTTP to trzycyfrowa liczba określająca w jaki sposób zostało zrealizowane (lub nie) żądanie HTTP. Na podstawie statusu aplikacja może wyświetlić stosowną informację użytkownikowi. Każdy status HTTP został przydzielony do jednej z pięciu grup kodów:

  • 1xx– Kody informacyjne, np. 111 Connection refused.
  • 2xx– Kody powodzenia, np. 200 OK.
  • 3xx– Kody przekierowania, np. 301 Moved Permanently.
  • 4xx– Kody błędu aplikacji klienta, np. 404 Not Found.
  • 5xx– Kody błędu serwera HTTP, np. 500 Internal Server Error.
Przykład żądań typu GET ze statusem odpowiedzi 200 OK oraz 404 Not Found.

Pierwsza aplikacja w ASP.NET Core Web API

Utwórz swoje pierwsze REST API przy pomocy ASP.NET Core Web API. Prosta aplikacja, którą za chwilę zbudujesz, będzie umożliwiać wykonywanie operacji CRUD na produktach. W pierwszej kolejności przygotuj środowisko. Zainstaluj .NET 5.0, Visual Studio 2019 (v16.8) oraz Postmana. Postman jest bezpłatnym narzędziem, które można wykorzystać do testów API. Jest to narzędzie intuicyjne i proste w obsłudze, które pozwala między innymi na wysyłanie żądań HTTP.

Zacznij od utworzenia nowego projektu. Wybierz szablon ASP.NET Core Web Application i kliknij Next.

Zmień nazwę projektu na WebAPI i kliknij Create.

Następnie wybierz szablon API i utwórz projekt klikając Create.

Dodaj kontroler. Kliknij prawym przyciskiem myszy folder Controllers i wybierz kolejno Add i Controller.

Następnie wybierz API Controller – Empty i kliknij Add.

Zmień nazwę kontrolera na ProductsController.

Utworzona przez Visual Studio klasa ProductsController wygląda następująco:

Następnie utwórz nowy folder Models i dodaj w nim klasę Product.

W kontrolerze ProductsController zainicjalizuj kolekcję produktów _products.

Dodaj metodę Get zwracającą wszystkie produkty.

Uruchom aplikację. W moim przypadku API jest dostępne pod adresem https://localhost:44341.

Wyślij żądanie GET na adres api/products z poziomu POSTMANA.

Dodaj metodę Get zwracającą produkt o podanym identyfikatorze.

Uruchom aplikację i wyślij żądanie GET na adres api/products/1 z poziomu POSTMANA.

Dodaj metodę Post, która dodaj nowy produkt.

Następnie uruchom menadżera pakietów NuGet.

I zainstaluj pakiet Microsoft.AspNetCore.Mvc.NewtonsoftJson.

W klasie Startup w metodzie ConfigureServices skonfiguruj Newtonsoft.Json.

Uruchom aplikację i wyślij żądanie POST na adres api/products z poziomu POSTMANA.

Gdy ponownie wyślesz żądanie GET na adres api/products z poziomu POSTMANA to zobaczysz, że zasób został dodany.

Dodaj metodę Put, która aktualizuje produkt.

Uruchom aplikację i wyślij żądanie PUT na adres api/products/2 z poziomu POSTMANA.

Gdy ponownie wyślesz żądanie GET na adres api/products/2 z poziomu POSTMANA to zobaczysz, że zasób o identyfikatorze 2 został zaktualizowany.

Dodaj metodę Delete, która usuwa produkt.

Uruchom aplikację i wyślij żądanie DELETE na adres api/products/3 z poziomu POSTMANA.

Gdy ponownie wyślesz żądanie GET na adres api/products/2 z poziomu POSTMANA to zobaczysz, że zasób o identyfikatorze 3 został usunięty.