El mòdul 9 es va tancar amb una constatació incòmoda: Papyrus funciona, està provat i té xarxa de seguretat, però viu en un sol ordinador. Si en Lluís vol saber quants exemplars de Faust queden, ha de trucar a l'Anna; si la Júlia vol reservar Hamlet, ha d'acostar-se a la botiga. La solució té nom des de fa trenta anys: convertir Papyrus en una aplicació web, un programa que corre en un servidor i al qual qualsevol accedeix des d'un navegador o un mòbil. Abans d'escriure la primera línia de Flask (això arriba a la propera lliçó), necessites entendre el terreny de joc: com conversen un navegador i un servidor, què és HTTP, què hi pinta l'HTML en tot això i què et regala un framework. Aquesta lliçó és el mapa; les quatre següents, el viatge.

Contingut

  1. El model client-servidor: qui demana i qui respon
  2. HTTP: el protocol de la conversa
  3. Mètodes HTTP: els verbs de la web
  4. Codis d'estat: el semàfor de les respostes
  5. Anatomia d'una URL
  6. HTML: el mínim imprescindible per a aquest mòdul
  7. Frontend i backend: dos mons, una frontera
  8. JSON: l'idioma de les APIs (vell conegut)
  9. Què fa un framework per tu
  10. El panorama Python: Flask, Django i FastAPI

El model client-servidor: qui demana i qui respon

Tota la web se sosté sobre una idea simple: hi ha programes que demanen (clients) i programes que responen (servidors). El navegador d'en Lluís és un client; l'ordinador on correrà Papyrus serà un servidor. La conversa sempre la inicia el client — el servidor mai no truca a ningú per iniciativa pròpia, només espera peticions i les contesta.

sequenceDiagram
    participant N as Navegador de Lluís (client)
    participant S as Servidor de Papyrus
    N->>S: GET /cataleg  (petició HTTP)
    Note over S: Executa codi Python:<br/>carregar_cataleg(), muntar la pàgina
    S->>N: 200 OK + HTML del catàleg (resposta)
    Note over N: El navegador dibuixa la pàgina
    N->>S: GET /llibre/Faust
    S->>N: 200 OK + fitxa de Faust (21.00 €, estoc 10)

Fixa't en el repartiment de papers:

  • El client (navegador, app mòbil, o fins i tot un script Python amb requests) construeix una petició, l'envia i espera.
  • El servidor rep la petició, executa codi — en el nostre cas, codi Python que ja tens escrit: cercar_llibre(), vendre(), carregar_cataleg() — i retorna una resposta.
  • Cada intercanvi és independent: petició → resposta, i la connexió conceptualment s'acaba. Aquesta amnèsia del servidor (se'n diu sense estat, i hi tornarem a 10-03) és el que permet que milers de clients parlin amb el mateix servidor sense que aquest hagi de recordar cadascú.

La conseqüència pràctica per a Papyrus: el paquet papyrus/ que vas construir a M3–M9 no canvia. El que hi afegiràs és una capa per sobre que tradueix peticions HTTP en crides a les teves funcions, i els valors de retorn (o les excepcions) en respostes HTTP.

HTTP: el protocol de la conversa

HTTP (HyperText Transfer Protocol) és el format acordat d'aquestes peticions i respostes. És text, i és llegible. Una petició real té tres parts — línia inicial, capçaleres i cos opcional:

GET /llibre/Hamlet HTTP/1.1
Host: papyrus.example
Accept: text/html

I la resposta, la mateixa estructura:

HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 1274

<!DOCTYPE html>
<html>...la fitxa de Hamlet...</html>
  • La línia inicial de la petició diu el mètode (GET), la ruta (/llibre/Hamlet) i la versió del protocol.
  • Les capçaleres (headers) són metadades en format Clau: valor — quin format accepta el client, quin format envia el servidor, quant ocupa el cos...
  • El cos (body) porta el contingut: HTML en una pàgina web, JSON en una API, res en moltes peticions GET.

