Les dues lliçons anteriors van donar a BiblioTech una interfície d'escriptori, primer amb Windows Forms i després amb WPF —totes dues exclusives de Windows i pensades per a un únic usuari executant l'aplicació al seu propi ordinador—. ASP.NET Core canvia radicalment aquest model: en lloc d'una finestra, exposa la lògica de BiblioTech com un servei web, accessible per HTTP des de qualsevol client —un navegador, una aplicació mòbil, un altre servei, o el mateix HttpClient que ja vas fer servir al Mòdul 5 per consumir una API externa—. Aquesta lliçó presenta ASP.NET Core centrant-se en les Minimal APIs, l'estil més modern i directe de definir endpoints HTTP a .NET, i construeix una API real de BiblioTech amb operacions per consultar el catàleg i registrar préstecs.

Contingut

  1. Què és ASP.NET Core i quin problema resol
  2. Minimal APIs davant Controllers
  3. Crear un projecte amb dotnet new webapi
  4. Enrutament i verbs HTTP: app.MapGet/app.MapPost
  5. Injecció de dependències bàsica al contenidor de serveis
  6. Retornar JSON: retomant System.Text.Json del Mòdul 5
  7. Exemple complet: API mínima de BiblioTech amb BibliotecaDbContext
  8. Provar l'API

  1. Què és ASP.NET Core i quin problema resol

ASP.NET Core és el framework de .NET per construir aplicacions i serveis web: des d'APIs que només retornen dades (el focus d'aquesta lliçó) fins a llocs web complets amb pàgines HTML renderitzades al servidor. La seva peça central és el servidor web integrat (Kestrel), que escolta peticions HTTP entrants i les envia al codi C# que les atén:

flowchart LR
    C["Client HTTP<br/>(navegador, app mobil, HttpClient...)"] -->|"GET /libros"| K["Kestrel<br/>(servidor web d'ASP.NET Core)"]
    K --> E["Endpoint C#<br/>app.MapGet(...)"]
    E --> B["Biblioteca<br/>(domini ja existent)"]
    E -->|JSON| K
    K -->|"Resposta HTTP"| C

A diferència de Windows Forms i WPF, una aplicació ASP.NET Core no té finestra ni interfície visual pròpia: s'executa com un procés en segon pla (en un servidor, en un contenidor, o localment durant el desenvolupament) i respon a peticions. Això la fa multiplataforma —s'executa igual a Windows, Linux o macOS— i adequada precisament per al paper que necessitava BiblioTech: un punt central al qual diferents clients (una futura app Blazor, una app MAUI, un tercer) puguin demanar dades del catàleg o registrar préstecs, sense duplicar la lògica de domini en cadascun d'ells.

  1. Minimal APIs davant Controllers

ASP.NET Core ofereix dos estils per definir endpoints HTTP:

