Wprowadzenie do aplikacji internetowych
Cześć! W ramach grupy Junior.NET Politechnika Łódzka postanowiłem spróbować swoich sił w prowadzeniu zajęć. W pierwszej części tego krótkiego kursu wprowadzę was w świat backendu aplikacji webowych.
Uwaga
Ten wpis to notka z zajęć, większość tematów została pokryta bardzo powierzchownie.
Jak działa przeglądarka internetowa?
W momencie w którym wchodzisz na wybraną prze siebie stronę twoja przeglądarka wysyła zapytanie do serwera korzystając z protokołu HTTP.
HTTP
HTTP, czyli Hypertext Transfer Protocol powstał w 1989 i jego głównym zadaniem jest właśnie obsługa stron WWW.
Na tym protokole zbudowany został standard REST dzięki któremu przez protokół HTTP przesyłane jest teraz o wiele więcej informacji niż same strony WWW (np. dane z czujników IoT, strony SPA), ale o tym innym razem.
Jak wygląda takie zapytanie?
Zapytania HTTP
Zapytanie utworzone przez przeglądarkę w momencie przejścia na stronę:
GET /index.html HTTP/1.1
Host: localhost:8000
Connection: keep-alive
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36
Upgrade-Insecure-Requests: 1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate, br
Accept-Language: pl,en-US;q=0.9,en;q=0.8,en-GB;q=0.7
Pierwsza linia definiuje typ zapytania.
GET
- Oznacza typ naszego zapytania
/index.html
- To zasób o który prosimy
HTTP/1.1
- Określa wersje protokołu za pomocą której chcemy się komunikować z serwerem.
Każda kolejna linia to kolejne nagłówki HTTP. Oprócz standardowych nagłówków które widzicie powyżej, możemy także tworzyć własne nazywając je X-nazwa
.
Listę standardowych nagłówków znajdziesz tutaj.
Host
- To adres naszego serwera
User-Agent
- Określa wersję naszej przeglądarki
Accept
- Mówi serwerowi o typie odpowiedzi jakiej oczekujemy (w tym wypadku HTML).
Odpowiedź serwera:
HTTP/1.1 200 OK
Accept-Ranges: bytes
Cache-Control: public, max-age=0
Last-Modified: Fri, 05 Jan 2018 10:50:03 GMT
ETag: W/"a11-160c5f12470"
Content-Type: text/html; charset=UTF-8
content-length: 2658
Date: Fri, 05 Jan 2018 10:51:39 GMT
Connection: keep-alive
<!DOCTYPE html>
<head>
<!-- I tak dalej... -->
W ciele zapytania (czyli we wszystkim co znajduje się po pustej linii po nagłówkach) Serwer zwrócił to o co go prosiliśmy - zasób index.html
.
200 OK
- To kod odpowiedzi
Content-Type
- Określa typ zawartości zwróconej w ciele zapytania
content-length
- Długość odpowiedzi wyrażona w bajtach.
Kody HTTP
Kody HTTP opisują odpowiedź serwera. Całą listę znajdziesz tutaj, natomiast najpopularniejsze to:
Kod | Nazwa kodu | Opis kodu |
---|---|---|
200 | OK | Zapytanie wykonało się poprawnie |
301 | Moved Permanently | Zasób, o który prosisz został przeniesiony w inne miejsce (serwer zazwyczaj wskazuje w które) |
400 | Bad Request | Zapytanie zostało przez nas źle sformułowane |
401 | Unauthorized | Nie mamy uprawnień do przeglądania zasobu |
404 | Not Found | Nie znaleziono zasobu |
500 | Internal Server Error | Coś złego stało się na serwerze co uniemożliwiło mu poprawne przetworzenie zapytania |
Metody HTTP
W ramach tego kursu zajmiemy się tylko metodami GET i POST.
- Zapytania typu GET zapisują się w historii przeglądarki, można je dodawać do zakładek
- Są one używane wszędzie tam, gdzie prosimy o jakiś zasób nie modyfikując go
- Mają limit długości (najlepiej nie przekraczać 2000 znaków)
- Mogą one przekazywać parametry do zapytania “w linku” (query string)
Przykładowy Query String: google.pl/search?q=smoki&ei=ogdTWqb4EsP_sQGbh4noBg
? oznacza koniec nazwy zasobu i początek parametrów. Parametry są podawane w formacie klucz=wartość, oraz są rozdzielane znakiem &.
Zapytania POST służą do modyfikacji zasobów, oraz do przekazywania wrażliwych danych. Oprócz przesyłania parametrów w query stringu mogą również przesyłać je w ciele zapytania:
POST /test/demo_form HTTP/1.1
Host: localhost:8000
login=admin&password=admin1
Przesyłanie parametrów tą metodą zdejmuje ograniczenie długości zapytania.
Narzędzia do pracy z HTTP
W narzędziach deweloperskich waszej przeglądarki (dostępne zazwyczaj pod klawiszem F12) w zakładce network możecie obejrzeć i zbadać wszystkie zapytania HTTP obsłużone przez obecną kartę. Czasem jednak potrzeba czegoś bardziej rozbudowanego od samej przeglądarki, poniżej wypisałem więc najbardziej przydatne darmowe narzędzia.
Jeżeli potrzebujecie przechwycić zapytanie z innej aplikacji lub checie dowiedzieć się więcej o nim polecam narzędzie Fiddler. Jest to proxy, które przechwytuje wszystkie zapytania HTTP z danej maszyny, umożliwia ich nagrywanie, oskryptowywanie, emulacje serwerów i wiele innych przydatnych rzeczy.
Do ręcznego tworzenia zapytań polecam narzędzie Postman.
.NET Core
.NET Core to nowa (wersja 1.0 została wydana w czerwscu 2016 roku), w pełni open source’owa wersja frameworka .NET. Została stworzona głównie do budowania rozwiązań opartych o chmurę i nie ma zastąpić pełnego .NETu.
- Umożliwia tworzenie aplikacji konsolowych, backendu aplikacji uniwersalnych oraz aplikacji internetowych - niestety nie napiszemy w nim programu z interfejsem graficznym.
- Aplikacje oparte o ten framework możemy tworzyć i uruchamiać na wszystkich popularnych systemach operacyjnych - Windows, Linux, macOS.
- Od wersji 2.0 możemy uruchamiać aplikacje także na Raspberry Pi 2 i 3
- Większość podstawowych bibliotek została rozbita na moduły - paczki NuGet.
ASP.NET Core
ASP.NET Core jest frameworkiem do budowania aplikacji internetowych. Obecnie jest jednym z najszybszych dostępnych rozwiązań, a na pewno jest dużo szybszy niż poprzednika (ASP.NET). Tak samo jak cały .NET Core, ASP.NET Core jest rozwiązaniem w pełni open source.
Tworzenie nowego projektu ASP.NET Core Razor Pages
Szablon projektu możemy stworzyć na dwa sposoby.
Pierwszy z nich to Visual Studio: File -> New project -> .NET Core -> ASP.NET Core Web Application.
Z menu na górze wybieramy .NET Core, ASP.NET Core 2.0, zaznaczamy Web Application, upewniamy się że obsługa Dockera oraz uwierzytelnianie są wyłączone.
Jeżeli jednak nie chcemy korzystać z Visual Studio możemy skorzystać z konsolowego narzędzia dotnet
. Wystarczy w konsoli wpisać dotnet new razor
w wybranym przez nas folderze.
Opis szablonu
Ścieżka | Opis |
---|---|
launchSettings.json | Ustawienia IIS (serwera naszej aplikacji) oraz profili uruchamiania |
appsettings.json | Ustawienia logowania, dodatkowo tutaj wrzucamy własne ustawienia |
Pages/ | Tutaj znajdują się nasze strony korzystające z technologii Razor Pages |
Startup.cs, Program.cs | Pliki uruchamiające nasz projekt |
Program.cs:
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
namespace WebApplication
{
public class Program
{
public static void Main(string[] args)
{
BuildWebHost(args).Run();
}
public static IWebHost BuildWebHost(string[] args)
{
return WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.Build();
}
}
}
Program.cs zawiera punkt startowy naszej aplikacji. Nie będziemy musieli go modyfikować, w skrócie plik ten odpowiada za uruchomienie naszego serwera oraz wywołanie pliku konfiguracyjnego Startup.cs.
Startup.cs:
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
namespace WebApplication
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseBrowserLink();
}
else
{
app.UseExceptionHandler("/Error");
}
app.UseStaticFiles();
app.UseMvcWithDefaultRoute();
}
}
}
Startup.cs odpowiada za konfigurację naszej aplikacji. Metody ConfigureServices
oraz Configure
są wywoływane co każde zapytanie.
ConfigureServices rejestruje potrzebne nam implementacje interfejsów. W domyślnym przykładzie dodaje tylko możliwość obsługi MVC - Czegoś, co umożliwia mu obsługiwanie Razor Pages. MVC omówimy dokładniej na trzecich zajęciach.
Metoda Configure obsługuje nasze zapytanie - jest to tak zwany request pipeline.
Jeżeli poprosimy o zasób /js/site.js wykonywanie tej metody zatrzyma się na app.UseStaticFiles()
ponieważ całe zapytanie zostanie obsłużone właśnie przez tę metodę.
Jeżeli natomiast poprosimy o /Index, app.UseStaticFiles()
nie będzie widział takiego pliku więc przekaże zapytanie do app.UseMvcWithDefaultRoute()
- metody która obsługuje MVC oraz nasze Razor Pages.
Razor Pages
Razor Pages to nowa metoda tworzenia aplikacji internetowych w ASP.NET. Pojedyńczy widok łączy się z pojedyńczym plikiem odpowiedzialnym za zachowanie strony.
Uruchom teraz aplikację, a następnie z menu na górze strony wybierz Contact. Jak widzisz na górze widnieje nagłówek Contact, poniżej drugi nagłówek Your contact page. Reszta to informacje kontaktowe. Otwórzmy teraz plik Contact.cs
@page
@model ContactModel
@{
ViewData["Title"] = "Contact";
}
<h2>@ViewData["Title"]</h2>
<h3>@Model.Message</h3>
<address>
One Microsoft Way<br>
Redmond, WA 98052-6399<br>
<abbr title="Phone">P:</abbr>
425.555.0100
</address>
<address>
<strong>Support:</strong><a href="mailto:[email protected]">[email protected]</a><br>
<strong>Marketing:</strong><a href="mailto:[email protected]">[email protected]</a>
</address>
Przypomina zwykły HTML, jednak znajduje się tutaj parę linijek które zawierają w sobie znak @. Wszystko poprzedzone małpą jest interpretowane przez silnik Razor jako zwykły C#, cała reszta jest interpretowana jak zwykły HTML.
@page
- Mówi Razorowi aby traktować tę stronę, jako stronę opartą o Razor Pages (strona taka powinna mieć także plik nazwaStrony.cshtml.cs do którego zaraz przejdziemy).
@model ContactModel
- Przypisuje konkretny model pod zmienną Model (zazwyczaj model nazywa się
NazwaStronyModel
). @{ ... }
- Wszystko w środku tych klamr jest interpretowane jako zwykły C# - tutaj do słownika ViewData w klucz Title wpisujemy wartość Contact.
Jeżeli chcemy wyświetlić wynik operacji z C# wśród znaczników HTML po prostu poprzedzamy nasz kod małpą.
Plik Contact.cshtml.cs to backend strony Contact. To on przechwytuje i obsługuje zapytania HTTP.
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace WebApplication4.Pages
{
public class ContactModel : PageModel
{
public string Message { get; set; }
public void OnGet()
{
Message = "Your contact page.";
}
}
}
Model ten ma tylko jedną właściwość Message, której przy zapytaniu typu GET zostaje przypisana wartość “Your contact page.” (To samo wyświetliło się na stronie w nagłówku!).
Możemy tworzyć wiele metod z tym samym schematem nazywania - OnGet
, OnPost
, OnGetAsync
itd.
Każda następna strona (Index oraz About) jest stworzona w ten sam sposób.
Zajmijmy się teraz 4 “specjalnymi stronami” (te z podłogą na początku):
_ViewStart.cshtml
@{
Layout = "_Layout";
}
Widok ten wykonuje się przed wszytkimi innymi. Domyślnie definiuje on tylko zmienną Layout, która określa jaka strona ma się wykonać przed naszą główną (Zauważ, że w pliku Contact.cshtml nie znajdowały się znaczniki takie jak <head>
).
_ViewImports.cshtml
@using WebApplication4
@namespace WebApplication4.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
Ustala przestrzenie nazw, importuje Tag Helpery. Przykład ich użycia znajduje się w następnym pliku:
_ValidationScriptsPartial.cshtml
<environment include="Development">
<script src="~/lib/jquery-validation/dist/jquery.validate.js"></script>
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"></script>
</environment>
<environment exclude="Development">
<script src="https://ajax.aspnetcdn.com/ajax/jquery.validate/1.14.0/jquery.validate.min.js"
asp-fallback-src="~/lib/jquery-validation/dist/jquery.validate.min.js"
asp-fallback-test="window.jQuery && window.jQuery.validator"
crossorigin="anonymous"
integrity="sha384-Fnqn3nxp3506LP/7Y3j/25BlWeA3PXTyT1l78LjECcPaKCV12TsZP7yyMxOe/G/k">
</script>
<script src="https://ajax.aspnetcdn.com/ajax/jquery.validation.unobtrusive/3.2.6/jquery.validate.unobtrusive.min.js"
asp-fallback-src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"
asp-fallback-test="window.jQuery && window.jQuery.validator && window.jQuery.validator.unobtrusive"
crossorigin="anonymous"
integrity="sha384-JrXK+k53HACyavUKOsL+NkmSesD2P+73eDMrbTtTk0h4RmOF8hF8apPlkp26JlyH">
</script>
</environment>
Plik ten używany jest przez strony na których znajduje się dowolny formularz do walidacji pól korzystając ze standardowej biblioteki jQuery Validator.
Zwróć uwagę na znaczniki. <script>
jest zwykłym znacznikiem z HTMLa, natomiast na pewno nie posiada on domyślnie atrybutu asp-fallback-src
.
Razor oprócz umożliwienia nam korzystania z c# w środku HTMLa dodaje specjalne znaczniki nazywane Tag Helperami, które czynią kod o wiele czytelniejszy i umożliwiają jego lepszą integrację z IDE.
_Layout.cshtml
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>@ViewData["Title"] - WebApplication4</title>
<!-- Wycięte -->
</head>
<body>
<nav class="navbar navbar-inverse navbar-fixed-top">
<!-- Wycięte -->
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li><a asp-page="/About">About</a></li>
<li><a asp-page="/Contact">Contact</a></li>
</ul>
</div>
</nav>
<div class="container body-content">
@RenderBody()
<hr><footer><p>© 2017 - WebApplication4</p></footer>
</div>
@RenderSection("Scripts", required: false)
</body>
</html>
To tutaj znajduje się cały szablon strony.
Metoda @RenderBody
zwraca zawartość konkretnego zasobu który jest wywoływany (np strona Contact).
@RenderSection("Scripts", false)
zwraca sekcję Scripts
, jeżeli została zadeklarowana na danej stronie.
Dodawanie własnej strony
Skoro wiemy już mniej więcej co dzieje się w poszczególnych plikach napiszmy własną stronę, która po wpisaniu w pole tekstowe imienia wyświetlała frazę Witaj {imię}!
.
Zacznijmy od utworzenia nowej strony o nazwie Hello.cshtml.
@page
@model HelloModel
@{
ViewData["Title"] = "Witajka";
}
<h2>@ViewData["Title"]</h2>
<form>
<input type="text" name="name" placeholder="Twoje Imię"/>
<input type="submit" value="Przywitaj mnie!"/>
</form>
@if (!string.IsNullOrEmpty(Model.ReturnValue))
{
<p>@Model.ReturnValue</p>
}
Formularz domyślnie wysyła swoją zawartość metodą GET. Po kliknięciu na przycisk Przywitaj mnie! wartość pola tekstowego o nazwie name zostanie umieszczona w pasku adresu.
Skoro mamy już widok zajmijmy się teraz logiką - Hello.cshtml.cs
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace WebApplication2.Pages
{
public class HelloModel : PageModel
{
public string ReturnValue { get; private set; }
public void OnGet(string name)
{
if (!string.IsNullOrEmpty(name))
ReturnValue = $"Cześć {name}!";
}
}
}
Metoda OnGet może przyjmować parametry - ich nazwa musi zgadzać się z nazwą klucza znajdującego się query stringu.
To wszystko! Jeżeli wejdziesz na adres swojego serwera pod ścieżkę /Hello twoim oczom ukaże się ta strona. Zauważ, że gdy wpiszesz imię i naciśniesz wyślij twoje imię pojawia się w pasku adresu twojej przeglądarki.
Jeżeli chcesz, możesz teraz dodać tę stronę do menu swojej aplikacji.
W tym celu otwórz plik _Layout.cshtml, i tam gdzie znajdują się pozostałe odnośniki do stron dopisz <li><a asp-page="/Hello">Hello</a></li>
Podsumowanie
Wiesz już jak stworzyć prostą aplikację internetową. Na następnych zajęciach omówię jak korzystać z baz danych, aby nasze aplikacje mogły przechowywać pliki, tekst i inne struktury.