10 个版本

0.3.1 2023 年 6 月 3 日
0.3.0 2021 年 4 月 13 日
0.2.4 2021 年 4 月 5 日
0.2.3 2021 年 2 月 5 日
0.1.0 2020 年 8 月 17 日

#370HTTP 服务器

37 每月下载次数

MIT 许可证

80KB
2.5K SLoC

Eve-rs

Eve-rs 是一个基于中间件的 Rust HTTP 框架,灵感来源于 ExpressJS 框架。

示例

让我们使用 eve-rs 创建一个示例应用程序,以直观的方式更好地理解框架。

创建 cargo 项目,这里我们将其命名为 demo,然后在 Cargo.toml 文件中,在依赖关系部分添加以下依赖项。

[dependencies]
eve-rs = '0.2.0'
log = '0.4.8'
tokio = {version = '0.2.22', features = ['full']}

更新 toml 文件后,我们然后在 main.rs 文件中输入以下代码

导入 eve-rs 包

第一步是将 eve_rs 带入项目的范围。因此,我们添加以下行

use eve_rs::{App as DemoApp, Context, DefaultContext, NextHandler, Error, listen, Request, DefaultMiddleware};

中间件

就像在 express 框架中一样,中间件基本上是一个可以访问请求和响应对象的函数。在 eve-rs 中,我们使用相同的术语,并使用中间件进行路由。

对于我们的示例应用程序,我们将创建一个名为 plaintext 的单个中间件,该中间件将在 GET 请求上打印纯文本。

中间件接受两个参数,ContextNextHandler

Context 提供了与请求相关的更多信息的 API。例如,获取请求正文、请求状态、内容类型等。由于它是一个 trait(类似于其他编程语言中的接口),我们可以在应用程序中定义自己的上下文,另一方面,用户可以使用 eve 提供的 DefaultContext

NextHandler 是序列中的下一个中间件。也就是说,如果当前中间件在完成处理且没有抛出错误后传递,则下一个中间件将被调用。

在我们的示例代码中,我们没有要执行的下一个中间件,因此我们将 _next 作为参数。

async fn plaintext(
	mut context: DefaultContext,
	_next: NextHandler<DefaultContext>
) -> Result<DefaultContext, Error<DefaultContext>> {

	let val = "Hello, World!";
	context.body(val);
	
	/**
	Use this if your code uses a next handler.
	let response = next(context).await;
	return response;
	*/
	Ok(context)
}

创建应用程序

我们的下一步是创建一个eve应用。在eve-rs中的App结构体提供了一个create()函数,用于创建App。该函数接收两个参数:context_generatorstate

context_generator是一个用于为我们的中间件创建上下文的函数。它接收两个参数:Requeststate。在这里,状态可以是我们的应用需要拥有的任何配置。假设我们需要应用拥有一些状态,那么我们将以以下方式传递状态

pub struct State {
	pub database_name : String;
}

fn context_generator(request : Request, state : &State) -> YourAppContext {
	let state = state.clone();
	YourAppContext::new(request, state)
}

由于在我们的例子中使用了DefaultContext,我们可以不传递状态就创建一个上下文。

fn default_context_generator(request: Request, _ : &()) -> DefaultContext {     
	// default context has no state as an argument.
	DefaultContext::new(request)
}

一旦我们有了上下文生成器,我们就可以继续创建应用。

pub fn create_app() -> DemoApp<DefaultContext, DefaultMiddleware<()>, ()>  {
	DemoApp::<DefaultContext, DefaultMiddleware<()>, ()>::create(default_context_generator, ())
}

在上面的代码中,第二个参数是存储在DemoApp中的状态,由于我们没有状态,我们向create函数传递了()

应用创建后,我们可以通过使用app.use_middleware()函数在应用的范围内添加中间件。

app.use_middleware("/plaintext", &[DefaultMiddleware::new(|context, next| {
	Box::pin(async move {
		plaintext(context, next).await 
	})
})])

以下是结合上述所有方法的代码示例。


// Bring eve-rs modules to the program scope.
use eve_rs::{App as DemoApp, Context, DefaultContext, NextHandler, Error, listen, Request, DefaultMiddleware};

// middleware
async fn plaintext(
	mut context: DefaultContext,
	 _: NextHandler<DefaultContext>
) -> Result<DefaultContext, Error<DefaultContext>> {

	let val = "Hello, World!";
	context.body(val);
	
	Ok(context)
}

// function to create an eve-rs app.
pub fn create_app() -> DemoApp<DefaultContext, DefaultMiddleware<()>, ()>  {
	DemoApp::<DefaultContext, DefaultMiddleware<()>, ()>::create(default_context_generator, ())
}


// context generator.
fn default_context_generator(request: Request, _: &()) -> DefaultContext {     
	// default context has no state as an argument.
	DefaultContext::new(request)
}


#[async_std::main]
async fn main() {
	println!("Starting server...");

	// call function to create default app.
	let mut app = create_app();
	
	// add middleware to the stack.
	// Can also be one of:
	// get
	// post
	// put
	// delete
	// head
	// options
	// connect
	// patch
	// trace
	app.use_middleware("/plaintext", &[DefaultMiddleware::new(|context, next| {
		Box::pin(async move {
			plaintext(context, next).await 
		})
	})]);
	

	// assign port number.
	let port = 8080;

	log::info!("Listening for connections on 127.0.0.1:{}", port);
	listen(app, ([127, 0, 0, 1], port), None).await;
}

使用cargo build命令构建项目。
使用cargo run命令运行项目。

在上面的例子中,我们使用了DefaultContext作为Context。这意味着eve-rs允许我们以自己的方式实现Context。为了简化,我们使用了DefaultContext

同样,对于DefaultMiddleware也是如此。在这里,我们使用了默认实现。您可以自由实现自己的。

致谢

本项目深受Thruster的启发。这个框架与Thruster非常相似,只是在一些小的设计决策上有所不同。虽然这个框架目前在我们公司的生产环境中使用,但如果您在寻找一个更成熟的项目,您绝对应该选择Thruster。

非常感谢@trezm在开发过程中对我的帮助。

依赖关系

~12–29MB
~453K SLoC