Expand description
Viz
Fast, robust, flexible, lightweight web framework for Rust.
§Features
-
Safety
#![forbid(unsafe_code)] -
Lightweight
-
Simple + Flexible
Handler&Middleware -
Handy
Extractors -
Robust
Routing -
Supports Tower
Service
§Hello Viz
use std::net::SocketAddr;
use tokio::net::TcpListener;
use viz::{serve, Request, Result, Router};
async fn index(_: Request) -> Result<&'static str> {
Ok("Hello, Viz!")
}
#[tokio::main]
async fn main() -> Result<()> {
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
let listener = TcpListener::bind(addr).await?;
println!("listening on http://{addr}");
let app = Router::new().get("/", index);
if let Err(e) = serve(listener, app).await {
println!("{e}");
}
Ok(())
}More examples can be found here.
§Handler
A simple pattern async fn(Request) -> Result<Response> is used to handle requests in Viz.
§Simple handlers
async fn index(_: Request) -> Result<Response> {
Ok(Response::text("Hello, World!"))
}
async fn about(_: Request) -> Result<&'static str> {
Ok("About Me!")
}
async fn not_found(_: Request) -> Result<impl IntoResponse> {
Ok("Not Found!")
}§Implemented Handler trait
The types can implement the Handler trait to customize handlers.
#[derive(Clone)]
struct MyHandler {
code: Arc<AtomicUsize>,
}
#[async_trait]
impl Handler<Request> for MyHandler {
type Output = Result<Response>;
async fn call(&self, req: Request) -> Self::Output {
let path = req.path();
let method = req.method().clone();
let code = self.code.fetch_add(1, Ordering::SeqCst);
Ok(format!("code = {}, method = {}, path = {}", code, method, path).into_response())
}
}§With extractors
Supports handler with zero or one or more extractors.
Extractors must implement the FromRequest trait for extracting data from the Request.
When joining the routing system, it should first be converted to a handler using
into_handler.
async fn show_todo(Params(id): Params<u64>) -> Result<impl IntoResponse> {
Ok(format!("Hi, NO.{}", id))
}
let app = Router::new().route("/:id", get(show_todo.into_handler()));§Why not supports handler with extractors by default?
Viz allows more flexibility in organizing your code.
async fn show_user(mut req: Request) -> Result<Response> {
let Params(id) = req.extract::<Params<u64>>().await?;
Ok(format!("post {}", id).into_response())
}
async fn show_user_ext(Params(id): Params<u64>) -> Result<impl IntoResponse> {
Ok(format!("Hi, NO.{}", id))
}
async fn show_user_wrap(req: Request) -> Result<impl IntoResponse> {
// https://github.com/rust-lang/rust/issues/48919
// show_user_ext.call(req).await
FnExt::call(&show_user_ext, req).await
}
let app = Router::new()
.get("/users/:id", show_user)
.get("/users_wrap/:id", show_user_wrap)
.get("/users_ext/:id", show_user_ext.into_handler());§Support process macros?
Support, you can enable the macros feature, using #[handler].
But it’s still recommended to use into_handler for conversion.
#[handler]
async fn show_user(Params(id): Params<u64>) -> Result<impl IntoResponse> {
Ok(format!("Hi, NO.{}", id))
}
async fn update_user(Params(id): Params<u64>) -> Result<impl IntoResponse> {
Ok(format!("Updated, NO.{}", id))
}
let app = Router::new()
.get("/users/:id", show_user)
.patch("/users/:id", update_user.into_handler());§Chaining and composing handlers
The HandlerExt is an extension trait for Handlers that provides a variety of convenient
combinator functions.
Likes the FutureExt and StreamExt traits.
async fn index(_: Request) -> Result<Response> {
Ok(Response::text("hyper"))
}
async fn before(req: Request) -> Result<Request> {
if req.method() == Method::POST {
Ok(req)
} else {
Err(StatusCode::METHOD_NOT_ALLOWED.into_error())
}
}
async fn around<H>((req, handler): Next<Request, H>) -> Result<Response>
where
H: Handler<Request, Output = Result<Response>>,
{
// before ...
let result = handler.call(req).await;
// after ...
result
}
async fn after(result: Result<Response>) -> Result<Response> {
result.map(|mut res| {
*res.status_mut() = StatusCode::NO_CONTENT;
res
})
}
let routing = Router::new()
.get("/", index.before(before).around(around).after(after));§Middleware
Viz’s middleware and handlers share a common Handler trait,
so its very easy to implement and extend the middleware.
We can add middleware to a single handler, or to all handlers.
We can also use Transform trait for wrapping the inner handler during construction.
async fn index(_: Request) -> Result<Response> {
Ok(StatusCode::OK.into_response())
}
async fn not_found(_: Request) -> Result<impl IntoResponse> {
Ok(StatusCode::OK)
}
async fn show_user(Params(id): Params<u64>) -> Result<impl IntoResponse> {
Ok(format!("post {}", id))
}
// middleware fn
async fn around<H>((req, handler): Next<Request, H>) -> Result<Response>
where
H: Handler<Request, Output = Result<Response>>,
{
// before ...
let result = handler.call(req).await;
// after ...
result
}
// middleware struct
#[derive(Clone)]
struct MyMiddleware {}
#[async_trait]
impl<H> Handler<Next<Request, H>> for MyMiddleware
where
H: Handler<Request>,
{
type Output = H::Output;
async fn call(&self, (i, h): Next<Request, H>) -> Self::Output {
h.call(i).await
}
}
// A configuration for Timeout Middleware
struct Timeout {
delay: Duration,
}
impl Timeout {
pub fn new(secs: u64) -> Self {
Self { delay: Duration::from_secs(secs) }
}
}
impl<H: Clone> Transform<H> for Timeout {
type Output = TimeoutMiddleware<H>;
fn transform(&self, h: H) -> Self::Output {
TimeoutMiddleware(h, self.delay)
}
}
// Timeout Middleware
#[derive(Clone)]
struct TimeoutMiddleware<H>(H, Duration);
#[async_trait]
impl<H> Handler<Request> for TimeoutMiddleware<H>
where
H: Handler<Request>,
{
type Output = H::Output;
async fn call(&self, req: Request) -> Self::Output {
self.0.call(req).await
}
}
let app = Router::new()
.get("/", index
// handler level
.around(around)
.around(MyMiddleware {})
.with(Timeout::new(1))
)
.route("/users/:id", get(
show_user
.into_handler()
.map_into_response()
// handler level
.around(around)
.with(Timeout::new(0))
)
.post(
(|_| async { Ok(Response::text("update")) })
// handler level
.around(around)
.with(Timeout::new(0))
)
// route level
.with_handler(MyMiddleware {})
.with(Timeout::new(2))
)
.get("/*", not_found
.map_into_response()
// handler level
.around(around)
.around(MyMiddleware {})
)
// router level
.with_handler(around)
.with_handler(MyMiddleware {})
.with(Timeout::new(4));§Extractors
Extracts data from the Request.
struct Counter(u16);
impl FromRequest for Counter {
type Error = Infallible;
async fn extract(req: &mut Request) -> Result<Self, Self::Error> {
let c = get_query_param(req.query_string());
Ok(Counter(c))
}
}
fn get_query_param(query: Option<&str>) -> u16 {
let query = query.unwrap_or("");
let q = if let Some(pos) = query.find('q') {
query.split_at(pos + 2).1.parse().unwrap_or(1)
} else {
1
};
cmp::min(500, cmp::max(1, q))
}§Routing
The Viz router recognizes URLs and dispatches them to a handler.
§Simple routes
async fn index(_: Request) -> Result<Response> {
Ok(().into_response())
}
let root = Router::new()
.get("/", index)
.route("/about", get(|_| async { Ok("about") }));
let search = Router::new()
.route("/", Route::new().get(|_| async { Ok("search") }));§CRUD, Verbs
Adds routes with the HTTP method.
async fn index_todos(_: Request) -> Result<impl IntoResponse> {
Ok(())
}
async fn create_todo(_: Request) -> Result<&'static str> {
Ok("created")
}
async fn new_todo(_: Request) -> Result<Response> {
Ok(Response::html(r#"
<form method="post" action="/">
<input name="todo" />
<button type="submit">Create</button>
</form>
"#))
}
async fn show_todo(mut req: Request) -> Result<Response> {
let Params(id): Params<u64> = req.extract().await?;
Ok(Response::text(format!("todo's id is {}", id)))
}
async fn update_todo(_: Request) -> Result<()> {
Ok(())
}
async fn destroy_todo(_: Request) -> Result<()> {
Ok(())
}
async fn edit_todo(_: Request) -> Result<()> {
Ok(())
}
let todos = Router::new()
.route("/", get(index_todos).post(create_todo))
.post("/new", new_todo)
.route("/:id", get(show_todo).patch(update_todo).delete(destroy_todo))
.get("/:id/edit", edit_todo);§Resources
// GET `/search`
async fn search_users(_: Request) -> Result<Response> {
Ok(Response::json::<Vec<u64>>(vec![])?)
}
// GET `/`
async fn index_users(_: Request) -> Result<Response> {
Ok(Response::json::<Vec<u64>>(vec![])?)
}
// GET `/new`
async fn new_user(_: Request) -> Result<&'static str> {
Ok("User Form")
}
// POST `/`
async fn create_user(_: Request) -> Result<&'static str> {
Ok("Created User")
}
// GET `/user_id`
async fn show_user(_: Request) -> Result<&'static str> {
Ok("User ID 007")
}
// GET `/user_id/edit`
async fn edit_user(_: Request) -> Result<&'static str> {
Ok("Edit User Form")
}
// PUT `/user_id`
async fn update_user(_: Request) -> Result<&'static str> {
Ok("Updated User")
}
// DELETE `/user_id`
async fn delete_user(_: Request) -> Result<&'static str> {
Ok("Deleted User")
}
let users = Resources::default()
.named("user")
.route("/search", get(search_users))
.index(index_users)
.new(new_user)
.create(create_user)
.show(show_user)
.edit(edit_user)
.update(update_user)
.destroy(delete_user);§Nested
async fn not_found(_: Request) -> Result<impl IntoResponse> {
Ok(StatusCode::NOT_FOUND)
}
let app = Router::new()
.nest("/", root)
.nest("/search", search)
.nest("/todos", todos.clone())
.nest("/users", users.nest("todos", todos))
.route("/*", any(not_found));Modules§
- future
- Asynchronous values.
- handler
- Traits and types for handling an HTTP.
- handlers
handlers - A collection of handlers for Viz.
- header
- HTTP header types
- headers
- Typed HTTP Headers
- middleware
- Built-in Middleware.
- tls
native-tlsorrustls - TLS A TLS listener wrapper.
- types
- Built-in Extractors types and traits.
Structs§
- BoxHandler
- A
Clone+SendboxedHandler. - Bytes
- A cheaply cloneable and sliceable chunk of contiguous memory.
- Bytes
Mut - A unique reference to a contiguous slice of memory.
- Incoming
- A stream of
Bytes, used when receiving bodies from the network. - Io
- A wrapper that implements Tokio’s IO traits for an inner type that implements hyper’s IO traits, or vice versa (implements hyper’s IO traits for a type that implements Tokio’s IO traits).
- Method
- The Request Method (VERB)
- Path
- Matched route path infomation.
- Path
Tree - A path tree.
- Resources
- A resourceful route provides a mapping between HTTP verbs and URLs to handlers.
- Responder
- Handles the HTTP
Requestand retures the HTTPResponse. - Route
- A collection of verb-handler pair.
- Router
- A routes collection.
- Server
- A listening HTTP server that accepts connections.
- Status
Code - An HTTP status code (
status-codein RFC 9110 et al.). - Tree
- Store all final routes.
Enums§
- Body
- A body for HTTP
Requestand HTTPResponse. - Body
State - A body state.
- Error
- Represents errors that can occur handling application.
Traits§
- FnExt
- A handler with extractors.
- From
Request - An interface for extracting data from the HTTP
Request. - Future
- A future represents an asynchronous computation, commonly obtained by use of
async. - Handler
- A simplified asynchronous interface for handling input and output.
- Handler
Ext - The
HandlerExttrait, which provides adapters for chaining and composing handlers. - Http
Body - Trait representing a streaming body of a Request or Response.
- Into
Handler - The trait implemented by types that can be converted to a
Handler. - Into
Response - Trait implemented by types that can be converted to an HTTP
Response. - Listener
- A trait for a listener:
TcpListenerandUnixListener. - Request
Ext - The
RequestExtension. - Request
Limits Ext - The
RequestExtension with a limited body. - Response
Ext - The
ResponseExtension. - Transform
- Then
Transformtrait defines the interface of a handler factory that wraps inner handler to a Handler during construction.
Functions§
- any
- Creates a route with a handler and any HTTP verbs.
- connect
- Creates a route with a handler and HTTP
CONNECTverb pair. - delete
- Creates a route with a handler and HTTP
DELETEverb pair. - get
- Creates a route with a handler and HTTP
GETverb pair. - head
- Creates a route with a handler and HTTP
HEADverb pair. - on
- Creates a route with a handler and HTTP verb pair.
- options
- Creates a route with a handler and HTTP
OPTIONSverb pair. - patch
- Creates a route with a handler and HTTP
PATCHverb pair. - post
- Creates a route with a handler and HTTP
POSTverb pair. - put
- Creates a route with a handler and HTTP
PUTverb pair. - serve
- Starts a server and serves the connections.
- trace
- Creates a route with a handler and HTTP
TRACEverb pair.
Type Aliases§
- BoxError
- An owned dynamically typed
StdError. - Next
- Represents a middleware parameter, which is a tuple that includes Requset and
BoxHandler. - Request
- Represents an HTTP Request.
- Response
- Represents an HTTP Response.
- Result
- Represents either success (Ok) or failure (Err).
Attribute Macros§
- async_
trait - handler
macros - Transforms
extract-handlerto a Handler instance.