This is an old revision of the document!


Laboratorul 06: Structura Backend. Api

Scopul Laboratorului

Acest laborator este o continuare a laboratorului precedent si va finaliza structura proiectului. Vom discuta despre cum am structurat partea de Api a proiectului si vom prezenta detalii despre tehnologiile utilizate, atat din acest laborator, cat si din cel precedent.

Cuvant inainte

Va recomandam sa aveti deschis tutorialul oficial de la microsoft pentru .NET 6 minimal API.

In plus, va recomandam sa utilizati canalul de Teams “Discutii Laborator” pentru orice intrebare referitoare la laborator.

Organizarea Codului API

Api-ul este al 3-lea proiect creat in cadrul solutiei noastre. Rolul sau este sa expuna rute catre exterior si sa foloseasca elementele de domeniu (core) si infrastructura pentru a realiza actiuni.

In general, cand se urmareste arhitectura Clean, in proiectul de API trebuie scrise configuratiile de server (printre care si dependency injection), middlewares si rute.

Modul in care noi am organizat codul nu reflecta o strategie generala, ci doar o parere personala.

Program.cs

Incepand cu .NET 6, Program.cs este punctul de acces si configurare al unui server. Pana in .NET 6, Program.cs era folosit ca punct de acces si Startup.cs servea rol de fisier de configurare.

using BookLibrary.Api.Authorization;
using BookLibrary.Api.Infrastructure;
using BookLibrary.Api.Web;
using MediatR;
using System.Reflection;
 
var builder = WebApplication.CreateBuilder(args);
 
builder.Services.AddControllers(options =>
{
    options.Filters.Add<ApiResponseExceptionFilter>();
});
 
builder.Services.AddEndpointsApiExplorer();
 
// Add Swagger with Bearer Configuration
builder.Services.AddSwaggerWithBearerConfig();
 
// Add Authentication configuration
builder.AddAuthenticationAndAuthorization();
 
// Add Database Context
builder.AddBookLibraryDbContext();
 
// Add MediatR
builder.Services.AddMediatR(Assembly.GetExecutingAssembly());
 
// Add Repositories
builder.Services.AddBookLibraryAggregateRepositories();
 
// Add Api Features Handlers
builder.Services.AddApiFeaturesHandlers();
 
var app = builder.Build();
 
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
} else
{
    app.UseHttpsRedirection();
}
 
app.UseAuthenticationAndAuthorization();
 
app.UseHttpLogging();
 
app.MapControllers();
 
app.Run();

In Visual Studio, puneti cursorul peste builder. O sa vedeti ca este un obiect de tipul WebApplicationBuilder.

Obiectul builder este folosit pentru configurarea serverului. Configurarea serverului implica injectarea obiectelor necesare la runtime. Un exemplu este:

builder.Services.AddEndpointsApiExplorer();

Secventa de cod de mai sus face posibila descoperirea ruterlor scrise de noi de catre server.

In Visual Studio, puneti cursorul peste app. O sa vedeti ca este un obiect de tipul WebApplication.

Obiectul app este folosit pentru adaugarea middlewares care sunt executate in momentul in care serverul primeste un request. Imaginati-va middlewares ca o serie de functii care sunt apelate una dupa alta.

app.UseHttpLogging();

Secventa de cod de mai introduce logging de request la fieacare request, sub forma unui middleware.

Dependency Injection

.NET foloseste foarte mult dependency injection. Si obiectul builder, prin apelarea acelor metode, de fapt introduce obiecte in containerul de depdendente folosind dependency injection.

Dependency Injection, pe scurt, se refera la injectarea obiectelor de care este nevoie, fara sa fie nevoie sa fie construite pe loc.

Propunem urmatorul scenariu: Obiectul A foloseste Obiectul B. Acest lucru se poate face in doua moduri:

  1. Obiectul A instantiaza Obiectul B
  2. Obiectul A obtine Obiectul B din containerul de dependente, direct instantiat

Scenariul 2 se refera la Dependency Injection. In loc ca obiectele de care se depinde, sa fie instantiate, ele sunt direct preluate din containerul de depenente.

Pentru a folosi dependency injection in .NET este nevoie de 2 lucruri:

  1. Obiectele trebuie injectate in containerul de dependente.
  2. Cand este nevoie de un obiect, acesta trebuie mentionat in constructorul obiectului parinte.

Containerul de dependente implicit din .NET este accesat prin obiectul IServiceCollection, obiect copil al WebApplicationBuilder.

Obiectele care sunt injectate, pot fi injectate in 3 cicluri de viata: transient, scoped si singletone.

Chiar daca acum pare putin ambiguu, o sa vedeti exemple concrete in cod.

