Od Zera do Dockera: ASP.NET Core

Tym razem, zamiast dzielić się “lifestylowymi” i projektowymi newsami, zacznę cykl “koderski”.
Jak wcześniej wspominałem, stworzyłem open sourcową pogodynkę, którą można znaleźć pod domeną http://czyjebnie.pl .
Żeby nie przedłużać: Miłej zabawy!

Wstęp

Do zabawy z dockerami zmotywowały mnie warsztaty Michała Franca (link) oraz blog Piotra Gankiewicza (link), który pomógł mi w poprawnym deploymencie oraz konfiguracji serwera. W 1. części kursu zajmę się stroną ASP.NET Core 2.0, a w drugiej zagadnieniem samego docker’a.
Czemu Docker? Dla sportu. Mogłem użyć normalnego deploymentu, jak w przypadku http://lifelike.pl , ale chciałem sprawdzić jak to działa w praktyce. Poza tym, jeszcze można sporo rozwinąć i podzielić logikę na parę mikroserwisów.

ASP.NET Core

Dlaczego ASP.NET Core? Bo nie przepadam za JS w tym Node.js, Ruby’m, a co najgorsze php, pewnie wielu odradzi, ale wciąż wolę starego dobrego Razor’a. W czyjebnie.pl oprę się jednak na webapi. Strona jest statyczna, a do poglądu API użyłem Swagger’a.
Zatem zacznijmy od pobrania runtime i sdk do .Net Core ze strony: https://www.microsoft.com/net/download/core#/runtime. W chwili pisania, jest to wersja 2.0. Osobiście, z racji poręczności instaluję wersję pod MacOS, ale jeśli używasz innego systemu, dobierz pod siebie … tzn. swój system. 🙂

Pierwszy projekt

