Paweł Łukasiewicz
2019-02-17
Paweł Łukasiewicz
2019-02-17
ASP.NET Core: najważniejsze funkcjonalności
ASP.NET jest jednym z najbardziej udanych frameworków do tworzenia aplikacji internetowych przygotowanych przez firmę Microsoft. Wraz z każdą aktualizacją pojawiają się nowe i rozszerzone funkcje, które pozwalają programistą wdrażać wysoce skalowalne i wydajne aplikacje internetowe.
W połączeniu z monitorowaniem aplikacji i narzędziami optymalizacyjnymi takimi jak Visual Studio Performance Profiler, ASP.NET staje się potężnym narzędziem pozwalającym na tworzenie niesamowitych aplikacji.
W samym frameworku dostępne są niezliczone funkcje, które pozwalają na pokonywanie wyzwań stających przed każdym programistą. Dodatkowo pozwalają na dodawanie przeróżnych funkcjonalności a także na zwiększenie wydajności samej aplikacji.
Obsługa wielu platform i kontenerów
Wraz z pojawieniem się technologii .NET Core możemy tworzyć aplikacje ASP.NET Core i wdrażać je na wielu systemach, tj. Windows, Linux oraz MacOS. Microsoft wraz ze społecznością programistyczną włożył ogromy wysiłek w uczynienie systemu Linux idealną platformą do wdrażania aplikacji ASP.NET Core.
Wraz z rosnącym zapotrzebowaniem na wydajność i coraz większą liczbę użytkowników architektura mikroserwisów jest teraz niezwykle popularna. Powszechne stało się używanie wirtualizacji za pomocą Docker’a oraz tworzenie i zarządzanie kontenerami za pomocą oprogramowania Kubernetes. ASP.NET Core pozwala na wykorzystanie i połączenie tych wszystkich technologii. Warto również mieć na uwadzę, że Microsoft Azure posiada własną usługę pozwalającą na wdrażanie aplikacji przy użyciu kontenerów.
Wysoka wydajność
Dla wielu, wydajność jest kluczową cechą oprogramowania. Nikt z nas przecież nie lubi długo czekać na odpowiedź z sewera czy długotrwałe uruchamianie systemu operacyjnego. ASP.NET Core wraz z serwerem Kestrel (.NET Core - Kestrel) reklamowany jest jako jeden z najszybszych frameworków do tworzenia aplikacji internetowych.
Technologia implementacji ASP.NET oraz IIS ma już około 17 lat (w momencie pisania artykułu). Zrobiła wszystko co mogła oraz cierpiała na swoje bolączki. Nowy serwer Kestrel został zaprojektowany od podstaw tak, aby pozwolić na programowanie asynchroniczne, być znacznie lżejszym i szybszym.
Asynchroniczność za pośrednictwem async/await
ASP.NET Core cechuje się ogromnym, natywnym wsparciem wykorzystania asynchronicznych wzorców programowania. Async jest zaimplementowany we wszystkich popularnych klasach i większości bibliotek zewnętrznych. Największym problem współczesnych aplikacji jest czas oczekiwania na wykonanie zapytania do bazy danych, zwrócenie danych z usługi internetowej (Web Service) oraz szereg innych operacji I/O.
Jednym z powodów dla których aplikacje ASP.NET Core są coraz szybsze jest szerokie wykorzystanie asynchronicznych wzorców w nowych szablonach MVC oraz serwera Kestrel.
// oznaczamy metodę jako asynchroniczną
public async Task<bool> GetData()
{
HttpClient client = new HttpClient();
// słowo kluczowe await zajmuje się obsługą złożoności wątku asynchronicznego
// i wywołań zwrotnych.
var data = await client.GetAsync("https://www.google.pl");
return data.IsSuccessStatusCode;
}
Ujednolicenie MVC i frameworków WebAPI
Przed pojawieniem się ASP.NET Core programiści najczęściej wybierali struktury MVC oraz WebAPI. Pierwsza z nich została przygotowana do tworzenia aplikacji internetowych obsługujących HTML. Interfejs WebAPI został zaprojektowany w celu tworzeniu usług RESTful w oparciu o język JSON lub XML.
Wraz z pojawieniem się ASP.NET Core obie te struktury zostały ze sobą połączone. W przeszłości ciągle się przenikały a samo ich połączenie jest dobrym posunięciem i znacząco upraszcza kolejne implementacje.
Wiele środowisk i trybów programowania
Niesamowitym ułatwieniem dla programistów jest możliwość rozróżnienia kodu będącego w trakcie rozwoju od tego będącego w produkcji za pomocą jednej dodatkowej instrukcji warunkowej.
// Interfejs: IHostingEnvironment dostarcza informacji o obecnie uruchomionym środowisku
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
// kod wewnątrz instrukcji wyświetla szczegółowe informacje dotyczące błędu - wyłącznie
// w środowisku deweloperskim
if (env.IsDevelopment())
{
app.UseBrowserLink();
app.UseDeveloperExceptionPage();
}
// na serwerze produkcyjnym użytkownik zostanie przekierowany na specjalnie
// przygotowną stronę wyświetlającą komunikat o błędzie
else
{
app.UseExceptionHandler("/Home/Error");
}
...
...
...
}
Przed pojawieniem się ASP.NET Core nie było standardowego sposobu na obsługę różnych warinatów kodu. Kod ten jest używany (domyślnie) m.in. w pliku Startup.cs - pozwala na łatwiejszą konfigurację aplikacji. W powyższym przykładzie odpowiada za wyświetlenie bardziej szczegółowego dziennika zdarzeń w przypadku uruchomienia aplikacji w środowisku programistycznym.
W przypadku tworzenia aplikacji webowych zmienne te są niezwykle pomocne. W zależności od opublikowanej wersji możemy używać różnych plików CSS czy JavaScript. W trakcie tworzenia aplikacji możemy używać lokalnych plików podczas gdy na sewerze produkcyjnym zostaną one zastąpione przez CDN. Jeżeli chesz dowiedzieć się więcej zapraszam pod poniższy adres: https://pl.wikipedia.org/wiki/Content_Delivery_Network
<environment names="Development">
<script src="~/lib/jquery/jquery.js"></script>
<script src="~/lib/bootstrap/js/bootstrap.js"></script>
</environment>
<environment names="Staging,Production">
<script src="https://ajax.aspnetcdn.com/ajax/jquery/jquery-2.2.0.min.js"
asp-fallback-test="window.jQuery"
</script>
</environment>
Jeżeli chcesz dowiedzieć się więcej o powyższych zmiennych środowiskowych zapraszam do oficjalnej dokumentacji: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/environments?view=aspnetcore-2.2
Dependency Injection (wstrzykiwanie zależności)
Jedną z najważniejszych funkcjonalności dostępnych w ASP.NET Core jest wbudowana obsługa Dependency Injection. Jest również użwana na każdym kroku w samym ASP.NET Core MVC. Warto mieć na uwadzę, że jest to preferowany sposób przekazywania kontekstów do kontrolerów MVC. Pod pojęciem kontekstów kryją się te odpowiedzialne za tworzenie dzienników zdarzeń, obsługę połączeń z bazą danych i szereg innych przekazywanych bezpośrednio do kontrolera.
public class CarService
{
private readonly ICarRepository _carReposiotry;
public CarService(ICarRepository carRepository)
{
_carReposiotry = carRepository;
}
public void Delete(int id)
{
_carReposiotry.Delete(id);
}
}
WebSockets & SignalR
Platforma ASP.NET Core oferuje doskonałe wsparcie dla WebSockets. Protokół ten (RFC 6455) pozwala na dwukierunkową komunikację za pośrednictwem połączeń TCP. Informaje te możemy wykorzystać do utrzymywania długich połączeń i komunikowania się z przeglądarką.
SignalR stanowi rozbudowaną część framworka ASP.NET. Pozwala na tworzenie komunikacji w czasie rzeczywistym. Jeżeli zależy Ci na ciagłej wymianie informacji bez klikania przycisku ‘Odswież’ warto sięgnać po takie rozwiązanie. Najłatwiejszym w zrozumieniu przykładem jest prosty czat. Wiadomość zwrotną jesteśmy w stanie otrzymać bez zbędnego (w tym wypadku) odswieżania okna naszej przeglądarki.
Ochrona przed CSFR (Cross-Site Request Forgery)
Bezpieczeństwo jest niezwykle ważne. Temu zagadnieniu warto też poświęcić wyjątkowo dużo czasu tworząc odpowiednie zabezpiecznia. Typów samych ataków jest mnóstwo. Warto jednak pokrótce zapoznać się z CSFR - atak ten pozwala na przechwycenie uwierzytelnionej sesji użytkownika a następnie wykonanie operacji, która nie została zainicjowana przez tego użytkownika.
Przykładem takiej operacji jest logowanie się na konto bankowe a następnie przejście do całkiem innej witryny internetowej. Jeżeli ta witryna byłaby w stanie wykonać operację typu POST do strony naszego banku moglibyśmy stracić wszystkie oszczędności. Taka możliwość zachodzi jeżeli nasza sesja po stronie banku jest ważna a sama organizacja nie sprawdza w poprawny sposób przychodzących żądań.
Platforma ASP.NET pozwala nas ochronić przed tego typu atakami. Generuje specjalne tokeny, które zapobiegają fałszerstwu. Więcej na temat tego typu ataków możecie dowiedzieć się z oficjalnej dokumentacji znajdującej się pod adresem: https://docs.microsoft.com/en-us/aspnet/mvc/overview/security/xsrfcsrf-prevention-in-aspnet-mvc-and-web-pages
Samodzielne hostowanie aplikacji
Termin "samodzielnego hostowania" (self-hosted) odnosi w głównej mierze do serwera Kestrel udostępnionego wraz z pojawieniem się technologii .NET Core. Więcej o samym serwerze możecie poczytać pod poniższym adresem: .NET Core - Kestrel
Przed pojawieniem się technologii .NET Core większość z nas decydowała się na serwer IIS. Nie był to oczywiście jedyny sposób. Używając frameworka w wersji 4.5 mogliśmy posłużyć się również Nancy, Owin czy WCF. Wszystko stało się zdecydowanie szybsze i wydajniejsze (chodź przy okrojonych funkcjach) wraz z pojawieniem się serwera Kestrel. Od teraz istnieje możliwość hostowania aplikacji ASP.NET Core na platformie Windows jako usługi Windows Service bez używania serwera IIS.
Filtry akcji
Obsługa filtrów akcji w ASP.NET Core jest niezwykle przydatna. Filtry pozwalają na implementacje funkcjonalności, które można zastosować do całego kontrolera lub akcji bez modyfikacji samej akcji.
Filtry są używane do cachowania danych, obsługi błędów, autoryzacji lub każdej innej, niestandardowej logiki, którą chcesz zaimplementować.
W pierwszej kolejności musimy utworzyć filtr. W naszym przypadku filtr akcji będzie odpowiedzialny za dodawnie odpowiednich wpisów do konsoli dziennika zdarzeń:
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Routing;
using System.Diagnostics;
namespace ActionFiltersExample.Filters
{
public class LogActionFilter : ActionFilterAttribute
{
/// <summary>
/// Wywoływana przez środowisko ASP.NET Core MVC przed wykonaniem akcji kontrolera
/// </summary>
/// <param name="filterContext"></param>
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
Log("OnActionExecuting", filterContext.RouteData);
}
/// <summary>
/// Wywoływana przez środowisko ASP.NET Core MVC po wykonaniu akcji kontrolera
/// </summary>
/// <param name="filterContext"></param>
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
Log("OnActionExecuted", filterContext.RouteData);
}
/// <summary>
/// Wywoływana przez środowisko ASP.NET Core MVC przed zwróceniem wyniku wykonania akcji kontrolera
/// </summary>
/// <param name="filterContext"></param>
public override void OnResultExecuting(ResultExecutingContext filterContext)
{
Log("OnResultExecuting", filterContext.RouteData);
}
/// <summary>
/// Wywoływana przez środowisko ASP.NET Core MVC po zwróceniu wyniku wykonania akcji kontrolera
/// </summary>
/// <param name="filterContext"></param>
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
Log("OnResultExecuted", filterContext.RouteData);
}
/// <summary>
/// Metoda logowania danych
/// </summary>
/// <param name="methodName"></param>
/// <param name="routeData"></param>
private void Log(string methodName, RouteData routeData)
{
var controllerName = routeData.Values["controller"];
var actionName = routeData.Values["action"];
string message = $"Nazwa metody: {methodName}, nazwa kontrolera: {controllerName}, nazwa akcji: {actionName}";
Debug.WriteLine(message, "Wpis dziennika zdarzeń");
}
}
}
Tak przygotowany filtr możemy zaaplikować na kontroler (bez zmiany każdej pojedynczej akcji):
namespace ActionFiltersExample.Controllers
{
[LogActionFilter]
public class HomeController : Controller
{
public HomeController()
{
}
public IActionResult Index()
{
ViewData["Message"] = "Your application description page.";
return View();
}
...
...
...
}
}
Wykonanie każdej akcji w kontrolerze Home będzie skutkowało dodaniem podanych zdarzeń do konsoli dziennika zdarzeń:
- Wpis dziennika zdarzeń:Nazwa metody: OnActionExecuting, nazwa kontrolera: Home, nazwa akcji: Index
- Wpis dziennika zdarzeń:Nazwa metody: OnActionExecuted, nazwa kontrolera: Home, nazwa akcji: Index
-
Wpis dziennika zdarzeń:Nazwa metody: OnResultExecuting, nazwa kontrolera: Home, nazwa akcji: Index
- Wpis dziennika zdarzeń:Nazwa metody: OnResultExecuted, nazwa kontrolera: Home, nazwa akcji: Index
Extensible Output Caching
Funkcjonalność ta pozwala na buforowanie danych wyjściowych generowanych przez stronę i przechowywanie ich w pamięci podręcznej celem obsługi przyszłych żądań. Dane przechowywane w pamięci nie są często aktualizowane – mogą zostać ponownie użyte.
ASP.NET Core pozwala na określenie w prosty sposób (przy pomocy nagłówków HTTP) jak długo każde żądanie powinno być przechowywane. Takie rozwiązanie pozwala również na przechowywanie tych danych bezpośrednio w pamięci na Twoim serwerze WWW. Nic nie stoi również na przeszkodzie przed użyciem innych dostawców do obsługi cachowania takich jak Redis.
Globalizacja i lokalizacja
ASP.NET Core ułatwia ‘lokalizowanie’ formatów dat czy liczb w aplikacjach internetowych. Pojęcie lokalizacji jest niezwykle istotne jeżeli chcesz używać swojej aplikacji na całym świecie.
Korzystając w odpowiedni sposób z pliku zasobów (resources) jesteśmy w stanie dostosować naszą aplikację do wielu języków. Pliki zasobów traktowane są jako centralne repozytorium a dostęp do odpowiednich tłumaczeń polega na odczytaniu odpowiedniej etykiety w konkretnym języku. Wyróżniamy dwa rodzaje zasobów:
- zasoby lokalne (local resources) – charakterystyczne dla danej strony (każda strona będzie posiadała swój lokalny plik zasobów);
- zasoby globalne (global resources) – charakterystyczne dla całej aplikacji (jeden, wspólny plik zasobów).
SwaggerUI
Jeżeli tworzysz API warto upewnić się, że skorzystasz z narzędzia jakim jest Swagger. Ułatwia dokumentowanie i testowanie interfejsów API.
Jeżeli chcesz dowiedzieć się więcej na temat tego narzędzia zapraszam do artykułu: https://www.plukasiewicz.net/Artykuly/AspNetWebAPISwagger
Podsumowanie
Środowisko ASP.NET Core posiada szereg istotnych ulepszeń w stosunku do poprzednich wersji. W artykule omówiłem pokrótce niektóre z kluczowych funkcji o których każdy programista powinien wiedzieć. Niektóre z nich są nowe, inne istnieją od pewnego czasu a kilka z nich to kluczowe funkcje nowej platformy ASP.NET Core.