Metode Extensie

Alt principiu folosit in .NET, in special pentru configurari, este Metoda extensie.

Metodele extensie reprezinta o metoda care este scrisa in numele altui obiect. Exemplu:

using Microsoft.AspNetCore.Authentication.JwtBearer;
 
namespace BookLibrary.Api.Authorization
{
    public static class AuthorizationExtensions
    {
        public static void AddAuthenticationAndAuthorization(this WebApplicationBuilder builder)
        {
            builder.Services.AddAuthentication(options =>
            {
                options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            }).AddJwtBearer(options =>
            {
                options.Authority = builder.Configuration["Auth:Authority"];
                options.Audience = builder.Configuration["Auth:Audience"];
            });
 
            // Add Authorization configuration
            builder.Services.AddAuthorization(configure =>
            {
                configure.AddPolicy("AdminAccess", policy => policy.RequireClaim("permissions", "book-library:admin"));
            });
        }
 
        //...
    }
}

Observati cum singurul parametru al acestei metode este WebApplicationBuilder builder, care este prefixat cu this. Prefixul cu this marcheaza faptul ca aceasta metoda este una de extensie pentru obiectul de tipulWebApplicationBuilder.

Utilizarea metodelor de extensie ajuta prin imbogatirea functionalitatilor unui obiect la care nu exista acces. In exemplul de mai sus, am adaugat functionalitate obiectului builder, pentru a putea configura aplicatia.

In cadrul proiectului nostru, metodele de extensie sunt grupate in functie de scopul lor:

Folderul Authorization contine metode de extensie pentru configurarea autentificarii si autorizarii la nivel de API, precum si pentru a extrage informatii din utilizatorul autentificat.

Folderul Infrastructure contine metode de extensie pentru dependency injection a repository-urilor definite in Core (sub forma de interfete) si implementate in Infrastructture.

public static void AddBookLibraryAggregateRepositories(this IServiceCollection services)
{
    services.AddTransient<IBooksRepository, BooksRepository>();
    services.AddTransient<IUsersRentalsRepository, UsersRentalsRepository>();
}

Pe langa injectarea repository-urilor, am configurat si contextul bazei de date, instruind serverul sa foloseasca Microsoft SQL Server cu configuratia scrisa de noi in appsettings.json.

public static void AddBookLibraryDbContext(this WebApplicationBuilder builder)
{
    builder.Services.AddDbContext<BookLibraryContext>(opt =>
        opt.UseSqlServer(builder.Configuration.GetConnectionString("BookLibraryDb")));
}

Reminder: Acest context a fost folosit in repository-uri, in cadrul proiectului Infrastructure. Motivul pentru care a putut fi folosit este ca a fost configurat si injectat aici.

public class BooksRepository : IBooksRepository
{
    private readonly BookLibraryContext context;
 
    public BooksRepository(BookLibraryContext context)
    {
        this.context = context;
    }
    // ...
}

Folderul Web contine metode de extensie pentru Swagger si features scrise de noi, o exceptie custom si un filtru de exceptie pentru exceptia noastra.

using BookLibrary.Api.Features.Books;
using BookLibrary.Api.Features.Metrics;
using BookLibrary.Api.Features.Profile;
using BookLibrary.Api.Features.Rentals;
 
namespace BookLibrary.Api.Web
{
    public static class ApiFeaturesExtensions
    {
        public static void AddApiFeaturesHandlers(this IServiceCollection services)
        {
            // Add Book Handlers
            services.AddBooksHandlers();
 
            // Add Profile Handlers
            services.AddProfilesHandlers();
 
            // Add Rentals Handlers
            services.AddRentalsHandlers();
 
            // Add Metrics Handlers
            services.AddMetricsHandlers();
        }
    }
}

In metoda de mai sus am instruit obiectul services sa apeleze metodele de extensie pentru fiecare feature scris in parte. Aceste metode de extensie sunt definite in cadrul fiecarui feature.

Exemplu:

using BookLibrary.Api.Features.Books.AddBook;
using BookLibrary.Api.Features.Books.ViewAllBooks;
using BookLibrary.Api.Features.Books.ViewStatusAboutBook;
 
namespace BookLibrary.Api.Features.Books
{
    internal static class BooksModule
    {
        internal static void AddBooksHandlers(this IServiceCollection services)
        {
            services.AddTransient<IAddBookCommandHandler, AddBookCommandHandler>();
            services.AddTransient<IViewAllBooksQueryHandler, ViewAllBooksQueryHandler>();
            services.AddTransient<IViewStatusAboutBookQueryHandler, ViewStatusAboutBookQueryHandler>();
        }
    }
}

