ebSkola

12.3 REST API izstrāde

Stundas uzdevums: Saprast REST principus un izveidot pareizi strukturētu API spēļu rezultātu pārvaldībai.

SR 2.4.11. Lieto standartizētas bibliotēkas un API SR 2.4.7. Lietotāja saskarne

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.
MetodeURLDarbība
GET/api/speletajiSaraksts ar visiem
GET/api/speletaji/42Viens pēc ID
POST/api/speletajiIzveidot jaunu
PUT/api/speletaji/42Aizstāt visu
PATCH/api/speletaji/42Daļēji atjaunināt
DELETE/api/speletaji/42Dzē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.

  1. Izveido sarakstu REZULTATI = [].
  2. Uzraksti @app.route("/api/rezultati", methods=["GET"]), kas atgriež visu sarakstu.
  3. Uzraksti otru funkciju tai pašai adresei ar methods=["POST"].
  4. Piešķir katram jaunam ierakstam id.
  5. Atgriez pēc izveides statusa kodu 201.
  6. Pārbaudi abus maršrutus ar curl vai 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.

  1. Pārbaudi, vai request.json satur atslēgu vards.
  2. Atgriez statusa kodu 400 ar paskaidrojumu, ja tās nav.
  3. Pārbaudi, vai punkti ir skaitlis un nav negatīvs.
  4. Nosūti POST pieprasījumu bez vārda un pieraksti atbildi.
  5. Nosūti pieprasījumu ar negatīviem punktiem un pieraksti atbildi.
  6. Nosūti derīgu pieprasījumu un pārbaudi, ka tas izdodas.
  7. 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.

  1. Nolasi parametru ar request.args.get("limit", 100, type=int).
  2. Sakārto rezultātus pēc punktiem dilstoši.
  3. Atgriez tikai pirmos limit ierakstus.
  4. Atver pārlūkā /api/rezultati?limit=5.
  5. Atver to pašu bez parametra un salīdzini.
  6. Ievadi parametru limit=abc un pieraksti, kas notiek.
  7. 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.

  1. Uzraksti @app.route("/api/rezultati/<int:id>", methods=["DELETE"]).
  2. Atrodi ierakstu pēc id un izdzēs to.
  3. Atgriez statusa kodu 404, ja ieraksta nav.
  4. Pārbaudi ar curl -X DELETE http://localhost:5000/api/rezultati/1.
  5. 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.json vs request.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
POST → 201 Created
GET ?limit=5 → top 5
404 → {kluda: 'Nav atrasts'}