Minimal APIs Controllers
Com es defineixen els endpoints Funcions lambda o mètodes registrats directament sobre app Classes Controller amb mètodes decorats amb atributs ([HttpGet], [HttpPost])
Quantitat de codi per a un endpoint simple Mínima: una línia per endpoint Més gran: una classe completa, amb la seva pròpia estructura
Origen històric Introduïdes a .NET 6 com a alternativa més lleugera L'estil original d'ASP.NET Core (i d'ASP.NET MVC, anterior a .NET Core)
Quan convé APIs petites o mitjanes, microserveis, prototips APIs grans amb molts endpoints relacionats, que es beneficien de l'organització en classes

Aquesta lliçó se centra en Minimal APIs per ser l'estil més simple i directe per començar, i perquè encaixa bé amb la mida de l'API de BiblioTech en aquesta etapa del curs. Els Controllers no desapareixen ni queden obsolets: per a una API amb desenes d'endpoints organitzats per recurs, l'estructura en classes dels Controllers pot resultar més còmoda de mantenir a llarg termini —una decisió de disseny, no una jerarquia d'opció "millor i pitjor".

  1. Crear un projecte amb dotnet new webapi

dotnet new webapi -n BiblioTech.Api --use-minimal-apis
cd BiblioTech.Api

El fitxer central d'una Minimal API és Program.cs, on es configura i arrenca tot:

var builder = WebApplication.CreateBuilder(args);

// aqui es registren serveis al contenidor (apartat 5)

var app = builder.Build();

// aqui es defineixen els endpoints (apartat 4)

app.Run();

WebApplication.CreateBuilder(args) prepara un builder amb la configuració per defecte (lectura d'appsettings.json, sistema de logging, etc.); builder.Build() construeix l'aplicació (app) a partir d'aquesta configuració; app.Run() arrenca Kestrel i es queda escoltant peticions fins que el procés s'atura. Tot el codi que registra serveis va abans de Build(); tot el codi que defineix endpoints va després.

  1. Enrutament i verbs HTTP: app.MapGet/app.MapPost

Cada endpoint es registra indicant el verb HTTP, la ruta, i una funció que l'atén:

app.MapGet("/libros", () =>
{
    return new List<string> { "Rayuela", "Ficciones" }; // simplificat, es completa a l'apartat 7
});

app.MapGet("/libros/{isbn}", (string isbn) =>
{
    // {isbn} a la ruta es vincula automaticament al parametre "isbn" de la funcio
    return $"Cercant el llibre amb ISBN {isbn}";
});

app.MapPost("/prestamos", (string isbn, int idSoci) =>
{
    return $"Prestec registrat: ISBN {isbn}, soci {idSoci}";
});
Verb HTTP Mètode d'ASP.NET Core Ús típic
GET app.MapGet(ruta, funcio) Consultar dades, sense modificar res al servidor
POST app.MapPost(ruta, funcio) Crear alguna cosa nova (aquí, un préstec)
PUT app.MapPut(ruta, funcio) Reemplaçar un recurs existent
DELETE app.MapDelete(ruta, funcio) Eliminar un recurs

{isbn} dins de la ruta és un paràmetre de ruta: ASP.NET Core extreu automàticament aquest segment de la URL (/libros/978-84-376-0495-4) i el passa com a argument string isbn a la funció, amb conversió de tipus automàtica si el paràmetre fos, per exemple, int. Aquest mecanisme d'enllaç de paràmetres (binding) és anàleg, en esperit, al data binding de WPF: tots dos eviten haver d'extreure i convertir dades manualment.

  1. Injecció de dependències bàsica al contenidor de serveis

ASP.NET Core porta integrat un contenidor d'injecció de dependències: un registre central de serveis (builder.Services) del qual l'aplicació obté instàncies, en lloc que cada endpoint construeixi les seves pròpies dependències amb new:

var builder = WebApplication.CreateBuilder(args);

// Registra Biblioteca com a servei unic compartit durant tota la vida de l'aplicacio
builder.Services.AddSingleton<Biblioteca>();

var app = builder.Build();

app.MapGet("/libros", (Biblioteca biblioteca) =>
{
    // "biblioteca" es rep ja construida: ASP.NET Core la resol del contenidor automaticament
    return biblioteca.Cataleg;
});

AddSingleton<Biblioteca>() li diu al contenidor: "crea una única instància de Biblioteca i reutilitza-la a cada petició que la necessiti". N'hi ha prou amb declarar Biblioteca biblioteca com a paràmetre de la funció de l'endpoint perquè ASP.NET Core la resolgui i la injecti automàticament, sense que l'endpoint hagi de saber com s'ha construït. Aquest mecanisme —injecció de dependències— és un pilar central del disseny d'aplicacions .NET modernes, i s'estudiarà en profunditat al Mòdul 8 (Bones Pràctiques i Patrons de Disseny); aquí n'hi ha prou amb reconèixer el patró bàsic: registrar a builder.Services, rebre com a paràmetre a l'endpoint.

  1. Retornar JSON: retomant System.Text.Json del Mòdul 5

Quan un endpoint retorna un objecte o una col·lecció (com biblioteca.Cataleg a l'exemple anterior), ASP.NET Core el serialitza automàticament a JSON fent servir System.Text.Json —la mateixa llibreria ja usada al Mòdul 5 amb JsonSerializer.Serialize— sense que l'endpoint l'hagi de cridar explícitament:

app.MapGet("/libros", (Biblioteca biblioteca) =>
{
    return biblioteca.Cataleg; // ASP.NET Core serialitza aquesta llista a JSON automaticament
});
[
  { "titol": "Rayuela", "autor": "Julio Cortazar", "disponible": true },
  { "titol": "Ficciones", "autor": "Jorge Luis Borges", "disponible": true }
]

Per defecte, ASP.NET Core fa servir camelCase per a les claus del JSON de sortida (titol, no Titol), la mateixa convenció que es va veure amb JsonNamingPolicy.CamelCase al Mòdul 5 —aquí aplicada automàticament pel framework, sense configuració addicional—. Si l'endpoint necessita control explícit sobre el codi d'estat HTTP de la resposta, pot retornar un IResult amb Results.Ok(...), Results.NotFound() o Results.BadRequest(...) en lloc de retornar l'objecte directament, com es veu a l'apartat 7.

  1. Exemple complet: API mínima de BiblioTech amb BibliotecaDbContext

Unint les peces anteriors amb la persistència en base de dades ja construïda al Mòdul 5 (BibliotecaDbContext, amb Entity Framework Core), una API mínima realista per a BiblioTech:

// Program.cs
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddDbContext<BibliotecaDbContext>(opcions =>
    opcions.UseSqlite("Data Source=bibliotech.db"));

var app = builder.Build();

app.MapGet("/libros", async (BibliotecaDbContext context) =>
{
    List<Llibre> llibres = await context.Llibres.ToListAsync(); // EF Core, Modul 5
    return Results.Ok(llibres);
});

app.MapGet("/libros/{isbn}", async (string isbn, BibliotecaDbContext context) =>
{
    Llibre? llibre = await context.Llibres.FirstOrDefaultAsync(l => l.Isbn == isbn);

    if (llibre is null)
    {
        return Results.NotFound($"No existeix cap llibre amb ISBN {isbn}.");
    }

    return Results.Ok(llibre);
});

app.MapPost("/prestamos", async (SollicitudPrestec sollicitud, BibliotecaDbContext context) =>
{
    Llibre? llibre = await context.Llibres.FirstOrDefaultAsync(l => l.Isbn == sollicitud.Isbn);
    Soci? soci = await context.Socis.FindAsync(sollicitud.IdSoci);

    if (llibre is null || soci is null)
    {
        return Results.BadRequest("ISBN o soci no trobats.");
    }

    if (!llibre.Disponible)
    {
        return Results.BadRequest($"'{llibre.Titol}' ja esta prestat.");
    }

    llibre.Prestar(); // logica de domini ja existent, Modul 2
    Prestec prestec = new Prestec(llibre, soci);
    context.Prestecs.Add(prestec);
    await context.SaveChangesAsync(); // EF Core, Modul 5

    return Results.Created($"/prestamos/{prestec.Id}", prestec);
});

app.Run();
// SollicitudPrestec.cs: un DTO (Data Transfer Object) senzill per al cos del POST
record SollicitudPrestec(string Isbn, int IdSoci);

Cap endpoint reimplementa la lògica de domini: GET /libros i GET /libros/{isbn} només consulten BibliotecaDbContext (ja construït al Mòdul 5), i POST /prestamos crida llibre.Prestar() exactament igual que ho faria la consola o el formulari de Windows Forms. L'API és, de nou, una capa d'accés nova sobre el mateix domini, ara accessible per HTTP en lloc de per teclat o clic de ratolí. SollicitudPrestec és un record (Mòdul 3) que modela el cos JSON esperat al POST; ASP.NET Core el deserialitza automàticament des del cos de la petició, sense codi addicional, aplicant el mateix mecanisme de binding de l'apartat 4.

  1. Provar l'API

Amb el projecte en execució (dotnet run), ASP.NET Core exposa típicament una interfície de prova interactiva (Swagger/OpenAPI) en una URL com https://localhost:5001/swagger, a més de poder-se provar directament amb curl o amb HttpClient (Mòdul 5):

curl https://localhost:5001/libros

curl -X POST https://localhost:5001/prestamos \
  -H "Content-Type: application/json" \
  -d '{"isbn": "978-84-376-0495-4", "idSoci": 1}'

-H "Content-Type: application/json" indica al servidor que el cos de la petició (-d) és JSON, perquè ASP.NET Core el deserialitzi correctament al paràmetre SollicitudPrestec de l'endpoint POST /prestamos.

Errors Comuns i Consells

  • Registrar BibliotecaDbContext amb AddSingleton en lloc d'AddDbContext: un DbContext no està pensat per compartir-se com a instància única entre peticions concurrents; AddDbContext (vist aquí) gestiona correctament crear una instància nova per petició.
  • Reimplementar la comprovació de Disponible dins de l'endpoint en lloc de delegar-la a llibre.Prestar(): duplicaria lògica ja resolta al domini des del Mòdul 2, amb risc de desincronitzar-se amb el temps.
  • Confondre Minimal APIs amb "sense injecció de dependències": Minimal APIs fa servir el mateix contenidor de serveis que els Controllers; només canvia com es registren els endpoints, no com funciona la injecció de dependències per sota.
  • Consell: per a una API petita com aquesta, Minimal APIs manté tot l'enrutament visible a Program.cs; si el nombre d'endpoints creixés molt, convé agrupar-los en fitxers separats amb app.MapGroup(...) per no acabar amb un únic fitxer enorme.

Exercicis

  1. Afegeix un endpoint GET /socios/{id} que retorni el Soci corresponent (amb Results.Ok) o Results.NotFound si no existeix cap soci amb aquest id.

  2. Afegeix un endpoint POST /devoluciones que rebi un record SollicitudDevolucio(int IdPrestec), busqui el Prestec corresponent, cridi RegistrarDevolucio() (Mòdul 2) i desi els canvis amb SaveChangesAsync(). Retorna Results.NotFound si el préstec no existeix.

Solucions

app.MapGet("/socios/{id}", async (int id, BibliotecaDbContext context) =>
{
    Soci? soci = await context.Socis.FindAsync(id);

    if (soci is null)
    {
        return Results.NotFound($"No existeix cap soci amb id {id}.");
    }

    return Results.Ok(soci);
});
record SollicitudDevolucio(int IdPrestec);

app.MapPost("/devoluciones", async (SollicitudDevolucio sollicitud, BibliotecaDbContext context) =>
{
    Prestec? prestec = await context.Prestecs.FindAsync(sollicitud.IdPrestec);

    if (prestec is null)
    {
        return Results.NotFound($"No existeix cap prestec amb id {sollicitud.IdPrestec}.");
    }

    prestec.RegistrarDevolucio(); // Modul 2
    await context.SaveChangesAsync();

    return Results.Ok(prestec);
});

Conclusió

En aquesta lliçó BiblioTech ha deixat de ser una aplicació d'un únic usuari i un únic ordinador: amb ASP.NET Core i Minimal APIs, la seva lògica de domini —sense cap canvi— queda accessible per HTTP mitjançant endpoints GET/POST, amb injecció de dependències bàsica per obtenir BibliotecaDbContext a cada petició i serialització JSON automàtica de les respostes.

La lliçó següent presenta Blazor, que retoma directament aquesta API (o, segons el model triat, es connecta al domini de forma encara més directa) per construir una interfície d'usuari web amb C# en lloc de JavaScript —tancant el cercle entre el data binding ja vist a WPF i el món del navegador.

Curs de Programació en C#

Mòdul 1: Introducció al C#

Mòdul 2: Estructures de Control

Mòdul 3: Programació Orientada a Objectes

Mòdul 4: Conceptes Avançats de C#

Mòdul 5: Treballant amb Dades

Mòdul 6: Temes Avançats

Mòdul 7: Construcció d'Aplicacions

Mòdul 8: Bones Pràctiques i Patrons de Disseny

Mòdul 9: Projecte Final

© Copyright 2026. Tots els drets reservats