Informatii generale

  • Hypertext Markup Language a fost propus in 1989 de Tim Berners-Lee, pentru organizatia stiintifica CERN (Elvetia)
  • HTML descrie structura unei pagini Web. Elementele HTML sunt reprezentate cu tag-uri
  • Browser-ul nu va afisa tagurile HTML, dar le va utiliza in vederea afisarii continutului paginii
  • Cascading Style Sheet este maniera preferata prin care atasam proprietati grafice de randare elementelor din DOM
  • Putem include declarații CSS în 3 moduri: inline, intern (in <head>) sau extern (versiunea recomandata)
  • In cazul unui fisier separat, acesta trebuie legat in head prin tagul:
     <link rel="stylesheet" type="text/css" href="mystylesheet.css"> 

Structura HTML

Pentru crearea unei pagini cu textul “Hello World” cu un link spre Google, vom scrie:

<!DOCTYPE html>
<html>
  <head>
    <title>Titlu</title>
  </head>
  <body>
  <a href="https://www.google.com">Hello World!</a>
  </body>
</html>

Explicatii:

  • Declaratia DOCTYPE este prima linie din fisierul HTML si specifica versiunea HTML in care a fost scrisa pagina
  • Minimul necesar pentru un document valid consta intr-un tag title; cu toate acestea, recomandarea este ca documentul sa contina cel putin un tag html, cu un singur head, un title si un body
  • href este un atribut si aduce informatii aditionale despre elementul HTML caruia ii este asignat
  • Ca structura, atributele vin in perechi cheie/valoare, precum: name=”value”, asa cum se poate observa in exemplul de sus

Tag-uri HTML uzuale

Pentru introducere text
article Articol (tag specific HTML5)
p Paragraf
h1-6 Titlu sectiune, ierarhizat pe 6 niveluri
a Link catre o alta adresa, sau un element din acelasi document, specificate in atributul href; poate contine atributul target care specifica modul in care se va deschide noua adresa (i.e. inlocuind pagina deschisa curenta, sau creand o alta noua)
ol, ul Lista ordonata, respectiv ne-ordonata; ambele contin enumerari de taguri li
dl Lista de grupuri nume-valoare (sau definitii), unde dt specifica numele si este urmat de dd care specifica valoarea
pre, code Afiseaza text pre-formatat, respectiv text care reprezinta cod intr-un limbaj de programare; aceste tag-uri indica clientului sa nu incerce sa formateze sau sa evalueze posibilele expresii din continutul tag-urilor, ci sa le afiseze asa cum sunt
Pentru crearea tabelelor
table Tabel nou
tr Table row; linie noua (poate fi continuta in thead, tbody sau tfoot)
td Table data; celula noua
th Table head; celula din capul de tabel
thead Cap de tabel
tbody Continut tabel
tfoot Subsol tabel
colgroup Grup de coloane folosite pentru a crea diviziuni intr-un tabel; contine tag-uri de tipul col si este inclus direct in tag-ul table
Formulare
form Formular; contine un atribut action si un atribut method specificand parametrii cererii HTTP ce se va genera in urma trimiterii datelor din formular
input Camp de interactiune cu utilizatorul; contine un atribut name prin care se specifica denumirea parametrului ce va fi trimis catre server si type prin care se specifica tipul campului
select Camp de selectie a unei valori dintr-o lista de tag-uri option
label Text afisat in dreptul unui input; contine un atribut for pentru a specifica input-ul corespunzator
fieldset Subdiviziune a unui formular; poate contine legend pentru a specifica titlul subdiviziunii
button Buton generic; preferat unui input de tipul button
textarea Camp pentru introducerea unui text extins

Tipul unui input poate fi:

  • text, password - contine text, respectiv text mascat pentru introducerea de parole
  • radio, checkbox - contine o optiune da/nu, exclusiva, respectiv non-exclusiva
  • file - se foloseste pentru incarcarea de fisiere
  • hidden - se foloseste pentru a trimite un parametru fara a fi vizualizat de utilizator
  • button,submit, reset - reprezinta buton generic, respectiv special pentru trimiterea sau golirea formularului

