Wprowadzenie
W poprzednim artykule: Visual Studio 2019 - Preview skupiliśmy się na omówieniu nowości pojawiających się wraz z kolejnymi aktualizacjami środowiska Visual Studio 2019. Dzięki temu mamy również możliwość zobaczyć co oferuje .NET Core 3.0.
.NET Core 3.0 Preview 2 pojawił się pod koniec 2018 roku: What’s new in .NET Core 3.0 (Preview 2). W tym wpisie utworzymy naszą pierwszą aplikację przy wykorzystaniu ASP.NET Core 3.0 i spojrzymy na nowe funkcjonalności.
Proces instalacji: zakładam, że jesteście już po krótkiej lekturze poprzedniego wpisu a Wasze środowisko jest gotowe na kolejne eksperymenty. Przechodzimy zatem do cześci właściwej – tworzenia nowego projektu.
Tworzenie nowego projektu
Jak wiecie, Visual Studio 2019 oferuje całkowicie odmieniony interfejs użytkownika. Z dostępnych szablonów wybieramy ASP.NET Core Web Application:
W kolejnym kroku wybieramy ASP.NET Core 3.0 oraz wzorzec MVC:
Po utworzeniu projektu możemy przejść do bardziej szczegółowej analizy. Struktura wygląda podobnie do poprzedniej wersji ASP.NET Core . Możemy zauważyć zmiany w zależnościach projektu dotyczące pakietu Microsoft.AspNetCore.Mvc.NewtonsoftJson:
Wprowadzony w ASP.NET Core 2.1 meta-pakiet (Microsoft.AspNetCore.App) będzie zawierał tylko te składowe, które są w pełni rozwinięte, obsługiwane i wspierane przez Microsoft. Doszło zatem do usunięcia z powyższego meta-pakietu następujących komponentów:
-
Json.NET (Newtonsoft.Json);
-
Entity Framework Core (Microsoft.EntityFrameworkCore.*);
- Roslyn (Microsoft.CodeAnalysis).
Nowo utworzony projekt jest teraz zależny od .NET Core 3.0:
Na powyższym zrzucie ekranu możemy również zauważyć odniesienie do pakietu Microsoft.AspNetCore.Mvc.NewtonsoftJon.
Istotne zmiany w kodzie
Otwórz Program.cs a zobaczysz poniższy kod:
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup();
});
}
ASP.NET Core 3.0 korzysta z Generic Host. W poprzednich wydaniach mogliśmy zauważyć domyślne użycie Web Host.
Generic Host (HostBuilder): głównym celem jego zastowania jest oddzielenie protokołu HTTP od API Web Host’a. Pozwala to na użycie znacznie szerszego scenariusza konfiguracji. Możemy teraz wykorzystać wstrzykiwanie zależności czy mechanizm logowania:
var host = new HostBuilder()
.ConfigureServices((hostContext, services) =>
{
if (hostContext.HostingEnvironment.IsDevelopment())
{
// Konfiguracja środowiska developerskiego
}
else
{
// Konfiguracja pozostałych środowisk
}
services.AddHostedService();
services.AddHostedService();
})
Przejdźmy do analizy powyższego kodu z pliku Program.cs. Kod ten wykorzystuje webBuilder, który jest typem interfejsu IWebHostBuilder używanego z WebHostBuilder. Zgodnie z oficjalną notatką zostanie on oznczony jako przestrarzały (deprecated) a jego funkcjonlaność zostanie ostatecznie zastąpiona przez HostBuilder - warto mieć na uwadze, że sam interfejs pozostanie.
Główną różnicą pomiędzy WebHostBuilder oraz HostBuilder jest fakt, że nie będzie już można wstrzykiwać dowolnych usług do Startup.cs. Takie zachowanie rozwiązuje jednak problem związanych z wstrzykiwaniem usług do Startup.cs przed wywołaniem ConfigureServices.
Wspomniałem wcześniej, że komponent Json.NET jest usunięty ze współdzielonego meta-pakietu i musi teraz być dodany jako osobny pakiet. Otwórz plik Startup.cs i spójrz na metodę ConfigureServices:
public void ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddMvc()
.AddNewtonsoftJson();
}
W ASP.NET Core 2.2 pojawiło się pojęcie EndPoint Routing - rozwiązanie takie pozwalało na mapowanie żądania do "ostatecznego punktu końcowego" jeszcze przed wywołaniem MVC. Dzięki temu oprogramowanie pośredniczące (middleware) dostało możliwość dokonywania bardziej przemyślanych wyborów dotyczących przetwarzania żądań i kierowania ich do odpowiedniego "punktu końcowego". Zaktualizowana konfiguracja jest teraz dostępna w szablonach projektu 3.0.0-preview-2 w poniższej sekcji:
// Source: Startups.cs -> Configure(...)
app.UseRouting(routes =>
{
routes.MapApplication();
routes.MapControllerRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
Konfiguracja app.UseRouting() dodaje nowe oprogramowanie pośredniczące dla wspominanego powyżej End Point Routingu. UseRouting zastępuje powszechnie używane w przeszłości UseMvc. MapApplication wprowadza kontrolery i strony MVC do routingu a MapControllerRoute definiuje domyślną ścieżkę/trasę.
Podsumowując, End Point Routing pozwala na mieszanie frameworków takich jak MVC z innymi "routowalnymi obiektami" przy użyciu oprogramowania pośredniczącego w sposób jaki nie był dostępny do tej pory:
app.UseRouting(routes =>
{
routes.MapApplication();
routes.MapGet("/witaj", context =>
{
string message = "Przejdz do /healthStatus w celu 'oceny stanu zdrowia' Twojej aplikacji...";
return context.Response.WriteAsync("Witaj Kolego/Koleżanko!" + Environment.NewLine + message);
});
routes.MapHealthChecks("/healthStatus");
});
W powyższym przykładzie MapGet(...) definiuje sposób połączenia delegata żądania z routingiem. Z kolei MapHealthChecks(...) odpowiada za połączenie oprogramowania pośredniczącego (odpowiedzialnego za "ocenę stanu zdrowia aplikacji") do odpowiedniego routingu.
Funkcjonalność ta jest ciągle rozwijana i na bardziej szczegółowym opisie skupię się, kiedy szeroko dostępna będzie wersja ostateczna. Jeżeli jednak chcesz przetestować powyższy przykład pamiętaj, żeby dokonać odpowiedniej konfiguracji projektu.
W tym celu należy dodać do metody ConfigureServices(...) poniższy wpis:
services.AddHealthChecks();
Na wpis dotyczący tego tematu poświęcę osoby artykuł ponieważ ta funkcjonalność pozwala na rozwiązanie wielu problemów. Sam korzystam (z niezbyt optymalnego rozwiązania) jakim jest pingownie danej witryny w pewnych odstępach czasu. Jednakże takie podejście nie jest optymalne ponieważ pojedyncze wywołanie strony nie zawsze powie nam wszystko o "stanie zdrowia naszej aplikacji".