No et caldrà escriure HTTP a mà mai — el navegador i el framework ho fan per tu —, però saber llegir-lo et convertirà en algú que depura problemes web en minuts en lloc d'hores.

Mètodes HTTP: els verbs de la web

El mètode declara la intenció de la petició. Amb quatre n'hi ha prou per a gairebé tot, i encaixen de manera natural amb les operacions que Papyrus ja sap fer:

Mètode Intenció Exemple a Papyrus Modifica dades?
GET Llegir, consultar Veure el catàleg, cercar L'Odissea No
POST Crear alguna cosa nova Donar d'alta un llibre nou al catàleg
PUT Actualitzar una cosa existent Canviar l'estoc de Hamlet de 6 a 9
DELETE Eliminar Retirar un llibre descatalogat

Dues regles d'or que els frameworks respecten i tu també hauràs de respectar:

  • GET mai no ha de modificar dades. Un cercador que indexi la teva web seguirà tots els enllaços GET que trobi; si /vendre/Hamlet fos un GET, un robot et podria buidar l'estoc sense voler.
  • POST, PUT i DELETE porten la informació sensible al cos de la petició, no a la URL (les URLs acaben en logs i historials).

Codis d'estat: el semàfor de les respostes

Cada resposta HTTP comença amb un codi de tres xifres que resumeix com ha anat la cosa. La primera xifra és la família: 2xx èxit, 4xx el client s'ha equivocat, 5xx el servidor s'ha trencat. Aquests cinc són els que faràs servir constantment:

Codi Nom Quan el retornarà Papyrus
200 OK Èxit GET /llibre/Hamlet i Hamlet existeix: aquí tens la seva fitxa
201 Created Creat POST d'un llibre nou acceptat: Faust II ja és al catàleg
400 Bad Request Petició mal formada POST d'un llibre sense titol o amb preu: "gratis"
404 Not Found No existeix GET /llibre/Rayuela — no el tenim (et sona LlibreNoTrobatError?)
500 Internal Server Error Error del servidor Una excepció no capturada al nostre codi Python

La fila del 404 és un avançament important: a M7 vas dissenyar LlibreNoTrobatError per assenyalar exactament aquesta situació dins de Python. A 10-03 veuràs que la traducció excepció → codi d'estat és literal: captures LlibreNoTrobatError i respons 404. La teva jerarquia d'excepcions de M7 ja pensava en la web sense saber-ho.

I un matís d'actitud professional: un 500 sempre és culpa teva (o del teu codi). Si un usuari pot provocar un 500 escrivint alguna cosa estranya en un formulari, et falta validació — hauria de ser un 400 amb un missatge útil.

Anatomia d'una URL

La URL és l'adreça completa d'un recurs. Disseccionem una futura URL de Papyrus:

https://papyrus.example:443/llibres/cercar?titol=odissea&max=10#resultats
└─┬─┘   └──────┬───────┘└┬┘└──────┬──────┘└─────────┬─────────┘└───┬────┘
esquema      host       port     ruta          query string    fragment
  • Esquema: el protocol, http o https (la s és HTTP xifrat; avui, sempre https en producció).
  • Host: el nom del servidor. En desenvolupament serà localhost o 127.0.0.1 — la teva pròpia màquina.
  • Port: la "porta" del servidor. S'omet si és l'estàndard (80 per a http, 443 per a https). El servidor de desenvolupament de Flask fa servir el 5000 i el de Django el 8000; els veuràs mil vegades.
  • Ruta (path): quin recurs es demana. És el que les teves rutes de Flask/Django mapejaran a funcions.
  • Query string: paràmetres opcionals després de ?, en parells clau=valor separats per &. És on viatgen els termes de cerca d'un formulari GET.
  • Fragment: després de #, una posició dins de la pàgina; no arriba al servidor.

HTML: el mínim imprescindible per a aquest mòdul

