Improve API error handling
This commit is contained in:
parent
c90d0b70b9
commit
0977a5ffcb
4 changed files with 41 additions and 32 deletions
|
@ -22,4 +22,5 @@ new_mime_guess = "3.0.2"
|
|||
headers = "0.3.5"
|
||||
url = {version = "2.2.2", features = ["serde"]}
|
||||
askama = "0.10"
|
||||
urlencoding = "2.1.0"
|
||||
urlencoding = "2.1.0"
|
||||
thiserror = "1.0.30"
|
|
@ -2,43 +2,50 @@ use std::convert::Infallible;
|
|||
|
||||
use axum::{
|
||||
body::{Bytes, Full},
|
||||
extract::multipart::MultipartError,
|
||||
response::IntoResponse,
|
||||
Json,
|
||||
};
|
||||
use reqwest::StatusCode;
|
||||
use thiserror::Error;
|
||||
|
||||
use super::models::ErrorResponse;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum APIError {
|
||||
#[error("SQL error: {0}")]
|
||||
Sql(#[from] sqlx::Error),
|
||||
#[error("Multipart form error: {0}")]
|
||||
Multipart(#[from] MultipartError),
|
||||
#[error("Bad request: {0}")]
|
||||
BadRequest(String),
|
||||
}
|
||||
|
||||
impl ErrorResponse {
|
||||
fn new(status: StatusCode, message: Option<String>) -> Self {
|
||||
ErrorResponse {
|
||||
let reason = status.canonical_reason().unwrap_or_default();
|
||||
Self {
|
||||
status,
|
||||
error: message,
|
||||
error: message.unwrap_or(reason.to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoResponse for ErrorResponse {
|
||||
impl IntoResponse for APIError {
|
||||
type Body = Full<Bytes>;
|
||||
|
||||
type BodyError = Infallible;
|
||||
|
||||
fn into_response(self) -> axum::http::Response<Self::Body> {
|
||||
let status = self.status.clone();
|
||||
(status, Json(self)).into_response()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<sqlx::Error> for ErrorResponse {
|
||||
fn from(err: sqlx::Error) -> Self {
|
||||
match err {
|
||||
sqlx::Error::RowNotFound => {
|
||||
Self::new(StatusCode::NOT_FOUND, Some("Not found".to_string()))
|
||||
}
|
||||
_ => Self::new(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
Some("Internal Server Error".to_string()),
|
||||
),
|
||||
}
|
||||
let res = match self {
|
||||
APIError::Sql(err) => match err {
|
||||
sqlx::Error::RowNotFound => ErrorResponse::new(StatusCode::NOT_FOUND, None),
|
||||
_ => ErrorResponse::new(StatusCode::INTERNAL_SERVER_ERROR, None),
|
||||
},
|
||||
APIError::Multipart(_) => ErrorResponse::new(StatusCode::INTERNAL_SERVER_ERROR, None),
|
||||
APIError::BadRequest(err) => ErrorResponse::new(StatusCode::BAD_REQUEST, Some(err)),
|
||||
};
|
||||
let status = res.status.clone();
|
||||
(status, Json(res)).into_response()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -88,7 +88,7 @@ pub struct UploadResponse {
|
|||
pub struct ErrorResponse {
|
||||
#[serde(serialize_with = "serialize_status")]
|
||||
pub status: StatusCode,
|
||||
pub error: Option<String>,
|
||||
pub error: String,
|
||||
}
|
||||
|
||||
//Query
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use crate::config::ConfVars;
|
||||
use crate::ipfs::IPFSFile;
|
||||
use crate::v1::models::*;
|
||||
use axum::extract::{ContentLengthLimit, Extension, Multipart, Query};
|
||||
use axum::handler::{get, post};
|
||||
|
@ -7,11 +8,13 @@ use axum::routing::BoxRoute;
|
|||
use axum::{Json, Router};
|
||||
use sqlx::MySqlPool;
|
||||
|
||||
use super::error::APIError;
|
||||
|
||||
async fn meme(
|
||||
params: Query<MemeIDQuery>,
|
||||
Extension(db_pool): Extension<MySqlPool>,
|
||||
Extension(vars): Extension<ConfVars>,
|
||||
) -> Result<impl IntoResponse, ErrorResponse> {
|
||||
) -> Result<impl IntoResponse, APIError> {
|
||||
let meme = Meme::get(params.id, &db_pool, vars.cdn).await?;
|
||||
Ok(Json(MemeResponse {
|
||||
status: 200,
|
||||
|
@ -24,7 +27,7 @@ async fn memes(
|
|||
params: Query<MemeFilterQuery>,
|
||||
Extension(db_pool): Extension<MySqlPool>,
|
||||
Extension(vars): Extension<ConfVars>,
|
||||
) -> Result<impl IntoResponse, ErrorResponse> {
|
||||
) -> Result<impl IntoResponse, APIError> {
|
||||
let memes = Meme::get_all(params.0, &db_pool, vars.cdn).await?;
|
||||
Ok(Json(MemesResponse {
|
||||
status: 200,
|
||||
|
@ -36,7 +39,7 @@ async fn memes(
|
|||
async fn category(
|
||||
params: Query<IDQuery>,
|
||||
Extension(db_pool): Extension<MySqlPool>,
|
||||
) -> Result<impl IntoResponse, ErrorResponse> {
|
||||
) -> Result<impl IntoResponse, APIError> {
|
||||
let category = Category::get(¶ms.id, &db_pool).await?;
|
||||
Ok(Json(CategoryResponse {
|
||||
status: 200,
|
||||
|
@ -47,7 +50,7 @@ async fn category(
|
|||
|
||||
async fn categories(
|
||||
Extension(db_pool): Extension<MySqlPool>,
|
||||
) -> Result<impl IntoResponse, ErrorResponse> {
|
||||
) -> Result<impl IntoResponse, APIError> {
|
||||
let categories = Category::get_all(&db_pool).await?;
|
||||
Ok(Json(CategoriesResponse {
|
||||
status: 200,
|
||||
|
@ -59,7 +62,7 @@ async fn categories(
|
|||
async fn user(
|
||||
params: Query<UserIDQuery>,
|
||||
Extension(db_pool): Extension<MySqlPool>,
|
||||
) -> Result<impl IntoResponse, ErrorResponse> {
|
||||
) -> Result<impl IntoResponse, APIError> {
|
||||
let user = User::get(params.0, &db_pool).await?;
|
||||
Ok(Json(UserResponse {
|
||||
status: 200,
|
||||
|
@ -68,9 +71,7 @@ async fn user(
|
|||
}))
|
||||
}
|
||||
|
||||
async fn users(
|
||||
Extension(db_pool): Extension<MySqlPool>,
|
||||
) -> Result<impl IntoResponse, ErrorResponse> {
|
||||
async fn users(Extension(db_pool): Extension<MySqlPool>) -> Result<impl IntoResponse, APIError> {
|
||||
let users = User::get_all(&db_pool).await?;
|
||||
Ok(Json(UsersResponse {
|
||||
status: 200,
|
||||
|
@ -83,7 +84,7 @@ async fn random(
|
|||
params: Query<MemeFilterQuery>,
|
||||
Extension(db_pool): Extension<MySqlPool>,
|
||||
Extension(vars): Extension<ConfVars>,
|
||||
) -> Result<impl IntoResponse, ErrorResponse> {
|
||||
) -> Result<impl IntoResponse, APIError> {
|
||||
let random = Meme::get_random(params.0, &db_pool, vars.cdn).await?;
|
||||
Ok(Json(MemeResponse {
|
||||
status: 200,
|
||||
|
@ -96,8 +97,8 @@ async fn upload(
|
|||
ContentLengthLimit(mut form): ContentLengthLimit<Multipart, { 1024 * 1024 * 1024 }>,
|
||||
Extension(db_pool): Extension<MySqlPool>,
|
||||
Extension(vars): Extension<ConfVars>,
|
||||
) -> impl IntoResponse {
|
||||
todo!();
|
||||
) -> Result<impl IntoResponse, APIError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
//TODO: Implement upload endpoint
|
||||
|
|
Loading…
Reference in a new issue