Skip to content

Commit

Permalink
doc(http): add from_request document
Browse files Browse the repository at this point in the history
  • Loading branch information
zavakid committed Dec 6, 2024
1 parent 5dca2ea commit a3b314f
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 7 deletions.
67 changes: 66 additions & 1 deletion crates/web/src/extract/from_request.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,81 @@
//! Typed data extraction from HTTP requests
//!
//! This module provides the [`FromRequest`] trait which enables extracting typed data
//! from HTTP requests. It serves as the foundation for the parameter extraction system,
//! allowing handlers to receive strongly-typed parameters.
//!
//! # Examples
//!
//! ```ignore
//! use micro_web::extract::FromRequest;
//! use micro_web::RequestContext;
//! use async_trait::async_trait;
//! use micro_http::protocol::ParseError;
//! use crate::body::OptionReqBody;
//!
//! struct CustomType(String);
//!
//! #[async_trait]
//! impl FromRequest for CustomType {
//! type Output<'r> = CustomType;
//! type Error = ParseError;
//!
//! async fn from_request<'r>(
//! req: &'r RequestContext,
//! body: OptionReqBody
//! ) -> Result<Self::Output<'r>, Self::Error> {
//! // Extract and validate data from request
//! let data = String::from_request(req, body).await?;
//! Ok(CustomType(data))
//! }
//! }
//! ```
use crate::body::OptionReqBody;
use crate::responder::Responder;
use crate::{RequestContext, ResponseBody};
use async_trait::async_trait;
use http::{Response, StatusCode};
use micro_http::protocol::ParseError;

/// Trait for extracting typed data from an HTTP request
///
/// This trait enables request handlers to receive typed parameters extracted from
/// the request. It supports extracting data from:
/// - Request headers
/// - URL parameters
/// - Query strings
/// - Request body (as JSON, form data, etc.)
///
/// # Type Parameters
///
/// * `Output<'r>`: The extracted type, potentially borrowing from the request
/// * `Error`: The error type returned if extraction fails
///
/// # Implementing
///
/// When implementing this trait, consider:
/// - Lifetime requirements of the extracted data
/// - Proper error handling and conversion
/// - Performance implications of extraction
#[async_trait]
pub trait FromRequest {
/// The type that will be extracted from the request
type Output<'r>: Send;

/// The error type returned if extraction fails
type Error: Responder + Send;

/// Extracts the type from the request
///
/// # Arguments
///
/// * `req` - The request context containing headers and other metadata
/// * `body` - Optional request body that can be consumed
async fn from_request<'r>(req: &'r RequestContext, body: OptionReqBody) -> Result<Self::Output<'r>, Self::Error>;
}

/// Implementation for Option<T> to make extractions optional
#[async_trait]
impl<T> FromRequest for Option<T>
where
Expand Down Expand Up @@ -41,6 +105,7 @@ where
}
}

/// Implementation for unit type to support handlers without parameters
#[async_trait]
impl FromRequest for () {
type Output<'r> = ();
Expand All @@ -51,6 +116,7 @@ impl FromRequest for () {
}
}

/// Responder implementation for ParseError to convert parsing errors to responses
impl Responder for ParseError {
fn response_to(self, req: &RequestContext) -> Response<ResponseBody> {
match self {
Expand All @@ -67,7 +133,6 @@ impl Responder for ParseError {
}
ParseError::InvalidBody { .. } => (StatusCode::BAD_REQUEST, "invalid body").response_to(req),
ParseError::Io { .. } => (StatusCode::BAD_REQUEST, "connection error").response_to(req),

}
}
}
13 changes: 7 additions & 6 deletions crates/web/src/extract/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ mod extract_url;
mod from_request;

pub use from_request::FromRequest;
use serde::Deserialize;

/// Represented as form data
///
/// when `post` as a `application/x-www-form-urlencoded`, we can using this struct to inject data,
/// note: the struct must impl [`de::Deserialize`] and [`Send`]
/// note: the struct must impl [`serde::Deserialize`] and [`Send`]
///
/// # Example
/// ```
Expand All @@ -26,12 +27,12 @@ pub use from_request::FromRequest;
/// format!("received params: {:?}", params)
/// }
/// ```
pub struct Form<T>(pub T);
pub struct Form<T>(pub T) where T: for<'de> Deserialize<'de> + Send;

/// Represented as json data
///
/// when `post` as a `application/json`, we can using this struct to inject data,
/// note: the struct must impl [`de::Deserialize`] and [`Send`]
/// note: the struct must impl [`serde::Deserialize`] and [`Send`]
///
/// # Example
/// ```
Expand All @@ -48,12 +49,12 @@ pub struct Form<T>(pub T);
/// format!("received params: {:?}", params)
/// }
/// ```
pub struct Json<T>(pub T);
pub struct Json<T>(pub T) where T: for<'de> Deserialize<'de> + Send;

/// Represented as url query data
///
/// when request with url query, we can using this struct to inject data,
/// note: the struct must impl [`de::Deserialize`] and [`Send`]
/// note: the struct must impl [`serde::Deserialize`] and [`Send`]
///
/// # Example
/// ```
Expand All @@ -70,4 +71,4 @@ pub struct Json<T>(pub T);
/// format!("received params: {:?}", params)
/// }
/// ```
pub struct Query<T>(pub T);
pub struct Query<T>(pub T) where T: for<'de> Deserialize<'de> + Send;

0 comments on commit a3b314f

Please sign in to comment.