Wprowadzenie
Od wielu lat usługi REST wypierają stare dobre usługi SOAP (a można już chyba nawet powiedzieć, że wyparły). Kiedy zaczynałem tworzyć usługi REST oraz z nich korzystać, miałem w pewnym sensie wrażenie, że robimy krok w tył. W SOAP mieliśmy WSDL-a, który opisywał usługę. Dzięki niemu Visual Studio mogło wygenerować wszystkie klasy, których następnie używałem do komunikacji z usługą.
W przypadku usług RESTowych nie mamy czegoś takiego z pudełka. Bardzo często spędzamy więc długie godziny, aby dodać sporo kodu, który wcześniej był generowany. Oczywiście wszystko do czasu, aż trafimy na tytułową bibliotekę. W tym wpisie pokażę Ci, w jaki sposób dodać do WebApi dokumentację usługi z wykorzystaniem Swaggera. Szczególnie że wiele osób nie wie jeszcze o istnieniu tego narzędzia.
WebApi
Aby zobaczyć, jak działa Swagger, przygotowałem proste WebApi, które posłuży nam do testów. Tradycyjnie przykład znajduje się na githubie (https://github.com/danielplawgo/SwaggerExample). Api jest dość proste. Udostępnia jeden kontroler, który umożliwia zarządzanie produktami w aplikacji.
Klasa Product wygląda tak:
/// <summary> | |
/// The product. | |
/// </summary> | |
public class Product | |
{ | |
/// <summary> | |
/// Gets or sets the identifier. | |
/// </summary> | |
/// <value> | |
/// The identifier. | |
/// </value> | |
public int Id { get; set; } | |
/// <summary> | |
/// Gets or sets the name. | |
/// </summary> | |
/// <value> | |
/// The name. | |
/// </value> | |
public string Name { get; set; } | |
/// <summary> | |
/// Gets or sets the category. | |
/// </summary> | |
/// <value> | |
/// The category. | |
/// </value> | |
public string Category { get; set; } | |
/// <summary> | |
/// Gets or sets the price. | |
/// </summary> | |
/// <value> | |
/// The price. | |
/// </value> | |
public decimal Price { get; set; } | |
} |
Natomiast testowy kontroler tak:
/// <summary> | |
/// The products API. | |
/// </summary> | |
/// <seealso cref="System.Web.Http.ApiController" /> | |
public class ProductsController : ApiController | |
{ | |
private static List<Product> _products; | |
static ProductsController() | |
{ | |
int id = 1; | |
_products = new Faker<Product>() | |
.RuleFor(o => o.Id, f => id++) | |
.RuleFor(p => p.Name, (f, p) => f.Commerce.ProductName()) | |
.RuleFor(p => p.Category, (f, p) => f.Commerce.Categories(1).FirstOrDefault()) | |
.RuleFor(p => p.Price, (f, p) => f.Random.Number(100, 100000) / 100M) | |
.Generate(10); | |
} | |
/// <summary> | |
/// Gets the products. | |
/// </summary> | |
/// <returns></returns> | |
public IEnumerable<Product> Get() | |
{ | |
return _products; | |
} | |
/// <summary> | |
/// Gets the product by identifier. | |
/// </summary> | |
/// <param name="id">The identifier.</param> | |
/// <returns></returns> | |
public Product Get(int id) | |
{ | |
return _products.FirstOrDefault(p => p.Id == id); | |
} | |
/// <summary> | |
/// Add the product. | |
/// </summary> | |
/// <param name="product">The product.</param> | |
public void Post([FromBody]Product product) | |
{ | |
product.Id = _products.Select(p => p.Id).Max() + 1; | |
_products.Add(product); | |
} | |
/// <summary> | |
/// Update the product. | |
/// </summary> | |
/// <param name="id">The identifier.</param> | |
/// <param name="product">The product.</param> | |
public void Put(int id, [FromBody]Product product) | |
{ | |
var existingProduct = _products.FirstOrDefault(p => p.Id == id); | |
if (existingProduct != null) | |
{ | |
existingProduct.Category = product.Category; | |
existingProduct.Name = product.Name; | |
existingProduct.Price = product.Price; | |
} | |
} | |
/// <summary> | |
/// Deletes the product. | |
/// </summary> | |
/// <param name="id">The identifier.</param> | |
public void Delete(int id) | |
{ | |
var product = _products.FirstOrDefault(p => p.Id == id); | |
if (product != null) | |
{ | |
_products.Remove(product); | |
} | |
} | |
} |
Jak widać, nie ma tutaj nic bardzo skomplikowanego. Klasa Product zawiera kilka prostych właściwości. Kontroler udostępnia natomiast metody, które modyfikują dane zapisane w lokalnej statycznej liście. Na start dane są generowane z wykorzystaniem biblioteki Bogus.
Zwróć uwagę, że kod zawiera komentarze dokumentujące poszczególne elementy kodu. Jest to o tyle pomocne, że później Swagger wykorzysta je podczas generowania dokumentacji. Komentarze te można wygenerować za pomocą dodatku GhostDoc, który omawiam w moim darmowym kursie Visual Studio.
Domyślnie niestety nie mamy żadnego narzędzia, które udokumentowałoby nam nasze api oraz później pomogło w wygenerowaniu proxy do wywołania usługi.
Swagger
Swagger to nie tylko biblioteka do generowania dokumentacji tworzonego api. To przede wszystkich cały ekosystem narzędzi, które umożliwiają nam tworzenie RESTowego api. Ekosystem zawiera narzędzia do projektowania api, jego dokumentowania czy też testowania. Otrzymujemy dziesiątki narzędzi, które wspierają różne języki programowania, technologie, frameworki. Więcej informacji o całym ekosystemie znajdziesz na stronie głównej Swaggera – https://swagger.io/.
Z czasem na blogu będą pojawiały się opisy kolejnych elementów ekosystemu, które możesz wykorzystać do tworzenia lepszego REST api.
Dokumentowanie WebApi
Dodanie dokumentowania WebApi za pomocą Swaggera jest bardzo proste. Wystarczy w pierwszej kolejności dodać pakiet Swashbuckle z nugeta. Pakiet po zainstalowaniu doda do projektu nowy plik o nazwie SwaggerConfig, który znajduje się w katalogu App_Start. Klasa znajdująca się w pliku jest odpowiedzialna za konfigurację Swaggera i jest wywoływana podczas startu aplikacji.
W klasie możemy skonfigurować dwie rzeczy: sposób generowania dokumentacji api przez Swaggera oraz Swagger UI (o tym w kolejnej sekcji artykułu). Przykładowa zawartość SwaggerConfig może wyglądać tak (usunąłem zbędne komentarze):
[assembly: PreApplicationStartMethod(typeof(SwaggerConfig), "Register")] | |
namespace SwaggerExample.Api | |
{ | |
public class SwaggerConfig | |
{ | |
public static void Register() | |
{ | |
var thisAssembly = typeof(SwaggerConfig).Assembly; | |
GlobalConfiguration.Configuration | |
.EnableSwagger(c => | |
{ | |
c.SingleApiVersion("v1", "SwaggerExample.Api"); | |
c.IncludeXmlComments(string.Format(@"{0}\bin\{1}.xml", System.AppDomain.CurrentDomain.BaseDirectory, thisAssembly.GetName().Name)); | |
}) | |
.EnableSwaggerUi(c => | |
{ | |
}); | |
} | |
} | |
} |
Atrybut przed namespace’em informuje, że metoda Register z tej klasy zostanie wykonana podczas startu aplikacji. W metodzie, jak wspomniałem, konfigurujemy dwie rzeczy:
- EnableSwagger – uruchamia generowanie dokumentacji, a w przekazanym delegacie możemy to skonfigurować np. IncludeXmlComments określający, że komentarze dokumentujące z kodu mają być wykorzystane do opisów w dokumentacji.
- EnableSwaggerUi – uruchamia oraz konfiguruje SwaggerUI.
W wygenerowanej klasie znajduje się sporo komentarzy pokazujących, jak skonfigurować różne aspekty biblioteki. W przykładzie je usunąłem, ale planuję w przyszłości dodać więcej wpisów na temat Swaggera, w których pokażę Ci inne funkcje biblioteki oraz całego ekosystemu.
SwaggerUI
SwaggerUI jest prostym interfejsem użytkownika, za pomocą którego możemy przejrzeć dostępne api wraz z opisem oraz przetestować te api.
Po uruchomieniu aplikacji SwaggerUI jest dostępny pod adresem [adres aplikacji]/swagger/ui/index i w testowym projekcie wygląda tak:
Jak widać na zrzucie ekranu powyżej, SwaggerUI wyświetla informacje o wszystkich dostępnych kontrolerach oraz akcje, które udostępnia. Widzimy, jakie są typy żądań, a z prawej strony widać komentarze, które są ustawione w kodzie przy poszczególnych akcjach.
Testowanie
SwaggerUI, poza wyświetleniem informacji o api, daje również możliwość jego przetestowania. Wystarczy kliknąć na adres wybranej akcji. Wtedy rozwinie się panel, w którym możemy z jednej strony uzyskać więcej informacji o akcji, a z drugiej strony możemy ją wywołać i zobaczyć wynik. Widać to na poniższym zrzucie ekranu, gdzie wywołana jest akcja dla typu żądania Get, w której przekazujemy ID produktu, którego dane chcemy zobaczyć:
W sekcji Parameters wpisujemy ID produktu (na zrzucie 1), a następnie naciskamy przycisk „Try it out!” (zaznaczony czerwoną ramką). Po chwili niżej zobaczymy odpowiedź z api, w tym przypadku dane testowego produktu.
W podobny sposób możemy przetestować pozostałe akcje, które udostępnia api.
Przykład
Na githubie (https://github.com/danielplawgo/SwaggerExample) dostępny jest testowy projekt, którego użyłem do przygotowania tego wpisu. Możesz go pobrać i uruchomić. Nie jest potrzebna jakaś dodatkowa konfiguracja. Testowy ProductsController przy starcie generuje za pomocą biblioteki Bogus 10 produktów, które mogą posłużyć Ci do testowania api.
Zachęcam do pobrania przykładu i pobawienia się SwaggerUI.
Podsumowanie
Swagger jest biblioteką, bez której dzisiaj nie wyobrażam sobie tworzenia WebApi. Jedną z pierwszych rzeczy, które robię po utworzeniu aplikacji, jest dodanie tej biblioteki. Dzięki temu praktycznie bez wysiłku mam prosty interfejs, który udostępnia mi informacje o api oraz umożliwia testowanie go.
Ale Swagger to nie tylko dokumentowanie api. W jednym w kolejnych wpisów pokażę Ci, w jaki sposób można wykorzystać dane udostępnione przez Swaggera do generowania klienta naszego api. Co ciekawe, dostępne są narzędzia właściwie do każdego dostępnego języka i środowiska. Na co dzień wykorzystuję to podczas tworzenia klienta w .NET oraz Angularze. Ale to już temat na zupełnie inny wpis.
1 thought on “Swagger – dokumentowanie REST API”