Skip to content

Commit

Permalink
docs: enhance framework documentation with detailed examples and arch…
Browse files Browse the repository at this point in the history
…itecture details
  • Loading branch information
zavakid committed Feb 7, 2025
1 parent 25e7e05 commit 1c379e3
Show file tree
Hide file tree
Showing 2 changed files with 173 additions and 80 deletions.
14 changes: 6 additions & 8 deletions crates/http/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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);
Expand Down Expand Up @@ -110,6 +107,7 @@ async fn simple_handler(request: Request<ReqBody>) -> Result<Response<String>, B

Ok(response)
}

```

## Architecture
Expand Down
239 changes: 167 additions & 72 deletions crates/web/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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<User>) -> 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<String>, str2: Option<String>) -> 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<User>) -> 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<User>) -> 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<String>, str2: Option<String>) -> 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<String>, str2: Option<String>) -> String {
println!("receive body: {:?}, {:?}", str, str2);
format!("receive from method: {}\r\n", method)
}

// JSON data handler
async fn handle_json(Json(user): Json<User>) -> 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;
}
Expand All @@ -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>) -> 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<User>) -> 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<MyData>) -> String { ... }
- Path parameters (`Path<T>`)
- Query parameters (`Query<T>`)
- Form data (`Form<T>`)
- JSON (`Json<T>`)
- 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<MyData>) -> 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

Expand Down

0 comments on commit 1c379e3

Please sign in to comment.