Kursus Python / Meminta data HTTP dan memvalidasi respons API JSON
API HTTP menerima permintaan dan mengembalikan status serta data respons. Klien Python perlu memeriksa keberhasilan permintaan, batas waktu tunggu, ukuran respons, dan struktur JSON sebagai pemeriksaan yang terpisah.
Bedakan protokol, alamat, dan format data
HTTP (Hypertext Transfer Protocol) mengatur komunikasi permintaan dan respons. URL (Uniform Resource Locator) menentukan sumber yang dituju, sedangkan JSON merupakan salah satu format isi respons. Status sukses tidak membuktikan bahwa data sesuai aturan aplikasi.
Latihan ini memakai server lokal agar respons dapat diubah sendiri dan tidak bergantung pada layanan luar. Server ini hanya untuk latihan klien, bukan rancangan penerapan server produksi. Untuk layanan luar, ikuti dokumentasi HTTPS, autentikasi, batas permintaan, dan ketentuan layanan yang bersangkutan.
Fungsi urlopen menyediakan objek respons yang dapat digunakan dalam konteks with. Rujukan parameter tersedia pada dokumentasi urllib.request. Sesudah mengambil isi, tetap gunakan validasi struktur dan tipe JSON sesuai kebutuhan data.
Siapkan respons dan server lokal
Gunakan folder latihan baru, Python 3.12 atau lebih baru, dan lingkungan proyek. Tidak diperlukan paket pihak ketiga. Simpan expenses.json dengan encoding UTF-8 dan isi lengkap berikut:
[{"id":"e1","name":"coffee","amount":3500},{"id":"e2","name":"lunch","amount":8000}]
Pada terminal pertama, masuk ke folder berkas tersebut dan jalankan:
python -m http.server 8000 --bind 127.0.0.1
Alamat 127.0.0.1 membatasi listener ke komputer yang sama. Biarkan terminal itu berjalan selama pengujian klien. Jika port 8000 dipakai, pilih port lain dan sesuaikan URL klien; jangan menghentikan layanan lain hanya untuk mengambil portnya.
Gunakan folder khusus berisi fixture yang memang boleh disajikan untuk latihan. Server menyajikan isi folder kerjanya dan tidak menyediakan kebijakan produksi. Batas penggunaannya dijelaskan pada dokumentasi http.server.
Jalankan klien lengkap dan validasi hasilnya
Simpan kode lengkap sebagai fetch_expenses.py dengan encoding UTF-8 pada folder latihan.
import json
import sys
from urllib.request import urlopen
from urllib.error import HTTPError, URLError
url = sys.argv[1]
try:
with urlopen(url, timeout=5) as response:
if response.status != 200:
raise ValueError("unexpected status")
raw = response.read(1_000_001)
if len(raw) > 1_000_000:
raise ValueError("response too large")
data = json.loads(raw.decode("utf-8"))
except (HTTPError, URLError, TimeoutError, UnicodeError, ValueError) as error:
raise SystemExit(f"request failed: {type(error).__name__}")
if not isinstance(data, list):
raise SystemExit("expected a list")
total = 0
for row in data:
if not isinstance(row, dict) or type(row.get("amount")) is not int or row["amount"] < 0:
raise SystemExit("invalid record")
total += row["amount"]
print("records:", len(data))
print("total:", total)
Buka terminal kedua dengan lingkungan proyek yang sama, masuk ke folder klien, dan jalankan:
python fetch_expenses.py http://127.0.0.1:8000/expenses.json
Hasil normal adalah dua baris records: 2 dan total: 11500. Selalu berikan argumen URL; contoh mengakses sys.argv[1] secara langsung dan belum menyediakan parser argumen ramah pengguna. Tanpa argumen, ia gagal sebelum mengirim permintaan.
Klien mengharapkan status akhir 200 dan membaca paling banyak 1.000.001 byte agar dapat menolak respons di atas batas 1.000.000 byte. Setelah itu, byte didekode sebagai UTF-8 dan di-parse sebagai JSON. Batas ukuran ini merupakan aturan contoh, bukan ukuran universal untuk semua API.
Pemeriksaan berikutnya mensyaratkan list berisi dictionary dengan jumlah berupa integer biasa yang tidak negatif. Boolean dan string angka ditolak. Contoh belum memvalidasi id atau name, memeriksa ID berulang, atau menyimpan data. Respons kosong berbentuk [] sah menurut kontrak ini dan menghasilkan nol record serta total nol.
Pisahkan kegagalan jaringan dari data yang tidak sah
HTTPError menunjukkan respons status HTTP yang diperlakukan sebagai kesalahan, misalnya permintaan path yang tidak ditemukan. URLError dapat mencakup kegagalan koneksi atau resolusi nama. Respons yang berhasil diterima masih dapat ditolak karena ukuran, UTF-8, sintaks JSON, atau isi record.
Pengaturan timeout=5 membatasi operasi jaringan yang memblokir, bukan menjamin seluruh proses dari awal sampai akhir selalu selesai dalam lima detik. Beberapa kesalahan pembacaan HTTP juga dapat diteruskan sebagai jenis pengecualian lain yang tidak termasuk penangan contoh. Tentukan cakupan penanganan berdasarkan kebutuhan klien, alih-alih menganggap tuple pengecualian ini mencakup seluruh kemungkinan kegagalan transport.
- Jalankan permintaan asli dan cocokkan dua record serta total 11500.
- Ubah nama berkas pada URL menjadi nama yang tidak ada. Klien harus melaporkan kegagalan HTTP, bukan jumlah nol yang tampak berhasil.
- Pulihkan URL dan ubah salah satu jumlah dalam JSON menjadi string. Server berkas membaca fixture saat diminta, sehingga klien berikutnya menolak record tersebut. Pulihkan angka setelah pengujian.
- Akhiri server melalui Ctrl+C pada terminal pertama, lalu coba klien lagi. Kegagalan koneksi harus dibedakan dari respons sukses.
Penangan contoh menampilkan jenis kesalahan untuk masalah permintaan, lalu berhenti. Ia tidak mengganti respons gagal dengan list kosong. Periksa kode keluar ketika menghubungkan klien ke pekerjaan lanjutan, sesuai pemisahan log, keluaran, dan status proses.
Jika menambah retry, batasi jumlah percobaan dan jeda serta tentukan apakah permintaan aman diulang. GET untuk membaca fixture berbeda dari operasi penulisan yang dapat menggandakan pekerjaan. Jangan menambahkan retry tanpa memahami efek samping atau kebijakan API.
Kelola token menurut kebijakan layanan dan hindari menyimpannya dalam sumber, URL, atau log. Untuk aplikasi yang menerima URL dari luar, tentukan pula tujuan dan skema yang boleh diakses; contoh localhost yang tetap bukan pemeriksaan umum keamanan URL. Sesudah latihan, pastikan server sudah berhenti dan pulihkan fixture sebelum membandingkan hasil lagi.




