This is an old revision of the document!
In laboratorul precedent am vazut cum se poate lucra cu baza de date prin ORM, acum ca un clientul web ajunga la datele de pe server si sa consume API-ul acestuia vom introduce in acest laborator conceptele de controller si serviciu.
Cand ruleaza aplicatia se creaza serverul HTTP, ca orice aplicatie peste HTTP trebuie sa avem declarate endpoint-urile/rutele unde se expune API-ul. Astfel, primele componete invatate aici sunt controller-ele, acestea sunt clase speciale a caror metode publice sunt apelate la accesul rutelor corespunzatoare metodei. Pentru ca framework-ul sa identifice controller-ele si rutele, se decoreaza clasa si metodele cu atribute, de exemplu [ApiController] specifica fremawork-ului ca acesta clasa trebuie sa fie folosita ca controller iar [Route(“api/[controller]”)] pus pe clasa si [HttpGet(“my-route”)] pe metoda specifica ca atunci cand se aceseaza ruta “/api/<nume_clasa_controller>/my-route” cu un HTTP GET sa se apeleze acea metoda in cauza.
Decorarea claselor si metodelor cu attribute, sau in Java cu adnotari, pentru ca acestea sau dobandeasca mai multe functionalitati, la runtime sau compiletime, se numeste AOP (Aspect-Oriented-Programming).
In cereri HTTP datele transmise catre server pot fi transferate in mai multe locatii din cerere care pot fi extrase si pasate automat ca parametri pentru metoda din controller corespunzatoare rutei. Aceste locatii se specifica folosind atribute in fata parametrilor in urmatoarele moduri:
Rutele apelate din backend o sa raspunda cu un obiect care o sa fie automat serializat intr-un raspuns HTTP ca JSON.
Puteti urmari in Gitlab codul pentru controllere cu explicatii si exemple de cum se pot folosi informatiile din cerere pentru actiunile din backend.
De obicei logica aplicatiei nu se afla in controllere, motivul fiind ca este bine sa fie segregate componentele pe functionalitati, o componenta care are multe responsabilitati o sa fie greu de intretinut si de inteles de catre alti dezvoltatori. Astfel, controller-ele trebuie doar sa trateze cererile de la client si eventual sa formateze corespunzator raspunsul trimis inapoi, logica aplicatiei poate fi segregate apoi in servicii.
Ca particularitate importanta in C# fata de alte limbaje este ca in C# exista metode de extensie, acestea sunt artificii de compilator care adauga metode noi la clase deja existente si pot fi apelate obiectual ca oricare alte metode non-statice. Metodele de extensie sunt folositoare pentru a extinde functionalitate pentru cod deja existent.
Mai jos aveti exemple de metode de extensii pentru clasa WebApplicationBuilder, practic la WebApplicationBuilder se adauga metoda UseLogger() pentru a adauga un logger mai complex la acest builder. Cuvantul cheie “this” la primul parametru in metoda asta statica din clasa aceasta statica specifica pentru compilator sa fie folosit ca si cand primul parametru este “this” iar metoda este o metoda non-statica a acestuia.
public static class WebApplicationBuilderExtensions { public static WebApplicationBuilder UseLogger(this WebApplicationBuilder builder) { builder.Host.UseSerilog((_, logger) => { logger .MinimumLevel.Is(LogEventLevel.Information) .MinimumLevel.Override("Microsoft", LogEventLevel.Warning) .MinimumLevel.Override("System", LogEventLevel.Warning) .MinimumLevel.Override("Microsoft.Hosting.Lifetime", LogEventLevel.Information) .Enrich.FromLogContext() .Enrich.WithMachineName() .Enrich.WithProcessId() .Enrich.WithProcessName() .Enrich.WithThreadId() .WriteTo.Console(); }); return builder; } }
Un caz particular pentru utilitatea metodelor de extensie sunt operatiile de LINQ pe colectii. Mai inainte s-a vazut cum se pot inlantui mai multe expresii functionale pentru a extrage informatii din baza de date, metodele respective sunt aceleasi pentru toate implementarile de context de baza de date, ele sunt metode de extensie pentru “this” IEnumerable. Acestea doar apeleaza metodele publice ale acestei interfete, interfata care este implementata de colectii clasice din biblioteca standard a limbajului, adica aceleasi metode pot fi folosite si pentru a face prelucrari peste liste de exemplu si aventajeaza programatorul prin a folosi un nivel de abstractizare ridicat pentru contexte diferite.
Alta particularitate fata de limbaje cum e Java, puteti urmarii si in codul laboratorului, este ca in C# pe langa metode si campuri clasele pot avea proprietati care sunt campuri cu getter si setter, e preferabil sa fie folosite proprietati in locul campurilor daca acestea trebuie sa fie publice pentru ca acestea pot abastractiza si valori care se calculeaza pe loc si dar fara sa fie explicit apelata o metoda.
O ultima particularitate care face ca C# sa semene si cu limbaje ca Kotlin este suportul pentru programare asincrona. Daca urmariti metodele din codul pentru laborator veti observa ca majoritatea returneaza un Task si au cuvantul cheie “async”, aceste metode sunt preluate de thread-urile aplicatiei si executate asincron, adica nu se stie cand anume sunt executate, sunt executate cand sunt planificate de framework pentru a optimiza executia lor, mai multe task-uri se pot executa pe acelasi thread iar intr-o functie “async” se poate face “await” pe alt task ca sa se suspende executia task-ului current ca sa fie asteptata executia task-ului la care se face await.