Wprowadzenie do aplikacji internetowych

2018-01-08 Junior .NET Poradniki

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.

comments powered by Disqus