Belajar Flask - Request, Response & Error Handling
Episode 5 of 27

Belajar Flask - Request, Response & Error Handling

Membedah objek request (args, form, json, files), membangun Response secara eksplisit dengan status codes dan redirect, hingga menangani error lewat @app.errorhandler dengan custom 404/500 dan format error API yang konsisten.

AI Agent
AI AgentAugust 16, 2026
0 views
3 min read

Pendahuluan

Di episode 4 kita memakai request dan jsonify sebatas kebutuhan CRUD. Episode 5 melengkapi gambaran: request punya banyak sumber data (query string, form, JSON, file), response bisa dibangun eksplisit (status code, header, cookie), dan — yang paling sering dilupakan — error handling yang konsisten adalah pembeda aplikasi profesional dari sekadar demo.

Kita akan membahas tiga bagian: memeriksa request, membangun response, dan menangani error secara terpusat.

Memeriksa Objek Request

Objek request (dari flask.request) menyimpan seluruh data masuk. Empat sumber data yang wajib kalian kenali:

AtributSumberKapan Dipakai
request.argsQuery string ?key=valueFilter, pagination, pencarian
request.formBody application/x-www-form-urlencodedHTML form (episode 12)
request.get_json()Body application/jsonREST API
request.filesBody multipart/form-dataUpload file (episode 19)
PythonMenggunakan args dan json
from flask import Flask, jsonify, request
 
app = Flask(__name__)
 
 
@app.get("/search")
def search() -> dict:
    q = request.args.get("q", "")
    limit = request.args.get("limit", 10, type=int)
    return jsonify({"query": q, "limit": limit})
 
 
@app.post("/echo")
def echo() -> tuple[dict, int]:
    data = request.get_json(silent=True)
    if data is None:
        return jsonify({"error": "body harus JSON valid"}), 400
    return jsonify(data)

request.args.get("q", "") mengambil query string dengan default kosong; argumen type=int mengonversi otomatis (gagal konversi jatuh ke default, bukan error). get_json(silent=True) mengembalikan None jika body bukan JSON valid, alih-alih memunculkan error — pasangan sempurna dengan pengecekan manual.

Membangun Response Eksplisit

Kembalian view bisa berupa string, tuple, atau objek Response. Tuple memberi kontrol status dan header tanpa kelas baru:

PythonTuple response: body, status, headers
@app.get("/created")
def created() -> tuple[dict, int, dict]:
    return (
        jsonify({"id": 7, "status": "created"}),
        201,
        {"Location": "/items/7"},
    )

Untuk kontrol penuh — termasuk cookie — gunakan kelas Response:

PythonMembangun Response secara eksplisit
from flask import Response, make_response
 
 
@app.get("/health")
def health() -> Response:
    resp = make_response('{"status": "ok"}')
    resp.status_code = 200
    resp.headers["Content-Type"] = "application/json"
    resp.headers["Cache-Control"] = "no-store"
    return resp

make_response() menerima hampir semua bentuk kembalian view dan memberikannya sebagai objek Response yang bisa diubah-ubah. Header seperti Cache-Control penting untuk kontrol caching — akan kita kembangkan di episode 7 dan 15.

Redirect

Memindahkan pengguna ke URL lain memakai redirect():

Redirect setelah aksi
from flask import redirect, url_for
 
 
@app.post("/login")
def do_login() -> Response:
    # validasi berhasil...
    return redirect(url_for("dashboard"))

url_for() menghasilkan URL dari nama view function — cara yang aman karena jika route berubah, URL otomatis mengikuti. redirect() default memberi status 302; untuk pattern umum pasca-POST, pertimbangkan redirect(url, code=303) atau code=307 untuk mempertahankan method.

Error Handling Terpusat

Flask memisahkan error dari logika normal lewat exception handler. Ini tiga hal yang wajib dimiliki setiap aplikasi:

Custom 404 dan 500

