· 3 min read
Understanding axum Responses and Custom Error Handling
Dive into how axum handles responses with explicit status codes and learn about custom error handling with the ChatError system.
Building Responses and Error Handling in Axum
When working with Axum, a web framework built on top of Tower and Tokio, one of the fundamental aspects of building a robust API is the management of responses and error handling. This post will cover how to construct responses with explicit status codes, how Axum handles different response types, and how to implement a custom error handler… Because honestly, the docs and guides out there are pretty terrible.
Responses in Axum
In Axum, you can return various types from your route handlers. The IntoResponse
trait allows any type that implements it to be used as a response. Here are a few common return types:
Json<T>
: Returns a JSON response with a content type ofapplication/json
. It will default to a status code of200 OK
unless specified otherwise.StatusCode
: This can be used to return an empty response with a specific status code.- Tuples: You can return a tuple that includes a status code and a response body, which allows for greater flexibility.
Example of Implicit and Explicit Status Codes
In Axum, some responses have implicit status codes while others require you to specify them explicitly.
Implicit Status Codes: When you return a
Json<T>
, the default status code is200 OK
:async fn get_message() -> Json<MessageResponse> { Json(MessageResponse { uuid: uuid::Uuid::new_v4(), text: "Hello, World!".to_string(), sent_at: chrono::Utc::now(), }) }
Explicit Status Codes: For a
POST
request where a resource is created, it is common to return a201 Created
status code:async fn post_message() -> (StatusCode, Json<MessageResponse>) { let response = MessageResponse { uuid: uuid::Uuid::new_v4(), text: "Message created".to_string(), sent_at: chrono::Utc::now(), }; (StatusCode::CREATED, Json(response)) }
This distinction is important for RESTful APIs, where the status code conveys meaningful information about the outcome of the request.
You can also use impl IntoResponse
, for something like this
async fn post_message() -> impl IntoResponse {
let response = MessageResponse {
uuid: uuid::Uuid::new_v4(),
text: "Message created".to_string(),
sent_at: chrono::Utc::now(),
};
Ok((StatusCode::CREATED, Json(response)).into_response())
}
Implementing a Custom Error Handler
Error handling is crucial in building resilient applications. Axum allows you to define custom error types and handle them gracefully. Below, we’ll implement a custom error handler using your ChatError
system.
Custom Error Enum
First, define the ChatError
enum:
use axum::{response::IntoResponse, Json};
use http::StatusCode;
#[derive(Debug)]
pub enum ChatError {
NotFound,
InternalServerError,
}
impl IntoResponse for ChatError {
fn into_response(self) -> axum::response::Response {
match self {
ChatError::NotFound => {
(StatusCode::NOT_FOUND, Json("Resource not found".to_string())).into_response()
}
ChatError::InternalServerError => {
(StatusCode::INTERNAL_SERVER_ERROR, Json("Internal server error".to_string())).into_response()
}
}
}
}
Using the Custom Error Handler in Handlers
You can now use ChatError
in your handlers. Here’s an example of a handler that might return a ChatError
:
#[axum::debug_handler]
pub async fn get_message() -> Result<Json<MessageResponse>, ChatError> {
// Simulate a condition to return an error
let found = false;
if !found {
return Err(ChatError::NotFound);
}
let response = MessageResponse {
uuid: uuid::Uuid::new_v4(),
text: "Hello, World!".to_string(),
sent_at: chrono::Utc::now(),
};
Ok(Json(response))
}
In this example, if the message is not found, the handler returns a ChatError::NotFound
, which will be transformed into a 404 Not Found
response.
Summary
When building APIs with Axum, understanding how responses and error handling work is crucial for creating a reliable service. The ability to return explicit status codes allows you to convey more information to the client about the outcome of their requests. Implementing custom error types, like ChatError
, gives you fine control over error responses and enhances the overall user experience of your API.
Happy coding!