迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
Thruster An fast and intuitive rust web frameworkDon't have time to read the docs? Check out ✅ Runs in stable✅ Runs fast✅ Doesn't use unsafe Features
MotivationThruster is a web framework that aims for developers to be productive and consistent across projects and teams. Its goals are to be:
Thruster also
FastThruster can be run with different server backends and represents a nicely packaged layer over them. This means that it can keep up with the latest and greatest changes from the likes of Hyper, Actix, or even ThrusterServer, a home-grown http engine. IntuitiveBased on frameworks like Koa, and Express, thruster aims to be a pleasure to develop with. ExampleTo run the example Middleware BasedThe core parts that make the new async await code work is designating middleware functions with the A simple example for using async await is: use std::boxed::Box;use std::future::Future;use std::pin::Pin;use std::time::Instant;use thruster::{App, BasicContext as Ctx, Request};use thruster::{m, middleware_fn, MiddlewareNext, MiddlewareResult, Server, ThrusterServer};#[middleware_fn]async fn profile(context: Ctx, next: MiddlewareNext<Ctx>) -> MiddlewareResult<Ctx> { let start_time = Instant::now(); context = next(context).await; let elapsed_time = start_time.elapsed(); println!( "[{}μs] {} -- {}", elapsed_time.as_micros(), context.request.method(), context.request.path() ); Ok(context)}#[middleware_fn]async fn plaintext(mut context: Ctx, _next: MiddlewareNext<Ctx>) -> MiddlewareResult<Ctx> { let val = "Hello, World!"; context.body(val); Ok(context)}#[middleware_fn]async fn four_oh_four(mut context: Ctx, _next: MiddlewareNext<Ctx>) -> MiddlewareResult<Ctx> { context.status(404); context.body("Whoops! That route doesn't exist!"); Ok(context)}#[tokio::main]fn main() { println!("Starting server..."); let mut app = App::<Request, Ctx, ()>::new_basic(); app.get("/plaintext", m![profile, plaintext]); app.set404(m![four_oh_four]); let server = Server::new(app); server.build("0.0.0.0", 4321).await;} Error handlingIt's recommended to use the This ends up looking like: use thruster::errors::ThrusterError as Error;use thruster::proc::{async_middleware, middleware_fn};use thruster::{map_try, App, BasicContext as Ctx, Request};use thruster::{MiddlewareNext, MiddlewareResult, MiddlewareReturnValue, Server, ThrusterServer};#[middleware_fn]async fn plaintext(mut context: Ctx, _next: MiddlewareNext<Ctx>) -> MiddlewareResult<Ctx> { let val = "Hello, World!"; context.body(val); Ok(context)}#[middleware_fn]async fn error(mut context: Ctx, _next: MiddlewareNext<Ctx>) -> MiddlewareResult<Ctx> { let res = "Hello, world".parse::<u32>(); let non_existent_param = map_try!(res, Err(_) => { Error { context, message: "Parsing failure!".to_string(), status: 400 } } ); context.body(&format!("{}", non_existent_param)); Ok(context)}#[middleware_fn]async fn json_error_handler(context: Ctx, next: MiddlewareNext<Ctx>) -> MiddlewareResult<Ctx> { let res = next(context).await; let ctx = match res { Ok(val) => val, Err(e) => { let mut context = e.context; context.body(&format!( "{{\"message\": \"{}\",\"success\":false}}", e.message )); context.status(e.status); context } }; Ok(ctx)}#[tokio::main]fn main() { println!("Starting server..."); let mut app = App::<Request, Ctx, ()>::new_basic(); app.use_middleware("/", m![json_error_handler]); app.get("/plaintext", m![plaintext]); app.get("/error", m![error]); let server = Server::new(app); server.build("0.0.0.0", 4321).await;} Quick setup without a DBThe easiest way to get started is to just clone the starter kit > git clone [email protected]:trezm/thruster-starter-kit.git> cd thruster-starter-kit> cargo run The example provides a simple plaintext route, a route with JSON serialization, and the preferred way to organize sub routes using sub apps. Quick setup with postgresThe easiest way to get started with postgres is to install thruster-cli, > cargo install thruster-cli And then to run > thruster-cli init MyAwesomeProject> thruster-cli component Users> thruster-cli migrate Which will generate everything you need to get started! Note that this requires a running postgres connection and assumes the following connection string is valid: postgres://postgres@localhost/<Your Project Name> This is all configurable and none of it is hidden from the developer. It's like seeing the magic trick and learning how it's done! Check out the docs for thruster-cli here. TestingThruster provides an easy test suite to test your endpoints, simply include the let mut app = App::<Request, Ctx, ()>::new_basic();...app.get("/plaintext", m![plaintext]);...let result = testing::get(app, "/plaintext");assert!(result.body == "Hello, World!"); Make your own middleware modulesMiddleware is super easy to make! Simply create a function and export it at a module level. Below, you'll see a piece of middleware that allows profiling of requests: #[middleware_fn]async fn profiling<C: 'static + Context + Send>( mut context: C, next: MiddlewareNext<C>,) -> MiddlewareResult<C> { let start_time = Instant::now(); context = next(context).await?; let elapsed_time = start_time.elapsed(); info!("[{}μs] {}", elapsed_time.as_micros(), context.route()); Ok(context)} You might find that you want to allow for more specific data stored on the context, for example, perhaps you want to be able to hydrate query parameters into a hashmap for later use by other middlewares. In order to do this, you can create an additional trait for the context that middlewares downstream must adhere to. Check out the provided query_params middleware for an example. Other, or Custom BackendsThruster is capable of just providing the routing layer on top of a server of some sort, for example, in the Hyper snippet above. This can be applied broadly to any backend, as long as the server implements use async_trait::async_trait;#[async_trait]pub trait ThrusterServer { type Context: Context + Send; type Response: Send; type Request: RequestWithParams + Send; fn new(App<Self::Request, Self::Context>) -> Self; async fn build(self, host: &str, port: u16);} There needs to be:
Within the
Using cargo generateNote: This hasn't yet been updated for the latest version of thruster If you have cargo generate --git https://github.com/ami44/thruster-basic-template.git --name myproject Why you should use thruster
fn ip_guard(head: &RequestHead) -> bool { // Check for the cloudflare IP header let ip = if let Some(val) = head.headers().get(CF_IP_HEADER) { val.to_str().unwrap_or("").to_owned() } else if let Some(val) = head.peer_addr { val.to_string() } else { return false; }; "1.2.3.4".contains(&ip)}#[actix_web::post("/ping")]async fn ping() -> Result<HttpResponse, UserPersonalError> { Ok(HttpResponse::Ok().body("pong"))}... web::scope("/*") // This is confusing, but we catch all routes that _aren't_ // ip guarded and return an error. .guard(guard::Not(ip_guard)) .route("/*", web::to(HttpResponse::Forbidden)), ) .service(ping);... Here is thruster: #[middleware_fn]async fn ip_guard(mut context: Ctx, next: MiddlewareNext<Ctx>) -> MiddlewareResult<Ctx> { if "1.2.3.4".contains(&context.headers().get("Auth-Token").unwrap_or("")) { context = next(context).await?; Ok(context) } else { Err(Error::unauthorized_error(context)) }}#[middleware_fn]async fn ping(mut context: Ctx, _next: MiddlewareNext<Ctx>) -> MiddlewareResult<Ctx> { context.body("pong"); Ok(context)}... app.get("/ping", m![ip_guard, plaintext]);... A bit more direct is nice! Why you shouldn't use thruster
If you got this far, thanks for reading! Always feel free to reach out. ![]() |
请发表评论