Custom query extractor response
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Timo Ley 2022-01-11 19:42:15 +01:00
parent 614c23ca53
commit 1dbdc020b9
4 changed files with 36 additions and 12 deletions

View file

@ -1,2 +1 @@
- Port API to Rust - Return API error response on missing query parameter
- Use IPFS as CDN

View file

@ -2,7 +2,7 @@ use std::convert::Infallible;
use axum::{ use axum::{
body::{Bytes, Full}, body::{Bytes, Full},
extract::multipart::MultipartError, extract::{multipart::MultipartError, rejection::QueryRejection},
response::IntoResponse, response::IntoResponse,
Json, Json,
}; };
@ -28,6 +28,8 @@ pub enum APIError {
Internal(String), Internal(String),
#[error("IPFS error: {0}")] #[error("IPFS error: {0}")]
Ipfs(#[from] IPFSError), Ipfs(#[from] IPFSError),
#[error("Query rejection: {0}")]
Query(#[from] QueryRejection),
} }
impl ErrorResponse { impl ErrorResponse {
@ -59,6 +61,7 @@ impl IntoResponse for APIError {
ErrorResponse::new(StatusCode::INTERNAL_SERVER_ERROR, Some(err)) ErrorResponse::new(StatusCode::INTERNAL_SERVER_ERROR, Some(err))
} }
APIError::Ipfs(_) => ErrorResponse::new(StatusCode::INTERNAL_SERVER_ERROR, None), APIError::Ipfs(_) => ErrorResponse::new(StatusCode::INTERNAL_SERVER_ERROR, None),
APIError::Query(_) => ErrorResponse::new(StatusCode::BAD_REQUEST, None),
}; };
let status = res.status; let status = res.status;
(status, Json(res)).into_response() (status, Json(res)).into_response()

View file

@ -3,4 +3,25 @@ pub mod models;
mod routes; mod routes;
mod sql; mod sql;
use async_trait::async_trait;
use axum::extract::{FromRequest, RequestParts};
pub use routes::routes; pub use routes::routes;
use serde::de::DeserializeOwned;
use self::error::APIError;
pub struct Query<T>(pub T);
#[async_trait]
impl<T, B> FromRequest<B> for Query<T>
where
T: DeserializeOwned,
B: Send,
{
type Rejection = APIError;
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
let query = axum::extract::Query::<T>::from_request(req).await?;
Ok(Self(query.0))
}
}

View file

@ -3,7 +3,7 @@ use crate::ipfs::IPFSFile;
use crate::lib::ExtractIP; use crate::lib::ExtractIP;
use crate::v1::models::*; use crate::v1::models::*;
use axum::extract::{ContentLengthLimit, Extension, Multipart, Query}; use axum::extract::{ContentLengthLimit, Extension, Multipart};
use axum::handler::{get, post}; use axum::handler::{get, post};
use axum::response::IntoResponse; use axum::response::IntoResponse;
use axum::routing::BoxRoute; use axum::routing::BoxRoute;
@ -12,9 +12,10 @@ use hyper::StatusCode;
use sqlx::MySqlPool; use sqlx::MySqlPool;
use super::error::APIError; use super::error::APIError;
use super::Query;
async fn meme( async fn meme(
params: Query<MemeIDQuery>, Query(params): Query<MemeIDQuery>,
Extension(db_pool): Extension<MySqlPool>, Extension(db_pool): Extension<MySqlPool>,
Extension(vars): Extension<ConfVars>, Extension(vars): Extension<ConfVars>,
) -> Result<impl IntoResponse, APIError> { ) -> Result<impl IntoResponse, APIError> {
@ -27,11 +28,11 @@ async fn meme(
} }
async fn memes( async fn memes(
params: Query<MemeFilterQuery>, Query(params): Query<MemeFilterQuery>,
Extension(db_pool): Extension<MySqlPool>, Extension(db_pool): Extension<MySqlPool>,
Extension(vars): Extension<ConfVars>, Extension(vars): Extension<ConfVars>,
) -> Result<impl IntoResponse, APIError> { ) -> Result<impl IntoResponse, APIError> {
let memes = Meme::get_all(params.0, &db_pool, vars.cdn).await?; let memes = Meme::get_all(params, &db_pool, vars.cdn).await?;
Ok(Json(MemesResponse { Ok(Json(MemesResponse {
status: 200, status: 200,
error: None, error: None,
@ -40,7 +41,7 @@ async fn memes(
} }
async fn category( async fn category(
params: Query<IDQuery>, Query(params): Query<IDQuery>,
Extension(db_pool): Extension<MySqlPool>, Extension(db_pool): Extension<MySqlPool>,
) -> Result<impl IntoResponse, APIError> { ) -> Result<impl IntoResponse, APIError> {
let category = Category::get(&params.id, &db_pool).await?; let category = Category::get(&params.id, &db_pool).await?;
@ -63,10 +64,10 @@ async fn categories(
} }
async fn user( async fn user(
params: Query<UserIDQuery>, Query(params): Query<UserIDQuery>,
Extension(db_pool): Extension<MySqlPool>, Extension(db_pool): Extension<MySqlPool>,
) -> Result<impl IntoResponse, APIError> { ) -> Result<impl IntoResponse, APIError> {
let user = User::get(params.0, &db_pool).await?; let user = User::get(params, &db_pool).await?;
Ok(Json(UserResponse { Ok(Json(UserResponse {
status: 200, status: 200,
error: None, error: None,
@ -84,11 +85,11 @@ async fn users(Extension(db_pool): Extension<MySqlPool>) -> Result<impl IntoResp
} }
async fn random( async fn random(
params: Query<MemeFilterQuery>, Query(params): Query<MemeFilterQuery>,
Extension(db_pool): Extension<MySqlPool>, Extension(db_pool): Extension<MySqlPool>,
Extension(vars): Extension<ConfVars>, Extension(vars): Extension<ConfVars>,
) -> Result<impl IntoResponse, APIError> { ) -> Result<impl IntoResponse, APIError> {
let random = Meme::get_random(params.0, &db_pool, vars.cdn).await?; let random = Meme::get_random(params, &db_pool, vars.cdn).await?;
Ok(Json(MemeResponse { Ok(Json(MemeResponse {
status: 200, status: 200,
error: None, error: None,