feat: add pagination helper and integrate serde_urlencoded for query extraction
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -5446,6 +5446,7 @@ dependencies = [
|
|||||||
"sea-orm",
|
"sea-orm",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"serde_urlencoded",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tower",
|
"tower",
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ jsonwebtoken = { version = "10.2.0", features = ["rust_crypto"] }
|
|||||||
uuid = { version = "1.19.0", features = ["v4", "serde", "fast-rng"] }
|
uuid = { version = "1.19.0", features = ["v4", "serde", "fast-rng"] }
|
||||||
tower-http = { version = "0.6.8", features = ["cors"] }
|
tower-http = { version = "0.6.8", features = ["cors"] }
|
||||||
reqwest = { version = "^0.12", features = ["json", "multipart", "stream"] }
|
reqwest = { version = "^0.12", features = ["json", "multipart", "stream"] }
|
||||||
|
serde_urlencoded = { version = "0.7.1" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tempfile = "3"
|
tempfile = "3"
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
mod auth;
|
mod auth;
|
||||||
mod health;
|
mod health;
|
||||||
|
mod helper;
|
||||||
mod openapi;
|
mod openapi;
|
||||||
mod restricted;
|
mod restricted;
|
||||||
|
|
||||||
|
|||||||
1
apps/api/src/routes/api/helper.rs
Normal file
1
apps/api/src/routes/api/helper.rs
Normal file
@@ -0,0 +1 @@
|
|||||||
|
pub mod pagination;
|
||||||
65
apps/api/src/routes/api/helper/pagination.rs
Normal file
65
apps/api/src/routes/api/helper/pagination.rs
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
use axum::{
|
||||||
|
extract::FromRequestParts,
|
||||||
|
http::{StatusCode, request::Parts},
|
||||||
|
};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, utoipa::ToSchema)]
|
||||||
|
/// Pagination parameters for API requests
|
||||||
|
pub struct Pagination {
|
||||||
|
/// Page number (1-based)
|
||||||
|
pub page: u32,
|
||||||
|
/// Items per page
|
||||||
|
pub per_page: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Pagination {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
page: 1,
|
||||||
|
per_page: 20,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, utoipa::ToSchema)]
|
||||||
|
/// Pagination information included in API responses
|
||||||
|
pub struct PaginationInfo {
|
||||||
|
/// Total number of items
|
||||||
|
pub total_items: u64,
|
||||||
|
/// Total number of pages
|
||||||
|
pub total_pages: u32,
|
||||||
|
/// Current page number
|
||||||
|
pub current_page: u32,
|
||||||
|
/// Items per page
|
||||||
|
pub per_page: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Extractor for pagination parameters from query string
|
||||||
|
pub struct ExtractPagination(pub Pagination);
|
||||||
|
|
||||||
|
impl<S> FromRequestParts<S> for ExtractPagination
|
||||||
|
where
|
||||||
|
S: Send + Sync,
|
||||||
|
{
|
||||||
|
type Rejection = (StatusCode, &'static str);
|
||||||
|
|
||||||
|
async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result<Self, Self::Rejection> {
|
||||||
|
let query = parts.uri.query().unwrap_or("");
|
||||||
|
let pagination: Pagination = serde_urlencoded::from_str(query).unwrap_or_default();
|
||||||
|
|
||||||
|
// validation
|
||||||
|
if pagination.page == 0 {
|
||||||
|
return Err((StatusCode::BAD_REQUEST, "page must be greater than 0"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if pagination.per_page < 1 || pagination.per_page > 100 {
|
||||||
|
return Err((
|
||||||
|
StatusCode::BAD_REQUEST,
|
||||||
|
"per_page must be between 1 and 100",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(ExtractPagination(pagination))
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user