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".
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.