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