Tag-uri media
img Afiseaza o imagine; sunt necesare atributele src, pentru a preciza URL-ul imaginii, si alt pentru a preciza textul afisat in lipsa imaginii
video Afiseaza un film; este necesar atributul src, pentru a preciza sursa video; continutul tag-ului poate contine o maniera alternativa de a afisa filmul, in caz ca tag-ul nu este suportat de navigator
Sectiuni
div, span Sectiuni dintr-un document HTML, fara o semantica speciala, cu rolul formatarii si pozitionarii selective
header, nav, section, aside, footer Sectiuni specifice HTML5 pentru atribuirea de semantica diverselor sectiuni tipice dintr-un document
Alte tag-uri
script Permite introducerea unui script rulat pe client, intr-un limbaj suportat; poate aparea in head sau in body
link Permite definirea unei legaturi intre document si o sursa externa, fiind utilizat si pentru atribuirea de caracteristici CSS
meta Permite introducerea de informatii despre document (e.g. setul de caracter utilizat, cuvinte cheie, scurta descriere, etc.)
iframe Permite introducerea unui alt document HTML intr-o sectiune a documentului curent; noul document se poate afla pe un alt server

Pentru a vizualiza lista completa a tag-urilor HTML, accesati w3schools

CSS

CSS (Cascading Style Sheets) este un limbaj de stilizare al elementelor HTML.

Formatul unei reguli CSS este urmatorul:

  • Selectorul face referire la elementul HTML peste care se aplica stilul
  • Selectorul poate fi id, nume de clasa sau nume de tag
  • Blocul “declaration” contine una sau mai multe perechi proprietate-valoare separate prin ”;”
  • Fiecare “declaration” include o proprietate si o valoare, separate prin ”:”
  • Blocurile “declaration” incep si se incheie prin acolade

Selectorul id (#)

  • Faciliteaza selectarea unui element HTML specific
  • Id-ului unui element trebuie sa fie unic intr-o pagina si identifica un unic element din aceasta
  • Se utilizeaza “#” inaintea numelui selectorului pentru a marca faptul ca acesta vizeaza un element cu id-ul dat
  • Id nu poate incepe cu un numar

Selectorul class (.)

  • Faciliteaza selectarea mai multor elemente avand ca atribut o anumita clasa
  • Se utilizeaza “.” inaintea numelui selectorului pentru a selecta elemente dintr-o anumita clasa
  • Numele clasei nu poate incepe cu un numar

Selectorii pot fi grupati:

h1, .myClass, #myId {
    text-align: center;
    color: red;
}

Combinarea selectorilor

/* reuniune */
h1, h2 {
  /* stilul se aplica elementelor h1 si h2 */
}

/* intersectie */
h1.titlu {
  /* stilul se aplica elementelor h1 care au clasa titlu */
}

/* descendenta */
.antet h1 {
  /* stilul se aplica elementelor h1 care au parinte un element cu clasa antet */
}

/* descendenta directa */
.antet > h1 {
  /* stilul se aplica elementelor h1 care au parinte **direct** un element cu clasa antet */
}

Daca doua sau mai multe reguli se aplica unui singur element, clientul va alege regula cea mai specifica.

Exercitii

Avand sablonul de mai jos, creati o pagina HTML specifica unui articol de blog (aveti libertatea de a alege tema blogului):

  1. [2p] Introduceti in sectiunea #main, datele specifice unui articol: titlul, descrierea articolului, insertii media (o imagine & un video) si doua comentarii text. Folositi taguri HTML5. Includeti in <head> meta-date (data publicarii, cuvinte cheie).
  2. [2p] Creati in sectiunea #form, un formular in care utilizatorii pot lasa feedback:
    • Text input selector (dropdown): pentru gradul de aprobare al articolului (foarte bine, bine, neutru, satisfacator si nesatisfacator);
    • Checkbox-uri pentru acceptarea termenilor & notificare email;
    • Optiunea tip text (textarea), pentru comentarii;
  3. [1p] Adaugati in sectiunea #footer copyright-ul si datele de contact
  4. [1p] Adaugati in sectiunea #nav un tabel cu linkuri catre blog-uri asemanatoare (legaturi catre diverse pagini web).

  Creati un fisier .css in care definiti (conform sablonului) design-ul articolului vostru si atasati-l documentului HTML:

  1. [0.5p] Intrati pe Google Fonts si alegeti minim 2 fonturi diferite pentru sectiunile prezente in sablon (#container, #header, #main, #nav etc):
  2. [1p] Modificati pagina web astfel incat sectiunile sa respecte alinierea, culorile, dimensiunea (800px pentru #container etc.)
  3. [1p] Stilizati tabelul din #nav astfel incat liniile pare sa fie colorate cu gri, iar cele impare cu verde. Nu folositi selectorii class sau id pentru acest exercitiu (hint: even and odd rules).
  4. [1p] Cu ajutorul FontAwesome, adaugati imagini pentru fiecare intrare din tabelul din #nav
  5. [0.5p] Validati pagina HTML cu W3Validator si reparati eventualele erori/warning-uri.
pw/laboratoare/06.1651051126.txt.gz · Last modified: 2022/04/27 12:18 by alexandru.hogea
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0