Erreurs & validation
Un seul type d'erreur, mappé une fois vers le bon statut HTTP et un JSON cohérent. Les erreurs DB sont loggées mais ne fuient jamais leur détail au client.
L'enum d'erreur #
// errors.rs
#[derive(Debug, thiserror::Error)]
pub enum MemoError {
#[error("Non authentifié")] Unauthorized,
#[error("Introuvable: {0}")] NotFound(String),
#[error("Données invalides: {0}")] Validation(String),
#[error("Erreur base de données")] Database(#[from] sqlx::Error),
#[error("Erreur interne")] Internal(#[from] anyhow::Error),
}
impl IntoResponse for MemoError {
fn into_response(self) -> Response {
let (status, code) = match &self {
MemoError::Unauthorized => (StatusCode::UNAUTHORIZED, "UNAUTHORIZED"),
MemoError::NotFound(_) => (StatusCode::NOT_FOUND, "NOT_FOUND"),
MemoError::Validation(_) => (StatusCode::UNPROCESSABLE_ENTITY, "VALIDATION"),
MemoError::Database(e) => { tracing::error!(error=%e, "db"); (StatusCode::INTERNAL_SERVER_ERROR, "DATABASE_ERROR") }
MemoError::Internal(e) => { tracing::error!(error=%e, "internal"); (StatusCode::INTERNAL_SERVER_ERROR, "INTERNAL_ERROR") }
};
(status, Json(json!({ "error": code, "message": self.to_string() }))).into_response()
}
}
pub type Result<T> = std::result::Result<T, MemoError>;| Erreur | Statut |
|---|---|
| Unauthorized | 401 |
| NotFound | 404 |
| Validation | 422 |
| Database / Internal | 500 |
Validation des entrées #
#[derive(Deserialize, validator::Validate)]
pub struct CreateNoteDto {
#[validate(length(min = 1, max = 500))]
pub title: String,
pub body: String,
}
// dans le handler create :
dto.validate().map_err(|e| MemoError::Validation(e.to_string()))?;Renvoyer une erreur — dans d'autres langages #
// Rust — via IntoResponse (cf. l'enum ci-dessus)
return (
StatusCode::NOT_FOUND,
Json(json!({ "error": "NOT_FOUND", "message": "Note introuvable" })),
).into_response();<?php
http_response_code(404);
header('Content-Type: application/json');
echo json_encode(['error' => 'NOT_FOUND', 'message' => 'Note introuvable']);
exit;# Python — Flask
return jsonify(error="NOT_FOUND", message="Note introuvable"), 404// Go
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusNotFound)
json.NewEncoder(w).Encode(map[string]string{
"error": "NOT_FOUND", "message": "Note introuvable",
})# Perl — Mojolicious
return $c->render(status => 404,
json => { error => "NOT_FOUND", message => "Note introuvable" });Règle
Toujours valider avant toute opération DB ; ne distinguez pas, sur les routes publiques, « introuvable » de « non autorisé ».