Siguem honestos: això no és un curs d'HTML, i no intentarà ser-ho. Però les plantilles de Flask i Django generen HTML, així que necessites llegir-lo i escriure'n el bàsic. HTML és un llenguatge de marcatge: emboliques contingut entre etiquetes que declaren què és cada cosa.

<!DOCTYPE html>
<html lang="ca">
<head>
    <meta charset="utf-8">
    <title>Papyrus — Llibreria</title>
</head>
<body>
    <h1>Catàleg de Papyrus</h1>
    <p>Els clàssics de sempre, al teu barri.</p>
    <ul>
        <li>L'Odissea — 12.50 €</li>
        <li>Hamlet — 9.95 €</li>
    </ul>
    <a href="/llibre/Faust">Mostra la fitxa de Faust</a>
</body>
</html>

Línia a línia: <!DOCTYPE html> declara HTML modern; <head> conté metadades que no es pinten (codificació, títol de la pestanya); <body> és el que es veu; <h1> un encapçalament, <p> un paràgraf, <ul>/<li> una llista, <a href="..."> un enllaç. Les etiquetes s'obren <p> i es tanquen </p>.

La peça que més faràs servir és el formulari, perquè és com l'usuari envia dades al servidor:

<form action="/cercar" method="get">
    <label for="titol">Cerca un llibre:</label>
    <input type="text" id="titol" name="titol">
    <button type="submit">Cerca</button>
</form>
  • action és la ruta a la qual s'envia; method el verb HTTP (get per a cerques, post per crear/modificar).
  • Cada <input> amb name es converteix en un paràmetre: en cercar "odissea", el navegador demana GET /cercar?titol=odissea. Aquí tens la query string d'abans.
  • <button type="submit"> dispara l'enviament.

Amb encapçalaments, paràgrafs, llistes, taules (<table>, <tr>, <td>), enllaços i formularis tens el 95 % del que aquest mòdul necessita.

Frontend i backend: dos mons, una frontera

Frontend Backend
On s'executa Al navegador de l'usuari Al servidor
Llenguatges típics HTML, CSS, JavaScript Python (el nostre!), i molts d'altres
Responsabilitat Presentació i interacció Lògica de negoci, dades, seguretat
A Papyrus Les pàgines que veuran en Lluís i la Júlia cercar_llibre(), vendre(), el catàleg

La frontera entre tots dos és exactament HTTP: el frontend demana, el backend respon. Aquest curs és de Python, així que viurem al backend; el frontend que escriurem serà HTML modest generat per plantilles, suficient perquè Papyrus sigui usable. Una regla que no has d'oblidar mai: tota validació del frontend és cortesia, no seguretat. Qualsevol pot enviar peticions HTTP sense passar pel teu formulari (ho faràs tu mateix amb curl a 10-03), així que el backend ha de validar sempre, es refiï o no de la pàgina que va servir.

JSON: l'idioma de les APIs (vell conegut)

Quan el client no és un navegador que vol HTML sinó un programa que vol dades — la futura app mòbil de la Júlia, un script d'en Lluís —, la resposta viatja en JSON. I aquí no hi ha res de nou a aprendre: és exactament el format de M6, el mateix de dades/cataleg.json:

{"titol": "L'Odissea", "preu": 12.5, "estoc": 4}

json.dumps() convertia diccionaris en text JSON; asdict() (M5) convertia les teves dataclasses Llibre en diccionaris. La cadena Llibre → asdict() → JSON → resposta HTTP és la columna vertebral de la lliçó 10-03, i ja en coneixes dues de les tres baules. Una API (Application Programming Interface) web no és res més que un conjunt d'URLs pensades per a programes: mateixes peticions HTTP, mateixa estructura, però JSON en lloc d'HTML al cos.

Què fa un framework per tu