Tu z pomocą i ułatwieniem przyszły template’y, które udostępnił MS  (https://github.com/dotnet/templating/wiki/Available-templates-for-dotnet-new) oraz w przypadku mikroserwisów, event busów itp. pomocny był template od wspomniany wcześniej Michała (link do jego template).
W przypadku tego projektu, użyłem template’u zwanego ASP.NET Core Web API.

Aby utworzyć projekt wystarczy użyć komendy w terminalu:

[code]
dotnet new webapi -n nazwaprojektu -o scieżkaprojektu
[/code]

Omówienie projektu

Teraz przejdźmy do omówienia stworzonego przez nas projektu, opiszę to na przykładzie wcześniej stworzonego projektu:

Drzewo projektu

Tu mała ciekawostka, do tej pory Nuget (repozytorium bibliotek) przyzwyczaiło nas, że wszystkie paczki znajdują się w odpowiednim pliku, który był tylko listą paczek package.config. Nie ukrywam, brakuje mi tego pliku 🙂
Tutaj, wszystkie paczki dodajemy w pliku projektu w grupie obiektu. Tak samo konfigurujemy obsługę CLI np. do Entity Framework, czy innych wybranych narzędzi.

[code]



[/code]

W dokumencie program.cs ustalamy parametry do inicjacji serwera, a w startup.cs możemy “wczepić” nasze moduły, oraz konfigurację serwera, zawierającą takie informacje jak lokalizacja plików, przekierowywanie czy ustawienia.

Kontrolery

Jeśli ktoś miał już doczynienia z asp.net mvc czy z web api, to może pominąć ten akapit, ponieważ nic odkrywczego tutaj nie znajdziecie. Jeśli jednak jesteś ciekaw – zapraszam. Generalnie, kontroler służy do obsługi zapytań na poziomie HTTP – w przypadku API -> REST, a w przypadku Razor’a -> zwrot Widoków (CSHTML).

Przekierowywanie zapytań

Aby ułatwić sobie zapytania – “adresologię” (do zapytań) – w asp.net core używamy Routingu. Może się odbyć to na 2 sposoby:

1. Startup.cs
W metodzie Configure, dodajemy kolejne ścieżki:

[csharp]
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
[/csharp]

Ważne! nazwy routingów nie mogą się powtarzać 🙂

2. Atrybuty w kontrolerze
Ten sposób jest jak dla mnie bardziej intuicyjny i żałuję, że go wcześniej nie odkryłem. Przy nazwie klasy kontrolera, umieszczamy główne przekierowanie:

[csharp]
[Route("Pogoda")]
public class WeatherController: Controller
[/csharp]

Dzięki czemu, zapytania zaczynają się od adres:port/Pogoda/
A następnie przy metodach ustalamy szczegółowe zapytania, jak np:

[csharp]
[HttpGet("{miasto}")]
public async Task Get(string miasto)
[HttpGet("Full/{miasto}")]
public async Task GetFull(string miasto)
[/csharp]

Tak więc w 1. przykładzie mamy adres:port/Pogoda/{miasto} a w drugim adres:port/Pogoda/Full/{miasto}

Wstrzykiwanie zależności

Niektórzy wstrzykują sobie morfinę, inni kofeinę (np. aeropressem). W programowaniu, Depedency Injection nie jest aż tak inwazyjne, a nawet zbawienne. Jest to przydatny pattern, zwłaszcza jak robimy większe serwisy.
Dla przykładu: WeatherService
Serwis, który implementuje interfejs IWeatherService ( w którym wrzuciliśmy metody: )

[csharp]
Task GetForCity(string city);
Task StatusForCity(string city);
Task ImageForCity(string city);
Task ImageForCity(string city, int hour);
Task StatusForCity(string city, int hour);
[/csharp]

A następnie wrzuciliśmy do Startup.cs w ConfigureService:

[csharp]
services.AddSingleton();
[/csharp]

Równie dobrze, w WeatherService możemy wrzucić inne klasy, bazujące na tym samym interfejsie, ale korzystające np. z innego API pogodynki, czy z API testowego, podkładającego nam dane z kosmosu, czy nawet ze Słońca (na które podobno w Korei Północnej dolecieli w nocy oraz wrócili tego samego dnia) 🙂
A teraz najlepsze, żeby dostać się do naszego serwisu, czy każdego innego istniejącego, w konstruktorze kontrolera (lub innego serwisu)
dodajemy parametr IWeatherService oraz przypisujemy go do lokalnej zmiennej.
O taaaak:

[csharp]
public WeatherController(IWeatherService weather)
{
_weather = weather;
}
private readonly IWeatherService _weather;
[/csharp]

Możemy pójść GŁĘĘBIEJ i wejść w kolejny stan snu!
W lifelike.pl użyłem repozytoria do danych ILinkRepository , w LinkRepository odwołuję się do wstrzykniętej w startup.cs bazy:

[csharp]
private readonly PortalContext _context;
public LinkRepository(PortalContext context)
{
_context = context;
}
[/csharp]

Dzięki temu, warstwa operowania na danych jest pomiędzy, a bazę… zawsze można zmienić 🙂 tak samo zapytania do bazy. Dzięki temu kontroler nie musi wiedzieć co głębiej dokładnie siedzi. Tylko dostaje metody, które może użyć 🙂

Szczegóły na : Dependency Injection in ASP.NET Core

(https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection)

Podgląd API

Jestem człowiekiem praktycznym, jeśli coś dobrze działa, to używam, jeśli chcę odkryć jak coś działa.. piszę to sam. Tu jednak posłużę się gotowcem. Jeśli tworzymy API, które dostarczamy innym programistom czy klientowi, polecam użyć biblioteki Swagger. Generuje ona stronę, która wyświetla wszystkie zapytania API, które mamy w naszych kontrolerach, opierając się na atrybutach oraz tworzy od razu tester tych metod.
Przykład można znaleźć na [Swagger UI](http://czyjebnie.pl/swagger/)
Instalacja tego jest prosta.
Dodajemy paczki:

Swagger

[code]




[/code]

A następnie w startup w ConfigureServices dodajemy:

[csharp]
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new Info { Title = "CzyJebnie API",
Version = "v1",
Description = "API do pogodynki pod tytułem CzyJebnie",
TermsOfService = "None",
Contact = new Contact { Name = "Szymon Motyka", Email = "szymon@lifelike.pl", Url = "https://lifelike.pl"}
});
});
}
[/csharp]

oraz w Configure:

[csharp]
app.UseSwagger();

// Enable middleware to serve swagger-ui (HTML, JS, CSS etc.), specifying the Swagger JSON endpoint.
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
});
[/csharp]

A podgląd dostępny na adres:port/swagger/
Proste, czyż nie 🙂 ? Taki “rocket science” na poziomie Korei Północnej!
I nie… to nie jebnie!

Podsumowanie

Zostawiam was z takim cliffhangerem i każę czekać do następnego odcinka, który pojawi się za tydzień. Przy okazji, mały screenshot z nowego feature’a w lifelike : rpg (muszę jakoś sensowniej ponazywać projekty). Jest to drop system grafiki w postaci aeropressu oraz apteczki polowej, zostały wykonane przez Olę. Web player będzie dostępny, jak skończę cały ten motyw.