From 1c379e3e283b5cf466ccacfa9d4adc3f95a95b47 Mon Sep 17 00:00:00 2001 From: zavakid Date: Fri, 7 Feb 2025 15:27:48 +0800 Subject: [PATCH] docs: enhance framework documentation with detailed examples and architecture details --- crates/http/README.md | 14 ++- crates/web/README.md | 239 +++++++++++++++++++++++++++++------------- 2 files changed, 173 insertions(+), 80 deletions(-) diff --git a/crates/http/README.md b/crates/http/README.md index 4856e8f..2d3d25b 100644 --- a/crates/http/README.md +++ b/crates/http/README.md @@ -40,20 +40,16 @@ use std::sync::Arc; use tokio::net::TcpListener; -use tracing::{error, info, warn, Level}; -use tracing_subscriber::FmtSubscriber; use micro_http::connection::HttpConnection; use micro_http::handler::make_handler; use micro_http::protocol::body::ReqBody; +use tracing::{error, info, warn, Level}; +use tracing_subscriber::FmtSubscriber; #[tokio::main] async fn main() { - // Initialize logging - let subscriber = FmtSubscriber::builder() - .with_max_level(Level::INFO) - .finish(); - tracing::subscriber::set_global_default(subscriber) - .expect("setting default subscriber failed"); + let subscriber = FmtSubscriber::builder().with_max_level(Level::INFO).finish(); + tracing::subscriber::set_global_default(subscriber).expect("setting default subscriber failed"); info!(port = 8080, "start listening"); let tcp_listener = match TcpListener::bind("127.0.0.1:8080").await { @@ -77,6 +73,7 @@ async fn main() { let handler = handler.clone(); + // one connection per task tokio::spawn(async move { let (reader, writer) = tcp_stream.into_split(); let connection = HttpConnection::new(reader, writer); @@ -110,6 +107,7 @@ async fn simple_handler(request: Request) -> Result, B Ok(response) } + ``` ## Architecture diff --git a/crates/web/README.md b/crates/web/README.md index 2c2e4cb..df5bb7c 100644 --- a/crates/web/README.md +++ b/crates/web/README.md @@ -5,10 +5,11 @@ A lightweight, modular web framework built on top of micro-http, providing an el ## Features - Built on micro-http for high performance HTTP handling -- Flexible routing with path parameters -- Request/Response abstraction -- Async/await support -- Extensible architecture +- Flexible routing with path parameters and filters +- Type-safe request/response handling with automatic data extraction +- Async/await support throughout +- Extensible architecture with decorators and middleware +- Built-in support for common tasks (compression, date headers, etc.) ## Quick Start @@ -25,96 +26,179 @@ tokio = { version = "1", features = ["rt-multi-thread", "net", "io-util", "macro Here's a simple hello world example: ```rust -use micro_web::wrapper::DateWrapper; -use micro_web::router::{get, Router}; +//! Basic example demonstrating how to create a simple web server using micro_web. +//! This example shows: +//! - How to define route handlers +//! - How to set up a router with middleware +//! - How to configure and start a server + +use micro_web::router::{get, Router}; use micro_web::{handler_fn, Server}; +use micro_web::date::DateServiceDecorator; -// Handler function +/// A simple handler that returns "hello world" async fn hello_world() -> &'static str { "hello world" } -// Default 404 handler +/// Default handler for unmatched handlers (404 responses) +/// +/// This handler is called when no other handlers match the incoming request async fn default_handler() -> &'static str { "404 not found" } #[tokio::main] async fn main() { - // Create router + // Create a new router using the builder let router = Router::builder() + // Add a route that matches GET requests to the root path "/" + // handler_fn converts our async function into a handler .route("/", get(handler_fn(hello_world))) - .wrap(DateWrapper) + // Add middleware that will add date headers to responses + .with_decorator(DateServiceDecorator) .build(); - // Configure and start server + // Configure and start the server Server::builder() + // Attach our router to handle incoming requests .router(router) + // Set the address and port to listen on .bind("127.0.0.1:3000") + // Set a handler for requests that don't match any routes .default_handler(handler_fn(default_handler)) + // Build the server .build() .unwrap() + // Start the server and wait for it to finish .start() .await; } + ``` ### Advanced Example -Here's a more complete example showing different request handlers and data extraction: +Here's a more complete example showing different request handlers, data extraction, and middleware: ```rust +use http::Method; use micro_web::extract::{Form, Json}; -use micro_web::filter::header; -use micro_web::wrapper::EncodeWrapper; +use micro_web::router::filter::header; use micro_web::router::{get, post, Router}; +use micro_web::encoding::encoder::EncodeDecorator; use micro_web::{handler_fn, Server}; use serde::Deserialize; -// Data structure for form/JSON extraction -#[derive(Deserialize)] -struct User { +/// User struct for demonstrating data extraction +#[allow(dead_code)] +#[derive(Deserialize, Debug)] +pub struct User { name: String, zip: String, } -// Form data handler -async fn handle_form(Form(user): Form) -> String { - format!("Received user: {:?}", user) +/// Simple GET handler that demonstrates method and optional string extraction +/// +/// Example request: +/// ```bash +/// curl http://127.0.0.1:8080/ +/// ``` +async fn simple_get(method: &Method, str: Option, str2: Option) -> String { + println!("receive body: {}, {}", str.is_some(), str2.is_some()); + format!("receive from method: {}\r\n", method) +} + +/// Handler that extracts form data into a User struct +/// +/// Example request: +/// ```bash +/// curl -v -H "Transfer-Encoding: chunked" \ +/// -d "name=hello&zip=world&c=abc" \ +/// http://127.0.0.1:8080/ +/// ``` +async fn simple_handler_form_data(method: &Method, Form(user): Form) -> String { + format!("receive from method: {}, receive use: {:?}\r\n", method, user) +} + +/// Handler that extracts JSON data into a User struct +/// +/// Example request: +/// ```bash +/// curl -v -H "Transfer-Encoding: chunked" \ +/// -H 'Content-Type: application/json' \ +/// -d '{"name":"hello","zip":"world"}' \ +/// http://127.0.0.1:8080/ +/// ``` +async fn simple_handler_json_data(method: &Method, Json(user): Json) -> String { + format!("receive from method: {}, receive use: {:?}\r\n", method, user) +} + +/// Simple POST handler demonstrating method and optional string extraction +/// +/// Example request: +/// ```bash +/// curl -X POST http://127.0.0.1:8080/ +/// ``` +async fn simple_handler_post(method: &Method, str: Option, str2: Option) -> String { + println!("receive body: {:?}, {:?}", str, str2); + format!("receive from method: {}\r\n", method) +} + +/// Another GET handler for a different route +/// +/// Example request: +/// ```bash +/// curl http://127.0.0.1:8080/4 +/// ``` +async fn simple_another_get(method: &Method, str: Option, str2: Option) -> String { + println!("receive body: {:?}, {:?}", str, str2); + format!("receive from method: {}\r\n", method) } -// JSON data handler -async fn handle_json(Json(user): Json) -> String { - format!("Received user: {:?}", user) +/// Default handler for unmatched routes +/// +/// Example request: +/// ```bash +/// curl http://127.0.0.1:8080/non-existent-path +/// ``` +async fn default_handler() -> &'static str { + "404 not found" } #[tokio::main] async fn main() { + // Build router with multiple routes and handlers let router = Router::builder() // Basic GET route - .route("/", get(handler_fn(hello_world))) - - // POST route for form data + .route("/", get(handler_fn(simple_get))) + // POST route for form data with content-type filter .route( - "/user", - post(handler_fn(handle_form)) + "/", + post(handler_fn(simple_handler_form_data)) .with(header(http::header::CONTENT_TYPE, mime::APPLICATION_WWW_FORM_URLENCODED.as_ref())), ) - - // POST route for JSON data + // POST route for JSON data with content-type filter .route( - "/user", - post(handler_fn(handle_json)) + "/", + post(handler_fn(simple_handler_json_data)) .with(header(http::header::CONTENT_TYPE, mime::APPLICATION_JSON.as_ref())), ) - - .wrap(EncodeWrapper) + // Default POST route + .route("/", post(handler_fn(simple_handler_post))) + // Additional GET route + .route("/4", get(handler_fn(simple_another_get))) + // Add response encoding wrapper + .with_decorator(EncodeDecorator) .build(); + // Configure and start the server Server::builder() .router(router) .bind("127.0.0.1:8080") - .build()? + .default_handler(handler_fn(default_handler)) + .build() + .unwrap() .start() .await; } @@ -125,72 +209,83 @@ async fn main() { ### Router The router provides flexible request routing with support for: -- Path parameters -- Query parameters -- HTTP method matching -- Wildcard routes +- Path parameters and pattern matching +- Query parameters extraction +- HTTP method filtering +- Header-based filtering +- Composable filter chains - Nested routers -- Request filters - -### Middleware - -Middleware provides a way to process requests/responses before they reach your handlers. Built-in middleware includes: - -- `DateWrapper`: Adds date headers to responses -- `EncodeWrapper`: Handles response encoding -- you can custom others through `Wrapper` - -Example of adding middleware: - -```rust -router.builder() - .wrap(DateWrapper) - .wrap(EncodeWrapper) - .build(); -``` ### Request Handlers -Handlers can be created from async functions using `handler_fn`: +Handlers can be created from async functions using `handler_fn`. The framework supports automatic type conversion for both request data extraction and response generation: ```rust -async fn my_handler() -> String { - "Hello".to_string() +// Simple handler returning a string +async fn hello() -> &'static str { + "Hello" +} + +// Handler with path parameter +async fn user_info(Path(user_id): Path) -> String { + format!("User ID: {}", user_id) } -router.route("/", get(handler_fn(my_handler))); +// Handler with JSON request body +async fn create_user(Json(user): Json) -> impl Responder { + // Process user... + StatusCode::CREATED +} ``` ### Data Extraction -The framework supports extracting data from requests in different formats: +The framework provides type-safe extractors for common data formats: -```rust -// Form data extraction -async fn handle_form(Form(data): Form) -> String { ... } +- Path parameters (`Path`) +- Query parameters (`Query`) +- Form data (`Form`) +- JSON (`Json`) +- Headers (`HeaderMap`) +- Custom extractors via `FromRequest` trait + +### Middleware and Decorators + +The framework uses a flexible decorator pattern for middleware: -// JSON extraction -async fn handle_json(Json(data): Json) -> String { ... } +```rust +// Add multiple decorators +router.builder() + .with_decorator(DateServiceDecorator) // Adds Date header + .with_decorator(CompressionDecorator) // Handles response compression + .build(); ``` +Built-in decorators include: +- `DateServiceDecorator`: Adds date headers to responses +- `CompressionDecorator`: Handles response compression +- Custom decorators via `Decorator` trait + ## Architecture The framework is built with a modular architecture: -- `router`: Request routing and handler dispatch -- `wrapper`: Middleware processing pipeline +- `router`: Request routing and filter system - `handler`: Request handler traits and implementations -- `response`: Response building and formatting +- `extract`: Type-safe data extraction +- `decorator`: Middleware and response transformation +- `responder`: Response generation ## Performance Built on micro-http, the framework maintains high performance through: - Zero-allocation routing where possible -- Efficient middleware chaining +- Efficient middleware chaining via decorators - Minimal copying of request/response data - Async I/O throughout -- Smart memory management +- Smart date handling with cached timestamps +- Optimized header handling ## Contributing