Podries escriure un servidor web amb la biblioteca estàndard de Python (http.server existeix), igual que podries escriure els teus propis tests sense pytest. Ningú no ho fa, per la mateixa raó: un framework web resol de sèrie els problemes que tota aplicació web comparteix.

  • Encaminament: mapejar GET /llibre/Hamlet a la teva funció Python que sap respondre. Sense framework, seria un if/elif gegant sobre la ruta.
  • Plantilles: generar HTML barrejant una estructura fixa amb dades variables (el catàleg, el resultat d'una cerca), sense concatenar cadenes a mà.
  • Formularis: llegir les dades enviades, convertir tipus, validar.
  • Seguretat: protecció contra atacs clàssics (injecció d'HTML maliciós, falsificació de peticions) que ni tan sols sabies que existien — i dels quals parlarem a 10-05.
  • Errors: convertir excepcions en respostes 404/500 raonables en lloc de deixar caure el servidor.

El framework s'ocupa de la fontaneria HTTP; tu escrius només allò que fa única la teva aplicació. A Papyrus, aquesta part única ja està escrita i provada: és el paquet papyrus/.

El panorama Python: Flask, Django i FastAPI

Python té tres frameworks dominants, i aquest mòdul t'ensenyarà els dos primers:

Flask Django FastAPI
Filosofia Micro: nucli mínim, hi afegeixes el que necessites Bateries incloses: ho porta tot Modern, especialitzat en APIs
Corba d'aprenentatge Suau: app en 5 línies Més empinada: estructura i convencions Suau si domines els type hints (M8)
Porta de sèrie Rutes, plantilles, servidor de desenvolupament A més: ORM (base de dades), admin, formularis, usuaris Validació automàtica, documentació automàtica
Brilla en Projectes petits/mitjans, APIs, aprendre Aplicacions grans amb moltes peces estàndard APIs d'alt rendiment (async, M8)
En aquest mòdul 10-02 i 10-03 10-04 i 10-05 Només aquesta menció honesta

Tots tres s'instal·len com qualsevol paquet: pip install flask, pip install django — dins d'un entorn virtual, com vas aprendre a M1. FastAPI mereix la menció perquè és el tercer en discòrdia i fa servir intensivament els type hints de M8 i asyncio per a servidors asíncrons; si algun dia construeixes una API seriosa, dona-li un cop d'ull. Però per aprendre els fonaments, Flask primer (veuràs cada peça per separat) i Django després (veuràs què és tenir-les totes integrades) és el camí clàssic, i és el nostre.

Errors Comuns i Consells

  • Confondre la URL amb la pàgina. La URL identifica el recurs; la pàgina és la resposta. Dues peticions a la mateixa URL poden retornar coses diferents (l'estoc canvia). Pensa en les URLs com a preguntes, no com a fitxers.
  • Fer servir GET per a operacions que modifiquen dades. És l'error conceptual més perillós dels principiants. Si modifica alguna cosa: POST, PUT o DELETE. Sempre.
  • Retornar 200 per a tot. Una API que respon 200 OK amb el cos {"error": "no trobat"} és una API mentidera: els clients automàtics miren primer el codi. Codis correctes des del primer dia.
  • Creure que cal dominar HTML/CSS/JavaScript abans de continuar. No. Amb l'HTML d'aquesta lliçó n'hi ha prou per a tot el mòdul. El backend és una especialitat completa per si mateixa.
  • Oblidar que el servidor no recorda res entre peticions. Cada petició arriba òrfena. Els mecanismes per "recordar" usuaris (sessions, cookies) existeixen i els frameworks els donen fets, però el protocol base és amnèsic — i això és una virtut, com veuràs a 10-03.

Exercicis

Exercici 1: verbs i codis

Per a cada operació de Papyrus, indica el mètode HTTP adequat i el codi d'estat que hauria de retornar el servidor en l'escenari descrit:

  1. La Júlia consulta la fitxa d'El Quixot (existeix).
  2. L'Anna dona d'alta Faust II amb títol, preu i estoc vàlids.
  3. En Lluís consulta la fitxa de Rayuela (no és al catàleg).
  4. Un script envia una alta de llibre amb "preu": "vint euros".
  5. L'Anna actualitza l'estoc de Hamlet a 9 unitats.

Exercici 2: dissecció d'URL

Descompon en les seves parts (esquema, host, port, ruta, query string) la URL:

http://localhost:5000/cercar?titol=quixot&soci=true

Quina parella clau=valor rebrà el servidor d'un formulari amb un únic <input> que té name="titol" si l'usuari escriu faust?

Exercici 3: el formulari de reserva

Escriu l'HTML d'un formulari perquè la Júlia reservi un llibre: un camp de text per al títol, un camp numèric (type="number") anomenat unitats, i un botó "Reserva". La reserva modifica dades (aparta estoc): tria action i method en conseqüència i justifica la tria en un comentari HTML (<!-- ... -->).

Solucions

Exercici 1:

  1. GET200 OK (lectura amb èxit).
  2. POST201 Created (recurs nou creat).
  3. GET404 Not Found (la petició està ben formada; el recurs no existeix — el cas LlibreNoTrobatError).
  4. POST400 Bad Request (el client ha enviat dades invàlides; validem i expliquem el motiu, mai no deixem que esclati en un 500).
  5. PUT200 OK (actualització d'un recurs existent).

Exercici 2: esquema http, host localhost, port 5000 (el de desenvolupament de Flask), ruta /cercar, query string titol=quixot&soci=true (dos paràmetres: titol=quixot i soci=true). Amb l'input name="titol" i el valor faust, el servidor rep titol=faust — la URL resultant seria /cercar?titol=faust.

Exercici 3:

<!-- method="post" perque reservar modifica l'estoc: les operacions
     amb efectes mai no han de viatjar en GET (els robots segueixen
     enllacos GET i els parametres GET acaben en historials i logs). -->
<form action="/reservar" method="post">
    <label for="titol">Títol:</label>
    <input type="text" id="titol" name="titol">
    <label for="unitats">Unitats:</label>
    <input type="number" id="unitats" name="unitats" min="1">
    <button type="submit">Reserva</button>
</form>

Conclusió

Ja tens el mapa. La web és un diàleg de peticions i respostes HTTP entre clients que demanen i un servidor que executa codi — aviat, el teu codi. Els mètodes (GET llegeix, POST crea, PUT actualitza, DELETE esborra) declaren la intenció; els codis d'estat (200, 201, 400, 404, 500) resumeixen el resultat, i ja has vist que LlibreNoTrobatError i el 404 estan destinats a trobar-se. Saps llegir una URL peça a peça, escriure l'HTML mínim amb formularis inclosos, i distingir el frontend (presentació, al navegador) del backend (lògica i dades, en Python). I saps quin framework triaràs per a cada cosa: Flask per començar lleuger, Django quan vulguis les bateries incloses, FastAPI com a assignatura optativa futura. A la propera lliçó s'acaba la teoria: instal·laràs Flask al venv, escriuràs una aplicació completa en cinc línies, descobriràs que @app.route és exactament un d'aquells decoradors de 08-02, i posaràs el catàleg de Papyrus — L'Odissea, Hamlet, El Quixot i Faust — en un navegador per primera vegada.

Curs de Programació en Python

Mòdul 1: Introducció a Python

Mòdul 2: Estructures de Control

Mòdul 3: Funcions i Mòduls

Mòdul 4: Estructures de Dades

Mòdul 5: Programació Orientada a Objectes

Mòdul 6: Gestió de Fitxers

Mòdul 7: Gestió d'Errors i Excepcions

Mòdul 8: Temes Avançats

Mòdul 9: Proves i Depuració

Mòdul 10: Desenvolupament Web amb Python

Mòdul 11: Ciència de Dades amb Python

Mòdul 12: Projecte Final

© Copyright 2026. Tots els drets reservats