Banyak aplikasi Python berkomunikasi dengan layanan lain melalui HTTP, misalnya mengambil data, mengirim laporan, atau menanyakan status antrean tiket. Pustaka requests menyederhanakan komunikasi tersebut menjadi pemanggilan fungsi dengan objek hasil yang mudah dibaca. Topik ini membahas permintaan (request) GET dan POST, pengiriman header, pembacaan kode status, serta penanganan error yang menyertainya. Seluruh contoh berjalan terhadap server lokal yang dinyalakan di dalam skripnya sendiri, sehingga setiap potongan kode dapat dicoba tanpa koneksi internet.
Permintaan GET pertama#
Permintaan GET mengambil data dari server tanpa mengubah keadaan apa pun di sana. Fungsi requests.get menerima URL lengkap, mengirim permintaan tersebut, dan mengembalikan objek response yang berisi kode status, header, serta badan respons. Skrip berikut menyalakan server kecil pada 127.0.0.1 di dalam thread daemon, mengirim satu permintaan GET, mencetak hasilnya, lalu mematikan server di blok finally.
"""Demo permintaan GET dengan requests ke server lokal.
Skrip menyalakan server kecil pada 127.0.0.1:8765 di dalam thread daemon,
mengirim satu permintaan GET, mencetak hasilnya, lalu mematikan server.
"""
import threading
from http.server import BaseHTTPRequestHandler, HTTPServer
import requests
PORT = 8765
class Handler(BaseHTTPRequestHandler):
def do_GET(self):
pengirim = self.headers.get("X-Pengirim", "tidak diketahui")
isi = (
f"Halo, {pengirim}. Ini respons dari server lokal.\n"
f"Jalur yang diminta: {self.path}\n"
).encode("utf-8")
self.send_response(200)
self.send_header("Content-Type", "text/plain; charset=utf-8")
self.end_headers()
self.wfile.write(isi)
def log_message(self, format, *args):
pass # mematikan log akses bawaan agar keluaran skrip tetap bersih
server = HTTPServer(("127.0.0.1", PORT), Handler)
thread = threading.Thread(target=server.serve_forever, daemon=True)
thread.start()
try:
respons = requests.get(
f"http://127.0.0.1:{PORT}/selamat-datang",
headers={"X-Pengirim": "belajar-requests"},
timeout=5,
)
print("status :", respons.status_code)
print("tipe :", respons.headers["Content-Type"])
print(respons.text, end="")
finally:
server.shutdown()
server.server_close()status : 200
tipe : text/plain; charset=utf-8
Halo, belajar-requests. Ini respons dari server lokal.
Jalur yang diminta: /selamat-datangBaris status menunjukkan kode 200 yang berarti berhasil, sedangkan baris tipe memperlihatkan header Content-Type yang menyatakan isinya berupa teks polos dengan charset UTF-8. Argumen headers menerima dictionary, sehingga header X-Pengirim ikut terkirim dan server membacanya kembali di dalam badan respons. Atribut respons.text memuat badan respons dalam bentuk string yang telah didekode menurut enkode yang dilaporkan server.
Bagian server pada skrip memakai modul http.server dari pustaka standar, yaitu kelas HTTPServer beserta handler yang menangani satu metode do_GET. Server dinyalakan di thread daemon agar skrip dapat melanjutkan ke pemanggilan requests, dan blok finally memastikan server dimatikan kembali apa pun yang terjadi. Pembaca tidak perlu menulis server untuk memakai requests, tetapi pola inilah yang membuat setiap contoh pada topik ini berjalan mandiri tanpa jaringan luar. Diagram berikut merangkum peran requests pada pertukaran data semacam ini.
Mengirim data dengan POST#
Permintaan POST mengirim data ke server, umumnya untuk membuat data baru. Argumen json= menerima dictionary, menyerialisasi nilainya menjadi JSON, dan mengisi header Content-Type dengan application/json secara otomatis. Skrip berikut mengirim satu catatan ke server lokal, kemudian mencetak balasan JSON yang dikembalikan server.
"""Demo permintaan POST dengan data JSON ke server lokal.
Server menerima JSON pada permintaan POST, membalas dengan kode 201,
dan mengembalikan JSON berisi konfirmasi yang isinya tetap.
"""
import json
import threading
from http.server import BaseHTTPRequestHandler, HTTPServer
import requests
PORT = 8766
class Handler(BaseHTTPRequestHandler):
def do_POST(self):
panjang = int(self.headers.get("Content-Length", 0))
data = json.loads(self.rfile.read(panjang))
balasan = {
"pesan": "catatan diterima",
"judul": data["judul"],
"panjang_isi": len(data["isi"]),
}
isi = json.dumps(balasan).encode("utf-8")
self.send_response(201)
self.send_header("Content-Type", "application/json")
self.end_headers()
self.wfile.write(isi)
def log_message(self, format, *args):
pass
server = HTTPServer(("127.0.0.1", PORT), Handler)
thread = threading.Thread(target=server.serve_forever, daemon=True)
thread.start()
try:
respons = requests.post(
f"http://127.0.0.1:{PORT}/catatan",
json={"judul": "belajar requests", "isi": "permintaan POST pertama"},
timeout=5,
)
print("status :", respons.status_code)
print("tipe :", respons.headers["Content-Type"])
print(respons.json())
finally:
server.shutdown()
server.server_close()status : 201
tipe : application/json
{'pesan': 'catatan diterima', 'judul': 'belajar requests', 'panjang_isi': 23}Kode status 201 menyatakan bahwa satu sumber daya baru berhasil dibuat. Metode respons.json() mengurai badan JSON menjadi dictionary Python, dan tanda kutip tunggal pada keluaran muncul karena itulah cara Python mencetak dictionary. Server melaporkan panjang_isi bernilai 23 karena string “permintaan POST pertama” memuat 23 karakter. Pada aplikasi nyata, bentuk permintaan semacam ini dipakai untuk mengirim data formulir, catatan baru, atau pesan antrean ke sebuah API.
Selain json=, requests menyediakan argumen data= untuk pengiriman bentuk formulir klasik, yaitu pasangan kunci dan nilai berenkode application/x-www-form-urlencoded. Pilihan argumen menentukan header Content-Type sekaligus bentuk isinya, sehingga keduanya perlu disesuaikan dengan apa yang diharapkan server. Apabila bentuknya keliru, server umumnya membalas 422 atau memperlakukan isinya sebagai kosong.
Kode status dan penanganan error#
Kode status HTTP mengelompokkan hasil permintaan menjadi tiga keluarga besar, yaitu 2xx untuk berhasil, 4xx untuk kesalahan dari sisi klien, dan 5xx untuk kesalahan di sisi server. Objek respons menyediakan atribut ok yang bernilai True untuk kode 200 sampai 299, serta metode raise_for_status() yang menimbulkan requests.HTTPError bila kode statusnya 4xx atau 5xx. Skrip berikut menguji kedua cabang tersebut terhadap server lokal yang membalas 200 untuk jalur /catatan/12 dan 404 untuk jalur lain.
"""Demo kode status dan raise_for_status dengan server lokal.
Server membalas 200 untuk /catatan/12 dan 404 untuk jalur lain,
sehingga kedua cabang penanganan status dapat diuji tanpa internet.
"""
import threading
from http.server import BaseHTTPRequestHandler, HTTPServer
import requests
PORT = 8767
class Handler(BaseHTTPRequestHandler):
def do_GET(self):
if self.path == "/catatan/12":
isi, kode = b"isi catatan nomor 12", 200
else:
isi, kode = b"catatan tidak ditemukan", 404
self.send_response(kode)
self.send_header("Content-Type", "text/plain; charset=utf-8")
self.end_headers()
self.wfile.write(isi)
def log_message(self, format, *args):
pass
server = HTTPServer(("127.0.0.1", PORT), Handler)
thread = threading.Thread(target=server.serve_forever, daemon=True)
thread.start()
try:
ada = requests.get(f"http://127.0.0.1:{PORT}/catatan/12", timeout=5)
print(ada.status_code, ada.ok)
ada.raise_for_status()
print("raise_for_status: lolos tanpa error")
hilang = requests.get(f"http://127.0.0.1:{PORT}/catatan/99", timeout=5)
print(hilang.status_code, hilang.ok)
try:
hilang.raise_for_status()
except requests.HTTPError as e:
print("HTTPError:", e)
finally:
server.shutdown()
server.server_close()200 True
raise_for_status: lolos tanpa error
404 False
HTTPError: 404 Client Error: Not Found for url: http://127.0.0.1:8767/catatan/99Permintaan pertama berakhir dengan 200 dan True, sehingga raise_for_status() lolos tanpa error. Permintaan kedua menerima 404 dan False, kemudian pemanggilan raise_for_status() menimbulkan HTTPError yang ditangkap blok except dan dicetak lengkap dengan URL yang bermasalah. Pola ini menjadi dasar penanganan error yang disiplin, yaitu memeriksa status terlebih dahulu sebelum badan respons diproses lebih lanjut.
Permintaan yang dibalas kode 3xx ditangani requests secara otomatis, yaitu mengikuti pengalihan (redirect) hingga respons akhirnya diterima. Atribut respons.history menyimpan rangkaian respons perantara tersebut, sedangkan respons.url memuat alamat akhir yang benar-benar dijangkau. Perilaku ini menjelaskan mengapa kode 301 jarang terlihat pada pemakaian sehari-hari, karena yang dikembalikan sudah menjadi respons dari alamat tujuannya.
Kesalahan umum#
Kesalahan pertama adalah memanggil respons.json() terhadap badan yang bukan JSON. Metode json() menimbulkan requests.exceptions.JSONDecodeError apabila isinya berupa teks polos atau halaman HTML, misalnya ketika server sebenarnya sedang membalas pesan error. Periksa header Content-Type terlebih dahulu, atau tangkap exception tersebut ketika bentuk isi respons belum dapat dipastikan.
Kesalahan kedua adalah melanjutkan pemrosesan tanpa memeriksa kode status. Permintaan yang dibalas 404 tetap mengembalikan objek respons, sehingga kode berikutnya memproses badan berupa pesan error seolah-olah data sah. Memanggil raise_for_status() segera setelah permintaan mengubah keadaan ini menjadi error yang jelas alih-alih data yang keliru tanpa terdeteksi. Apabila kode tertentu memerlukan perlakuan berbeda, misalnya memisahkan 404 dari 500, atribut status_code tetap dapat diperiksa secara eksplisit.
Kesalahan ketiga adalah salah alamat, misalnya port yang belum dinyalakan atau nama host yang keliru. requests menimbulkan requests.exceptions.ConnectionError tanpa objek respons sama sekali, sehingga blok except untuk HTTPError tidak menjangkaunya. Kedua exception tersebut berada di bawah requests.exceptions.RequestException, yang menjadi pilihan tepat untuk penanganan paling luar.
Ringkasan#
requests.getdanrequests.postmengirim permintaan HTTP dan mengembalikan objek respons berisi kode status, header, serta badan respons.- Argumen
json=menyerialisasi dictionary menjadi JSON dan mengisiContent-Typesecara otomatis, sedangkanheaders=mengirim header kustom. - Metode
raise_for_status()mengubah kode 4xx dan 5xx menjadirequests.HTTPError, sehingga kegagalan terdeteksi sebelum data diproses. - Argumen
timeoutmencegah program menggantung ketika server tidak menjawab.
Referensi#
- Quickstart — requests project — Panduan resmi requests untuk permintaan GET, POST, header, dan pembacaan respons.
- API Reference — requests project — Referensi objek Response beserta metode raise_for_status dan keluarga exception requests.