Study
Axum

Error Handling dengan IntoResponse

Penjelasan

Karena handler Axum bisa mengembalikan tipe apa pun yang meng-implementasi `IntoResponse`, cara paling idiomatis menangani error adalah membuat enum error kustom lalu meng-implementasi `IntoResponse` untuknya. Dengan begitu, handler bisa mengembalikan `Result<T, AppError>`, dan Axum otomatis tahu cara mengubah `AppError` menjadi response HTTP yang sesuai (misalnya 404 untuk data tidak ditemukan, 400 untuk input tidak valid). Pola umumnya: definisikan enum (misalnya `AppError::NotFound`, `AppError::BadRequest(String)`), lalu implementasikan `impl IntoResponse for AppError` yang men-`match` setiap varian ke `StatusCode` dan body JSON yang sesuai. Keuntungan pendekatan ini: seluruh logic konversi error-ke-response terpusat di satu tempat, handler-handler individual jadi bersih — cukup memakai operator `?` seperti kode Rust biasa tanpa perlu menangani konversi response di setiap fungsi.

Contoh Konsep

use axum::{
    http::StatusCode,
    response::{IntoResponse, Response},
    routing::get,
    Json, Router,
};
use serde_json::json;

enum AppError {
    NotFound,
    BadRequest(String),
}

impl IntoResponse for AppError {
    fn into_response(self) -> Response {
        let (status, message) = match self {
            AppError::NotFound => (StatusCode::NOT_FOUND, "data tidak ditemukan".to_string()),
            AppError::BadRequest(msg) => (StatusCode::BAD_REQUEST, msg),
        };
        (status, Json(json!({ "error": message }))).into_response()
    }
}

async fn get_note(axum::extract::Path(id): axum::extract::Path<u32>) -> Result<String, AppError> {
    if id == 0 {
        return Err(AppError::BadRequest("id tidak boleh 0".to_string()));
    }
    if id > 100 {
        return Err(AppError::NotFound);
    }
    Ok(format!("Catatan dengan id {}", id))
}

#[tokio::main]
async fn main() {
    let app = Router::new().route("/notes/{id}", get(get_note));
    let listener = tokio::net::TcpListener::bind("127.0.0.1:3000").await.unwrap();
    axum::serve(listener, app).await.unwrap();
}

Praktikum

Tambahkan varian `AppError::Unauthorized` yang menghasilkan status 401 dengan pesan "akses ditolak".

Editor Latihan

Ketik/edit bebas di sini untuk latihan — kode ini tidak dijalankan.

Tips

Kalau error-mu berasal dari library lain (misalnya `sqlx::Error`), tambahkan varian `AppError::Database(sqlx::Error)` lalu implementasikan `impl From<sqlx::Error> for AppError` — dengan begitu operator `?` bisa otomatis mengonversi error dari query database menjadi `AppError` tanpa `.map_err(...)` manual di setiap handler.