Cześć, z tej strony Jarmatys. To druga lekcja kursu o podstawach ASP.NET Core MVC. Tutaj bierzemy na warsztat dwa filary, bez których żadna aplikacja webowa w .NET nie ruszy z miejsca: routing i controllery. Zobaczysz jak zbudowane są pliki Program.cs i Startup.cs, jak działa domyślny pattern URL-a, czym różni się akcja od metody i jak zmienić routing przez atrybut [Route]. Zrzucę z tego mgłę tak, żebyś potem swobodnie poruszał się po każdym projekcie .NET.
Program.cs i Startup.cs — co tu się w ogóle dzieje
Gdy odpalasz nowy projekt ASP.NET Core, widzisz dwa pliki, do których rzadko będziesz zaglądać, ale warto wiedzieć, co siedzi w środku.
Program.cs jest bardzo krótki. Jedyne, co tam się dzieje, to stworzenie hosta i uruchomienie aplikacji. Metoda Main buduje hosta, który podczas startu serwera pociąga konfigurację z klasy Startup. Tyle. Na tym etapie kursu to wszystko, co musisz wiedzieć.
Startup.cs jest ciekawszy i to z nim będziesz gadać najczęściej. Zobacz jego trzy sekcje:
- Konstruktor — przez DI wstrzykiwana jest tu
IConfiguration, która trafia do publicznej właściwości. Będziesz z tego korzystać w różnych miejscach (appsettings, zmienne środowiskowe itp.). ConfigureServices— wpada tu kolekcja serwisów. Domyślnie zobaczysz coś w styluservices.AddControllersWithViews(). To ustawienie mówi .NET-owi: "obsługuj mi controllery (folderControllers) i widoki (folderViews)".Configure— tu kleisz pipeline HTTP. SprawdzaszIsDevelopment(), włączaszUseDeveloperExceptionPage, redirect na HTTPS,UseStaticFiles,UseRouting,UseAuthorizationi na końcu — to dla nas dzisiaj najważniejsze —UseEndpoints.
Na produkcji nie chcesz, żeby użytkownik dostał brzydką stronę z wyjątkiem z twojej aplikacji — dlatego exception page pokazujesz tylko w developmencie.
Fragment Configure, który nas interesuje, wygląda mniej więcej tak:
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
I to jest właśnie routing konwencjonalny.
Jak czytać domyślny pattern URL-a
Wzorzec {controller=Home}/{action=Index}/{id?} mówi trzy rzeczy:
- Pierwszy segment URL to nazwa controllera. Domyślnie
Home. - Drugi segment to akcja (czyli metoda w tym controllerze). Domyślnie
Index. - Trzeci segment to opcjonalny parametr
id(znak?oznacza opcjonalny).
Dzięki temu, jak wchodzisz na https://localhost/, .NET wie, że ma pociągnąć HomeController.Index(). Jak usuniesz domyślne wartości z patternu i zostawisz sam {controller}/{action}/{id?}, to na gołym URL-u dostaniesz 404 — bo .NET nie ma pojęcia, do kogo cię kierować. Musiałbyś wpisać /Home/Index ręcznie. Ktoś ci kiedyś powie "link do mojej strony to moja-domena.pl" i ma się to działać — więc bez domyślnych wartości ani rusz.
Routing to nic innego jak mapa. Mówisz .NET-owi: "jak ktoś wejdzie na taki URL, zawołaj taką akcję". A akcja to zwykła publiczna metoda w klasie controllera.
Controller, akcja i zwracanie widoku
Controller to po prostu klasa, która dziedziczy po Controller (albo ControllerBase) i ma w środku metody — te metody nazywamy akcjami. Każda akcja zwraca jakiś typ, który .NET zamienia na odpowiedź HTTP: widok, redirect, JSON, status itd.
Zobacz prosty przykład — HomeController z akcją Product, która przyjmuje parametr name:
public class HomeController : Controller
{
public IActionResult Index()
{
return View();
}
public IActionResult Product(string name)
{
return View();
}
}
Jak teraz wejdę na /Home/Product, .NET złapie mnie w tę akcję. Jeśli nie ma widoku Product.cshtml w Views/Home/, dostaniesz exception z informacją, że widok nie został znaleziony — i od razu masz podpowiedź, w których folderach szukał. Visual Studio daje ci wtedy skrót: prawy przycisk na nazwie akcji → Add View. Postawi ci plik Product.cshtml we właściwym folderze, z referencją do _Layout i pustym <h1>, gotowy do wypełnienia.
Żeby przekazać parametr, możesz pójść najprostszą drogą — query string: /Home/Product?name=coś. Po postawieniu breakpointa w akcji zobaczysz, że name ma wartość "coś". Tyle że takie URL-e są brzydkie. Ładniej wyglądałoby /Home/Product/coś, gdzie coś siedzi jako segment ścieżki, nie query. Żeby tak zrobić, musisz zmienić routing.
Attribute routing — jak przejąć pełną kontrolę
Domyślny pattern jest wygodny, ale czasem chcesz mieć swój, ładny URL — np. /polityka-prywatnosci albo /product/{name} bez części z Home. Do tego służy attribute routing: dekorujesz akcję atrybutem [Route("...")] i on nadpisuje konwencjonalny routing dla tej metody.
public class HomeController : Controller
{
[Route("product/{name}")]
public IActionResult Product(string name)
{
return View();
}
[Route("polityka-prywatnosci")]
public IActionResult Privacy()
{
return View();
}
public IActionResult GoToPrivacy()
{
return RedirectToAction("Privacy");
}
}
Co tu się dzieje:
[Route("product/{name}")]mówi: "adres to/product/{name}licząc od domeny, nie od nazwy controllera". Wchodzisz na/product/jakis-produkti trafiasz w akcję, aname="jakis-produkt".[Route("polityka-prywatnosci")]to URL zupełnie bez udziału nazwy controllera/akcji. Super rzecz do landing page'y i stron statycznych.RedirectToAction("Privacy")przekierowuje w obrębie tego samego controllera. Jak chcesz przekierować do akcji w innym controllerze, podajesz drugi parametr:RedirectToAction("Index", "Product").
Attribute routing bierze pierwszeństwo. W momencie gdy przykleisz
[Route(...)]do akcji, stary/Home/Productprzestaje działać —.NETkieruje się już tylko tym, co napisałeś w atrybucie.
Pamiętaj, że RedirectToAction masz za darmo, bo twój controller dziedziczy po Controller. W podpowiedziach IntelliSense zobaczysz też Redirect (do dowolnego URL-a), RedirectToRoute, Json, Content, File itd. Akcja nie musi zwracać widoku — może zwrócić cokolwiek, byle było typu IActionResult.
Nowy controller i domyślna akcja Index
Dodajmy drugi controller, żeby zobaczyć, jak to się układa. Prawy przycisk na folderze Controllers → Add → Controller → Empty. Visual Studio wygeneruje ci boilerplate:
public class ProductController : Controller
{
public IActionResult Index()
{
return View();
}
}
Dwie rzeczy, które warto zapamiętać:
- Sufiks
Controllerw nazwie klasy jest obowiązkowy..NETpatrzy na typ i tnie ten sufiks, żeby wyciągnąć "segment URL".ProductController→ w URL-u piszeszproduct. - Akcja
Indexjest domyślna. Jeśli wejdziesz na/product, trafisz doProductController.Index(). Jeśli dopiszesz inną metodę, np.Details, to musisz ją wywołać jawnie jako/product/details.
Dokładasz widok analogicznie jak wcześniej — prawy przycisk na akcji → Add View — i Views/Product/Index.cshtml jest gotowy. Wpisujesz /product w przeglądarce, masz swój nowy controller w akcji.
Co dalej?
To był fundament, na którym zbudujemy wszystko, co dalej. W następnej lekcji biorę się za modele i widoki — pokażę ci, jak przekazać dane z akcji do widoku, jak silnie typować View(model) i jak renderować listy i formularze. Po tym routing i controllery wpadną ci już zupełnie naturalnie.
Kod do tej lekcji masz na GitHubie: asp-net-core-podstawy / Lekcja-2. Jakbyś miał pytania albo coś zgrzytało — walnij komentarz pod filmem, odpiszę. Trzymaj się i do zobaczenia w następnym wpisie.



