Metoda InvokeAsync przyjmuje obiekt HttpContext jako parametr, co umożliwia dostęp do informacji o żądaniu oraz odpowiedzi. Zastosowanie tej metody jest jedną z najlepszych praktyk, mających na celu poprawę wydajności oraz skalowalności aplikacji. Proces ten odbywa się w ramach wykonywania delegata _next(context), który również przyjmuje obiekt HttpContext. W przykładzie przedstawionym poniżej, przed kontynuowaniem wykonania kolejnego middleware, zapisywana jest informacja o żądaniu, a po wykonaniu - o odpowiedzi.
Takie podejście daje możliwość monitorowania i zapisywania kluczowych informacji w trakcie przetwarzania żądania i odpowiedzi, co może pomóc w diagnostyce i optymalizacji aplikacji.
Wstrzykiwanie zależności (DI) w middleware
Tworząc niestandardowe klasy middleware, należy przestrzegać zasady „Explicit Dependencies Principle” (EDP), o której mówiliśmy w poprzednich rozdziałach. Zasada ta mówi, że zależności klasy muszą być określone w konstruktorze. Middleware w ASP.NET Core jest tworzony podczas inicjalizacji aplikacji, co uniemożliwia wstrzykiwanie zależności o czasie życia scoped, jak ma to miejsce w przypadku kontrolerów. Jeśli zależności są wymagane w klasie middleware, należy dodać je bezpośrednio do sygnatury metody InvokeAsync, dzięki czemu będą one rozwiązywane przez DI.
Przykład:
To podejście umożliwia korzystanie z wstrzykiwania zależności w metodzie InvokeAsync, jednak może ograniczać widoczność tych zależności w kodzie.
Praca z middleware opartym na fabrykach
Alternatywą dla klasycznego wstrzykiwania zależności jest podejście oparte na fabrykach, które oferuje lepszą wydajność oraz elastyczność przy wykorzystaniu DI. Takie podejście jest szczególnie przydatne, gdy middleware wymaga zależności o czasie życia scoped. Middleware oparte na fabrykach korzysta z interfejsu IMiddleware, który umożliwia aktywację middleware przez kontener DI.
W tym podejściu konstruktor klasy zawiera zależności, a metoda InvokeAsync przyjmuje tylko dwa parametry: HttpContext oraz RequestDelegate. Przykład implementacji middleware:
Różnicą w tym przypadku jest to, że zależności są przekazywane do konstruktora klasy, a nie do samej metody InvokeAsync, co upraszcza kod. Aby zarejestrować middleware w aplikacji, używamy metody app.UseMiddleware, podobnie jak w przypadku tradycyjnego podejścia, ale z dodatkową rejestracją zależności w kontenerze DI.
Rejestracja w Program.cs:
Dzięki temu możemy zarządzać cyklem życia middleware na poziomie pojedynczego żądania, co zapewnia większą elastyczność, szczególnie w przypadku zależności o czasie życia scoped.
Rejestracja middleware w cyklu życia aplikacji
Middleware jest rejestrowane w pipeline aplikacji ASP.NET Core za pomocą metody UseMiddleware, która jest wywoływana w pliku Program.cs. Wszystkie middleware są tworzone w trakcie inicjalizacji aplikacji, a nie w odpowiedzi na każde żądanie, jak ma to miejsce w przypadku usług o czasie życia scoped. To podejście zapobiega dodawaniu zależności do konstruktorów klasy middleware, ale pozwala na ich dodanie do metody InvokeAsync. Istnieje również możliwość rejestracji middleware opartego na fabrykach, co pozwala na wykorzystanie zależności wstrzykiwanych przez DI przy zachowaniu większej wydajności i elastyczności.
Dodawanie funkcjonalności do aplikacji przy użyciu middleware
Middleware jest potężnym narzędziem do rozszerzania możliwości aplikacji webowych. Z pomocą middleware możemy dodać różne funkcjonalności, takie jak globalne obsługiwanie błędów, logowanie żądań czy ograniczanie liczby żądań. Nie ma sztywno określonej struktury katalogów ani przestrzeni nazw do tworzenia klas middleware, jednak dobrym zwyczajem jest organizowanie ich w osobnych folderach, na przykład w folderze „Middlewares” w głównym katalogu projektu.
W tym kontekście omówimy następujące middleware:
-
Globalne obsługiwanie błędów,
-
Logowanie żądań,
-
Ograniczanie liczby żądań.
Tworzenie aplikacji z tymi funkcjami może znacząco poprawić jakość i bezpieczeństwo aplikacji. Aby rozpocząć pracę z middleware, można utworzyć nowy projekt ASP.NET Core, a następnie dodać klasę middleware w odpowiednim folderze.
Rejestracja middleware opartego na fabrykach i zarządzanie cyklem życia usług
Rejestrując middleware w aplikacji ASP.NET Core, ważne jest zrozumienie różnicy między różnymi typami cykli życia usług. Middleware oparte na fabrykach pozwala na zarządzanie cyklem życia usług w kontekście żądania, co jest szczególnie istotne w przypadku usług o czasie życia scoped. Dzięki tej elastyczności możemy optymalizować aplikację pod kątem wydajności, jednocześnie zachowując pełną kontrolę nad zależnościami wstrzykiwanymi do middleware.
Jak działa wieloetapowa budowa obrazów Docker i jak zarządzać kontenerami?
Wielostopniowy proces budowy obrazów Docker jest jednym z kluczowych elementów efektywnego zarządzania aplikacjami i ich uruchamianiem w kontenerach. Dzięki temu podejściu można zminimalizować rozmiar wynikowego obrazu, co znacząco wpływa na wydajność oraz czas uruchamiania kontenera. Istotą jest zastosowanie kilku etapów w procesie budowy, co pozwala na oddzielenie etapów kompilacji, publikacji i ostatecznego uruchomienia aplikacji w kontenerze. W kontekście aplikacji ASP.NET Core oraz technologii Docker, zrozumienie tych procesów jest kluczowe do efektywnego wdrożenia i zarządzania środowiskiem produkcyjnym.
Pierwszym etapem w wielostopniowej budowie jest etap kompilacji. W tym etapie Docker wykorzystuje obraz bazowy, który zawiera zainstalowane odpowiednie SDK, w tym przypadku .NET SDK w wersji 8.0. Na tym etapie wykonywane są takie operacje jak kopiowanie plików źródłowych do kontenera, przywracanie zależności z pliku projektu (dotnet restore), a następnie kompilacja aplikacji (dotnet build). Wynikiem tych operacji jest zbudowany projekt, gotowy do dalszej publikacji.
Kolejnym etapem jest publikacja aplikacji. W tym momencie Docker bierze na bazę obraz z poprzedniego etapu, czyli ten, który zawiera zbudowaną aplikację, i uruchamia proces publikacji (dotnet publish). Celem jest przygotowanie aplikacji do wdrożenia, czyli skompilowanie jej w taki sposób, aby zawierała wszystkie niezbędne pliki do uruchomienia w środowisku produkcyjnym. W wyniku tego procesu powstaje paczka aplikacji, która może być przeniesiona do etapu końcowego.
Na ostatnim etapie, Docker tworzy ostateczny obraz, który będzie zawierał jedynie pliki niezbędne do uruchomienia aplikacji. W tym etapie wykorzystywany jest obraz bazowy, zawierający tylko środowisko uruchomieniowe (w tym przypadku .NET Runtime), bez zbędnych narzędzi deweloperskich czy plików tymczasowych. W wyniku tego etapu powstaje obraz finalny, który jest gotowy do uruchomienia w kontenerze produkcyjnym. Przykład ustawienia punktu wejścia (ENTRYPOINT) wskazuje, że aplikacja będzie uruchamiana poprzez komendę dotnet UrlShortener.dll.
Warto również zauważyć, że Docker pozwala na automatyzację tego procesu. Zamiast ręcznie przygotowywać aplikację, a następnie kopiować ją do obrazu, można w pełni zautomatyzować proces budowy i publikacji aplikacji, co znacząco przyspiesza i upraszcza proces wdrożeniowy.
Po zbudowaniu obrazu aplikacji, kolejny krok to jej uruchomienie w kontenerze. Aby to zrobić, należy użyć polecenia docker run, które pozwala na uruchomienie kontenera na podstawie wcześniej zbudowanego obrazu. Warto zwrócić uwagę na mapowanie portów, ponieważ aplikacja uruchomiona wewnątrz kontenera nasłuchuje na określonym porcie (w tym przypadku 8080), ale dzięki mapowaniu portów na hoście możemy uzyskać dostęp do aplikacji przez port hosta, na przykład 8899.
Zarządzanie wersjami obrazów Docker jest kluczowe, by utrzymać porządek w środowisku produkcyjnym. Obrazy Docker są niemutowalne, co oznacza, że każda zmiana w aplikacji wymaga wygenerowania nowego obrazu z innym tagiem. To pozwala na precyzyjne śledzenie wersji aplikacji oraz łatwiejsze zarządzanie kontenerami uruchomionymi na serwerach. W przypadku aktualizacji aplikacji należy wygenerować nowy obraz, co pomaga unikać problemów związanych z nieaktualnymi wersjami uruchomionych kontenerów.
Zasadniczo, podejście wielostopniowe w Dockerze nie tylko zapewnia lepszą organizację procesu budowy obrazu, ale również prowadzi do stworzenia bardziej zoptymalizowanego obrazu końcowego, który zawiera tylko niezbędne komponenty aplikacji, a pozbawiony jest narzędzi deweloperskich i innych zbędnych elementów. To z kolei przekłada się na mniejszy rozmiar obrazu, szybsze uruchamianie kontenerów oraz oszczędności w zasobach systemowych.
Aby w pełni zrozumieć korzyści wynikające z tego podejścia, warto pamiętać o następujących zasadach:
-
Optymalizacja rozmiaru obrazu jest kluczowa, ponieważ ma to bezpośredni wpływ na czas uruchamiania kontenera i zasoby systemowe.
-
Warto zawsze stosować wersjonowanie obrazów, aby zachować pełną kontrolę nad wersjami aplikacji w środowisku produkcyjnym.
-
Automatyzacja procesów budowy i publikacji aplikacji jest niezbędna w większych środowiskach produkcyjnych, gdzie ręczne zarządzanie może być zbyt czasochłonne i podatne na błędy.
-
Zrozumienie różnicy między SDK a Runtime w kontekście Docker pomaga w optymalizacji obrazów oraz przyspiesza procesy budowy i publikacji aplikacji.
Jak tworzyć natywne rozwiązania chmurowe, które naprawdę działają?
Wdrożenie rozwiązań natywnych dla chmury to znacznie więcej niż tylko uruchomienie aplikacji w środowisku chmurowym. To proces wymagający świadomości architektonicznej, umiejętności adaptacji i głębokiego zrozumienia zarówno celów biznesowych, jak i technicznych uwarunkowań nowoczesnych środowisk IT. Podejście to nie ogranicza się do kodowania — obejmuje także planowanie, zarządzanie, ciągłe doskonalenie i przyjęcie mentalności DevOps, która łączy zespoły biznesowe, operacyjne i deweloperskie we wspólnym celu: szybszym i skuteczniejszym dostarczaniu wartości.
Framework CAF (Cloud Adoption Framework) dostarcza zestawu zasad, które nie skupiają się na pojedynczej aplikacji, ale raczej na budowie całościowej struktury środowiska chmurowego. Stanowi on solidną podstawę do planowania i wdrażania nowych usług, angażując zespoły od poziomu infrastruktury aż po ciągłe dostarczanie i monitorowanie.
Komplementarnym zasobem jest WAF — Well-Architected Framework od Microsoft. Zawiera on pięć filarów: doskonałość operacyjną, bezpieczeństwo, niezawodność, wydajność oraz optymalizację kosztów. Każdy z tych filarów wspiera tworzenie odpornych, skalowalnych, efektywnych i zgodnych z założeniami biznesowymi aplikacji. WAF oferuje nie tylko dokumentację, ale również narzędzia do analizy istniejących obciążeń chmurowych, gotowe listy kontrolne i praktyczne wytyczne, pozwalając inżynierom analizować architekturę z wielu perspektyw jednocześnie.
Jednak aby naprawdę myśleć w sposób natywny dla chmury, inżynierowie oprogramowania muszą wyjść poza ramy pisania kodu. Dzisiejsza kultura DevOps nie sprowadza się już tylko do efektywnej komunikacji między zespołami — zakłada wspólne standardy, dzielenie się wiedzą, automatyzację i podejście „wszystko jako kod”. Zespoły operacyjne zaczęły korzystać z tych samych narzędzi co deweloperzy: repozytoriów GitHub, pipelines, Infrastructure as Code. To zacieranie granic wymaga od każdego członka zespołu umiejętności wykraczających poza jego tradycyjną rolę: znajomości zagadnień sieciowych, infrastrukturalnych, bezpieczeństwa i danych.
W środowisku, w którym czas dostarczenia wartości jest jednym z głównych wskaźników sukcesu, szybkie reagowanie na potrzeby rynku to nie tylko przewaga konkurencyjna, ale konieczność. Jednak szybkość nie jest tożsama ze zwinnością. Zwinność oznacza możliwość elastycznego reagowania na zmiany — szybkość bez refleksji i kontroli może prowadzić do nieefektywnych rozwiązań, trudnych w utrzymaniu. Oznacza to konieczność głębokiej analizy każdego etapu procesu tworzenia oprogramowania: od zarządzania zadaniami, przez zbieranie wymagań, implementację, aż po wdrożenie i utrzymanie.
To, że aplikacja została uruchomiona w chmurze, nie czyni jej natywną dla tego środowiska. Aplikacje natywne chmurowo muszą być projektowane z myślą o skalowalności, odporności na błędy i ciągłym rozwoju. Kluczowe elementy to nowoczesne podejście do projektowania (Design for Cloud), integracja z usługami wspierającymi, konteneryzacja, mikrousługi, a także orkiestracja i zarządzanie cyklem życia aplikacji. Każdy z tych aspektów musi być rozwijany równolegle, jako część całościowego podejścia, a nie jako element dodany na końcu.
Warto zauważyć, że model cloud-native nie jest zależny od konkretnego dostawcy chmury. Przeciwnie — to podejście niezależne od platformy, wspierane przez Cloud Native Computing Foundation (CNCF), organizację działającą w ramach Linux Foundation. CNCF zrzesza ponad 400 firm i promuje otwarte standardy, współdzielenie wiedzy oraz interoperacyjność. Jej celem jest tworzenie zrównoważonego ekosystemu technologii chmurowych — od konteneryzacji, przez mikrousługi, aż po dynamiczną orkiestrację.
CNCF nie tylko wspiera rozwój narzędzi, ale także zapewnia neutralną przestrzeń dla współpracy społeczności deweloperów, dostawców i użytkowników końcowych. Dzięki temu możliwe jest wdrażanie nowoczesnych, opartych na otwartych standardach rozwiązań, które są przenośne, elastyczne i gotowe na zmieniające się wymagania rynku.
Dla inżynierów tworzących aplikacje chmurowe oznacza to konieczność posiadania wiedzy i kompetencji wykraczających poza tradycyjne role. Konieczne staje się ciągłe uczenie się, umiejętność pracy w interdyscyplinarnych zespołach oraz dążenie do automatyzacji i optymalizacji procesów. Architektura aplikacji, jej bezpieczeństwo, sposób skalowania, reakcja na awarie czy efektywność kosztowa — wszystkie te aspekty muszą być brane pod uwagę już na etapie projektowania, a nie dopi
Jak stworzyć własne kolczyki z drutu?
Jak doświadczenie i pokora kształtują mistrzostwo w pieczeniu?
Jak nowoczesność prowadzi do ksenofobii i autorytarnego populizmu?
Jak populizm współczesny przyciąga tłumy? Przykład Trumpa i Mussoliniego w kontekście etyki i komunikacji politycznej
Jakie znaczenie mają tradycyjne niemieckie potrawy w kontekście kuchni i kultury?
Jakie materiały i techniki są najważniejsze przy tworzeniu amigurumi i odzieży?

Deutsch
Francais
Nederlands
Svenska
Norsk
Dansk
Suomi
Espanol
Italiano
Portugues
Magyar
Polski
Cestina
Русский