[−][src]Crate roa
Introduction
Roa is an async web framework inspired by koajs, lightweight but powerful.
Application
A Roa application is a structure containing a middleware group which composes and executes middleware functions in a stack-like manner.
The obligatory hello world application:
use roa::{App, Context}; use roa::preload::*; use log::info; use std::error::Error as StdError; async fn end(ctx: &mut Context<()>) -> roa::Result { ctx.write_text("Hello, World"); Ok(()) } #[async_std::main] async fn main() -> Result<(), Box<dyn StdError>> { let app = App::new(()).end(end); app.listen("127.0.0.1:8000", |addr| { info!("Server is listening on {}", addr) })? .await?; Ok(()) }
Cascading
Like koajs, middleware suspends and passes control to "downstream" by invoking next.await
.
Then control flows back "upstream" when next.await
returns.
The following example responds with "Hello World", however first the request flows through the x-response-time and logging middleware to mark when the request started, then continue to yield control through the response middleware. When a middleware invokes next the function suspends and passes control to the next middleware defined. After there are no more middleware to execute downstream, the stack will unwind and each middleware is resumed to perform its upstream behaviour.
use roa::{App, Context, Next}; use roa::preload::*; use log::info; use std::error::Error as StdError; use std::time::Instant; #[async_std::main] async fn main() -> Result<(), Box<dyn StdError>> { let app = App::new(()) .gate(logger) .gate(x_response_time) .end(response); app.listen("127.0.0.1:8000", |addr| { info!("Server is listening on {}", addr) })? .await?; Ok(()) } async fn logger(ctx: &mut Context<()>, next: Next<'_>) -> roa::Result { next.await?; let rt = ctx.resp.must_get("x-response-time")?; info!("{} {} - {}", ctx.method(), ctx.uri(), rt); Ok(()) } async fn x_response_time(ctx: &mut Context<()>, next: Next<'_>) -> roa::Result { let start = Instant::now(); next.await?; let ms = start.elapsed().as_millis(); ctx.resp.insert("x-response-time", format!("{}ms", ms))?; Ok(()) } async fn response(ctx: &mut Context<()>) -> roa::Result { ctx.write_text("Hello, World"); Ok(()) }
Error Handling
You can catch or straightly throw an error returned by next.
use roa::{App, Context, Next, throw}; use roa::preload::*; use roa::http::StatusCode; use async_std::task::spawn; use log::info; #[async_std::main] async fn main() -> Result<(), Box<dyn std::error::Error>> { let app = App::new(()) .gate(catch) .gate(not_catch) .end(error); app.listen("127.0.0.1:8000", |addr| { info!("Server is listening on {}", addr) })? .await?; Ok(()) } async fn catch(_ctx: &mut Context<()>, next: Next<'_>) -> roa::Result { // catch if let Err(err) = next.await { // teapot is ok if err.status_code != StatusCode::IM_A_TEAPOT { return Err(err); } } Ok(()) } async fn not_catch(ctx: &mut Context<()>, next: Next<'_>) -> roa::Result { next.await?; // just throw unreachable!() } async fn error(ctx: &mut Context<()>) -> roa::Result { throw!(StatusCode::IM_A_TEAPOT, "I'm a teapot!") }
error_handler
App has an error_handler to handle error thrown by the top middleware. This is the error_handler:
use roa_core::{Context, Error, Result, ErrorKind}; pub async fn error_handler<S>(ctx: &mut Context<S>, err: Error) -> Result { // set status code to err.status_code. ctx.resp.status = err.status_code; if err.expose { // write err.message to response body if err.expose. ctx.resp.write(err.message.clone()); } if err.kind == ErrorKind::ServerError { // thrown to hyper Err(err) } else { // caught Ok(()) } }
The error thrown by this error_handler will be handled by hyper.
Router.
Roa provides a configurable and nestable router.
use roa::preload::*; use roa::router::{Router, get}; use roa::{App, Context}; use async_std::task::spawn; use log::info; #[async_std::main] async fn main() -> Result<(), Box<dyn std::error::Error>> { let router = Router::new() .on("/:id", get(end)); // get dynamic "/:id" let app = App::new(()) .end(router.routes("/user")?); // route with prefix "/user" app.listen("127.0.0.1:8000", |addr| { info!("Server is listening on {}", addr) })? .await?; Ok(()) } async fn end(ctx: &mut Context<()>) -> roa::Result { // get "/user/1", then id == 1. let id: u64 = ctx.must_param("id")?.parse()?; // do something Ok(()) }
Query
Roa provides a middleware query_parser
.
use roa::preload::*; use roa::query::query_parser; use roa::{App, Context}; use async_std::task::spawn; use log::info; async fn must(ctx: &mut Context<()>) -> roa::Result { // request "/?id=1", then id == 1. let id: u64 = ctx.must_query("id")?.parse()?; Ok(()) } #[async_std::main] async fn main() -> Result<(), Box<dyn std::error::Error>> { let app = App::new(()) .gate(query_parser) .end(must); app.listen("127.0.0.1:8080", |addr| { info!("Server is listening on {}", addr) })? .await?; Ok(()) }
Other modules
- body: dealing with body more conviniently.
- compress: supports transparent content compression.
- cors: CORS support.
- forward: "X-Forwarded-*" parser.
- header: dealing with headers more conviniently.
- jwt: json web token support.
- logger: a logger middleware.
Re-exports
pub use roa_body as body; |
pub use roa_router as router; |
pub use roa_tcp as tcp; |
pub use roa_tls as tls; |
pub use roa_websocket as websocket; |
pub use roa_diesel as diesel; |
pub use roa_tokio as tokio; |
Modules
compress | The compress module of roa.
This module provides a middleware |
cookie | The cookie module of roa.
This module provides a middleware |
cors | The cors module of roa.
This module provides a middleware |
forward | The forward module of roa.
This module provides a context extension |
header | The header module of roa.
This module provides a Request/Response extension |
http | A general purpose library of common HTTP types |
jwt | The jwt module of roa.
This module provides middlewares |
logger | The logger module of roa.
This module provides a middleware |
preload | Reexport all extensional traits. |
query | The query module of roa.
This module provides a middleware |
Macros
throw | Throw an |
Structs
AddrStream | A transport returned yieled by |
App | The Application of roa. |
Boxed | Boxed endpoint. |
Chain | A middleware composing and executing other middlewares in a stack-like manner. |
Context | A structure to share request, response and other data between middlewares. |
Error | The |
Executor | A type implementing hyper::rt::Executor |
JoinHandle | A handle that awaits the result of a task. |
Request | Http request type of roa. |
Response | Http response type of roa. |
Server | A listening HTTP server that accepts connections in both HTTP1 and HTTP2 by default. |
Shared | Shared middleware. |
Variable | A variable. |
Enums
Body | The body of response. |
ErrorKind | Kind of Error. |
Traits
Accept | Asynchronously accept incoming connections. |
Endpoint | Endpoint |
EndpointExt | Extra methods of endpoint. |
Middleware | Middleware |
MiddlewareExt | A set of method to chain middleware/endpoint to middleware or make middleware shared. |
Spawn | Executor constraint. |
State | The |
Type Definitions
Next | Type of the second parameter in a middleware. |
Result | Type alias for |
ResultFuture | Type alias for |
Attribute Macros
async_trait |