Axum: Custom ApiResponse Enum with Optional Redirects/Cookies – Better to Implement IntoResponse or Just Return impl IntoResponse?

⚓ Rust    📅 2025-10-12    👤 surdeus    👁️ 6      

surdeus

Warning

This post was published 50 days ago. The information described in this article may have changed.

Would it be better to just return impl IntoResponse in axum handlers? Or is the use of enums a good thing here?

Would you do a if let Some() = fn {} else {}.

/// Handler for login page, here the form data gets send to when a request gets made to '/login'
async fn login(
    State(state): State<AppState>,
    jar: CookieJar,
    Form(auth): Form<AuthCredentials>,
) -> Result<ApiResponse, ApiError> {
    println!("login: jar: {jar:?}, auth: {auth:?}");
    let pool = state.pool;

    if let Some(cookie) = auth_login(&pool, auth).await {
        println!("login: cookie: {cookie:?}");
        let cookie = Cookie::build((cookie.0, cookie.1)).max_age(Duration::seconds(60));
        let jar = jar.add(cookie);
        let redirect = Redirect::to("app");

        return Ok(ApiResponse::OK {
            jar: Some(jar),
            redirect: Some(redirect),
        });
    }

    let redirect = Redirect::to("/registration");
    Err(ApiError::Unauthorised { redirect })
}

#[derive(Debug)]
struct CookieBase(String, String);

impl CookieBase {
    fn new() -> Self {
        let token = Uuid::new_v4().to_string();
        Self(SESSION_ID.to_string(), token)
    }
}

#[derive(Debug, Deserialize)]
struct AuthCredentials {
    username: String,
    password: String,
}

pub enum ApiResponse {
    OK {
        jar: Option<CookieJar>,
        redirect: Option<Redirect>,
    },
    Created,
}

impl IntoResponse for ApiResponse {
    fn into_response(self) -> Response {
        match self {
            Self::OK { jar, redirect } => {
                if let Some(jar) = jar {
                    if let Some(redirect) = redirect {
                        return (StatusCode::OK, jar, redirect).into_response();
                    }
                    return (StatusCode::OK, jar).into_response();
                }
                (StatusCode::OK).into_response()
            }
            Self::Created => (StatusCode::CREATED).into_response(),
        }
    }
}

pub enum ApiError {
    BadRequest,
    Forbidden,
    Unauthorised { redirect: Redirect },
    InternalServerError,
    Conflict { message: String },
}

impl IntoResponse for ApiError {
    fn into_response(self) -> Response {
        match self {
            Self::BadRequest => (StatusCode::BAD_REQUEST).into_response(),
            Self::Forbidden => (StatusCode::FORBIDDEN).into_response(),
            Self::Unauthorised { redirect } => (StatusCode::UNAUTHORIZED, redirect).into_response(),
            Self::InternalServerError => (StatusCode::INTERNAL_SERVER_ERROR).into_response(),
            Self::Conflict { message } => (StatusCode::CONFLICT, Json(message)).into_response(),
        }
    }
}

1 post - 1 participant

Read full topic

🏷️ Rust_feed