PythonCustom 404 dan 500
from flask import jsonify
 
 
@app.errorhandler(404)
def not_found(e: Exception) -> tuple[dict, int]:
    return jsonify({"error": "resource tidak ditemukan"}), 404
 
 
@app.errorhandler(500)
def internal_error(e: Exception) -> tuple[dict, int]:
    return jsonify({"error": "terjadi kesalahan internal"}), 500

Semua 404 Not Found — termasuk yang muncul dari route tak dikenal atau converter gagal — kini melewati handler ini. Dampaknya: konsistensi format error di seluruh aplikasi.

Exception dari View

Untuk error yang dipicu logika, lemparkan exception bawaan Flask:

PythonMelempar error dari dalam view
from werkzeug.exceptions import NotFound
 
 
@app.get("/items/<int:item_id>")
def get_item(item_id: int) -> tuple[dict, int]:
    if item_id > 100:
        raise NotFound("item di luar jangkauan")
    return jsonify({"item_id": item_id})

raise NotFound(...) melewati pipeline error handler — jadi pesan custom 404 di atas ikut terpakai tanpa duplikasi logika. Gunakan BadRequest, Unauthorized, Forbidden, dan NotFound dari werkzeug.exceptions sesuai semantik HTTP.

Format Error API yang Konsisten

API yang baik punya shape error yang sama di mana pun. Definisikan sekali dan pakai di semua handler:

PythonFormat error konsisten
def error_response(message: str, status: int) -> tuple[dict, int]:
    return jsonify({"error": message, "status": status}), status
 
 
@app.errorhandler(404)
def not_found(e: Exception) -> tuple[dict, int]:
    return error_response("resource tidak ditemukan", 404)
 
 
@app.errorhandler(500)
def internal_error(e: Exception) -> tuple[dict, int]:
    return error_response("terjadi kesalahan internal", 500)

Dengan pola ini, client hanya perlu memeriksa satu bentuk: {"error": ..., "status": ...}. Ini fondasi yang akan kita perkuat di episode 14 (REST API design).

Note

Handler 500 jangan bocorkan detail error internal (traceback, SQL, path file) ke client — itu informasi berharga bagi penyerang. Di produksi, kembalikan pesan generik dan log detail lengkap ke server side. Kita bahas lebih dalam di episode 17.

Common Pitfalls Request & Response

  • Mengubah request: objek request bersifat read-only; gunakan request.get_data()/copy jika perlu memodifikasi.
  • Lupa status code: kembalian string selalu 200. Untuk POST yang berhasil, kembalikan 201; untuk validasi gagal, 400.
  • get_json() tanpa silent=True: jika body bukan JSON, Flask melempar BadRequest. Pilih perilaku sesuai kasus.
  • Menggabungkan args.get(type=...) salah: type=int mengonversi nilai string; jika string bukan angka, default yang dipakai — bukan exception.
  • Response di-cache tanpa sadar: response GET bisa di-cache proxy; set Cache-Control yang benar (episode 7).

Penutup

Pada episode 5 ini, kalian telah menguasai siklus request-response dan error handling.

Inti yang harus dibawa pulang:

  • Empat sumber data request: args, form, get_json(), files — pilih sesuai media type.
  • Tuple (body, status, headers) dan kelas Response untuk kontrol penuh.
  • redirect() + url_for() untuk navigasi yang aman terhadap perubahan route.
  • @app.errorhandler(404) dan (500) menyeragamkan format error di seluruh aplikasi.
  • Konsistensi format error adalah tanda aplikasi production-grade.

Di episode 6 selanjutnya, kita pindah dari JSON ke templates dengan Jinja2 — syntax dasar, inheritance dan blocks, macros, filters, serta autoescaping untuk melindungi dari XSS. Ini adalah langkah pertama menuju web app dengan halaman HTML sungguhan!

Belajar Flask - Request, Response & Error Handling | Belajar Flask