Any way to render errors as late as possible? #932
-
Hi! I've been looking for a way to "render" errors in a custom way, say for example, I have an API endpoint that's only accessible via let app = Router::new().route("/hello-world", get(|| "Hello, World!")); Calling {
"status": 405,
"status_message": "Method Not Allowed",
"error": "Method Not Allowed",
"error_code": "method_not_allowed"
} I suppose I could do this via Not only does this feel like a hack, it would also have to do horrible string/content-based heuristics to determine if the error response was generated by axum or one of my own endpoints. For example, let's say an authentication middleware has encountered an expired authentication token, and has returned a custom error that I implemented {
"status": 403,
"status_message": "Forbidden",
"error": "Given authentican token has expired",
"error_code": "authentication_token_expired"
} I've played with the If not, is something like this possible in |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 5 replies
-
For 404s axum has So to handle this in one place you have to write a middleware that checks the response status code. I don't think thats a hack especially because returning 405 from domain logic is very rare, so if you see one its probably safe to assume its from axum and thus valid to override the response body. |
Beta Was this translation helpful? Give feedback.
-
For anyone else trying to do exactly what @RAnders00 was aiming for. Using https://docs.rs/axum/latest/axum/middleware/fn.from_fn.html: #[derive(Serialize, Debug)]
pub struct Error {
pub response: String,
pub text: String,
}
impl Error {
pub fn new(text: &str) -> Self {
Self {
response: "ERROR".to_string(),
text: text.to_string(),
}
}
}
pub async fn method_not_allowed<B>(req: Request<B>, next: Next<B>) -> impl IntoResponse {
let resp = next.run(req).await;
let status = resp.status();
match status {
StatusCode::METHOD_NOT_ALLOWED => Err((
StatusCode::METHOD_NOT_ALLOWED,
Json(Error::new("Method not allowed")),
)
.into_response()),
_ => Ok(resp),
}
} ... let app = Router::new()
.route("/", get(frontpage))
.layer(middleware::from_fn(method_not_allowed)) Generates a response with a 405 status: {
"response": "ERROR",
"text": "Method not allowed"
} No guarantees about performance! |
Beta Was this translation helpful? Give feedback.
For 404s axum has
Router::falback
but we don't have something like that for 405s. That is because there isn't one single method router. When you doget(foo)
in one place andpost(bar)
somewhere, those two don't know about each other and thus cannot coordinate their fallbacks.So to handle this in one place you have to write a middleware that checks the response status code. I don't think thats a hack especially because returning 405 from domain logic is very rare, so if you see one its probably safe to assume its from axum and thus valid to override the response body.