This is an old revision of the document!
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.
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.
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.
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();
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.
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.
.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:
Pentru a folosi dependency injection in .NET este nevoie de 2 lucruri:
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")); }); } //... } }
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"))); }
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(); } } }
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>(); } } }
<link rel="stylesheet" type="text/css" href="mystylesheet.css">
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>
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 |
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 |
CSS (Cascading Style Sheets) este un limbaj de stilizare al elementelor HTML.
Formatul unei reguli CSS este urmatorul: