12.3 REST API izstrāde
Stundas uzdevums: Saprast REST principus un izveidot pareizi strukturētu API spēļu rezultātu pārvaldībai.
70 min plāns: Teorija un paraugs (~10 min) · 1. uzdevums (~15 min) - uzbūvē GET un POST pēc REST · 2. uzdevums (~25 min) - pievieno validāciju un pareizus statusa kodus · 3. uzdevums (~20 min) - pievieno filtrēšanu un kārtošanu. Papildu uzdevumu sāc tikai tad, ja pārējie trīs ir gatavi.
Pirms sāc: atver savu 12.2 stundas serveris.py. Šoreiz maršrutus sakārtosi pēc REST principiem, lai citi programmētāji tos saprastu bez skaidrojuma.
Teorija: REST principi
REST (Representational State Transfer) ir API projektēšanas stils, kas nosaka:
- Resursu identificēšana caur URL (piem.,
/api/speletaji/42). - Standarta HTTP metodes ar konkrētu nozīmi (GET = lasīt, POST = izveidot, PUT = atjaunināt, DELETE = dzēst).
- Stateless - katrs pieprasījums saturs visu vajadzīgo info; serveris nesaglabā stāvokli starp pieprasījumiem.
- JSON kā datu formāts.
| Metode | URL | Darbība |
|---|---|---|
| GET | /api/speletaji | Saraksts ar visiem |
| GET | /api/speletaji/42 | Viens pēc ID |
| POST | /api/speletaji | Izveidot jaunu |
| PUT | /api/speletaji/42 | Aizstāt visu |
| PATCH | /api/speletaji/42 | Daļēji atjaunināt |
| DELETE | /api/speletaji/42 | Dzēst |
Pareizie statusa kodi: 200 OK, 201 Created, 204 No Content, 400 Bad Request, 401 Unauthorized, 404 Not Found, 500 Server Error.
Praktiskie uzdevumi
1. uzdevums -
Uzbūvē GET un POST pēc REST
Beigās viena adrese apkalpos gan lasīšanu, gan pievienošanu.
- Izveido sarakstu
REZULTATI = []. - Uzraksti
@app.route("/api/rezultati", methods=["GET"]), kas atgriež visu sarakstu. - Uzraksti otru funkciju tai pašai adresei ar
methods=["POST"]. - Piešķir katram jaunam ierakstam
id. - Atgriez pēc izveides statusa kodu 201.
- Pārbaudi abus maršrutus ar
curlvai pārlūku.
Gatavs, kad: viena adrese ar GET atgriež sarakstu, bet ar POST pievieno ierakstu un atgriež 201.
2. uzdevums -
Pievieno validāciju un pareizus statusa kodus
Beigās serveris noraidīs sliktus datus, nevis tos saglabās.
- Pārbaudi, vai
request.jsonsatur atslēguvards. - Atgriez statusa kodu 400 ar paskaidrojumu, ja tās nav.
- Pārbaudi, vai
punktiir skaitlis un nav negatīvs. - Nosūti POST pieprasījumu bez vārda un pieraksti atbildi.
- Nosūti pieprasījumu ar negatīviem punktiem un pieraksti atbildi.
- Nosūti derīgu pieprasījumu un pārbaudi, ka tas izdodas.
- Pieraksti vienu secinājumu: kāpēc validācija jāveic serverī, nevis pārlūkā.
Gatavs, kad: trūkstoši vai nederīgi dati atgriež 400 ar paskaidrojumu, bet derīgi - 201.
3. uzdevums -
Pievieno filtrēšanu un kārtošanu
Beigās klients varēs prasīt tikai TOP 5 rezultātus.
- Nolasi parametru ar
request.args.get("limit", 100, type=int). - Sakārto rezultātus pēc punktiem dilstoši.
- Atgriez tikai pirmos
limitierakstus. - Atver pārlūkā
/api/rezultati?limit=5. - Atver to pašu bez parametra un salīdzini.
- Ievadi parametru
limit=abcun pieraksti, kas notiek. - Pieraksti, ko dara
type=intšajā izsaukumā.
Gatavs, kad: ar ?limit=5 atgriežas tieši pieci ieraksti, un nederīgs parametrs neizraisa 500 kļūdu.
Papildu uzdevums - Pievieno DELETE
Ja pamatdarbs ir gatavs, pabeidz visu CRUD komplektu.
- Uzraksti
@app.route("/api/rezultati/<int:id>", methods=["DELETE"]). - Atrodi ierakstu pēc
idun izdzēs to. - Atgriez statusa kodu 404, ja ieraksta nav.
- Pārbaudi ar
curl -X DELETE http://localhost:5000/api/rezultati/1. - Pārbaudi, ka GET vairs šo ierakstu nerāda.
Gatavs, kad: esošs ieraksts tiek izdzēsts, bet neesoša dzēšana atgriež 404.
Biežākās kļūdas
- POST izmanto GET datus: JSON sūta caur body, ne URL parametriem. Dažādi
request.jsonvsrequest.args. - 200 OK pat kļūdām: Atgriez pareizu statusa kodu - 4xx klienta kļūdām, 5xx servera kļūdām.
- API atgriež HTML kļūdas: Noklusējuma Flask 404 ir HTML - pārraksti to, lai vienmēr būtu JSON.
Koda piemērs
from flask import Flask, jsonify, request, abort
app = Flask(__name__)
REZULTATI = []
@app.route("/api/rezultati", methods=["GET"])
def saraksts():
limit = request.args.get("limit", 100, type=int)
sakartoti = sorted(REZULTATI, key=lambda r: r["punkti"], reverse=True)
return jsonify(sakartoti[:limit])
@app.route("/api/rezultati", methods=["POST"])
def jauns():
d = request.json or {}
if not d.get("vards"): return jsonify({"kluda": "Trūkst vārds"}), 400
if d.get("punkti", 0) < 0: return jsonify({"kluda": "Negatīvi punkti"}), 400
d["id"] = len(REZULTATI) + 1
REZULTATI.append(d)
return jsonify(d), 201
@app.errorhandler(404)
def not_found(e):
return jsonify({"kluda": "Nav atrasts"}), 404
GET ?limit=5 → top 5
404 → {kluda: 'Nav atrasts'}