Zino
致力于打造基于Rust语言的新一代组装式应用开发框架,提供一站式跨平台多端解决方案,可用于后端API开发、桌面应用开发等。我们奉行『约定优于配置』的原则,提供开箱即用的功能模块,极大提升开发效率;并通过应用接口抽象与actix-web
、axum
、dioxus
、ntex
等框架集成,打通社区生态。
本文将以zino
仓库下的examples/axum-app
为例,详细讲解怎么使用zino框架快速开发一个后端API服务。首先是使用cargo new axum-app --bin
新建一个项目,然后在Cargo.toml
中添加以下依赖:
这里我们都使用线上的版本,对于ORM选用MySQL(如果是PostgreSQL就换成orm-postgres
)。进而我们在src
目录创建controller
、model
、router
三个模块(此时mod.rs
中都还是空文件),在main.rs
中添加以下代码:
注意logs
目录是应用运行时自动生成的。运行cargo run
(需要nightly工具链),此时已经能正常启动了,在终端里你应该能看到类似于以下的输出:
如果不指定配置文件的话,默认运行端口就是6080。当然,目前我们还没手动注册任何路由,你唯一能看到的就是框架默认注册的OpenAPI文档页面:
下一步我们来添加配置文件。在项目目录下添加config
目录(注意不是src
目录下,因为配置文件不属于源代码),并为开发环境dev
和生产环境prod
创建两个配置文件:
这里我们新添加了一个debug
服务器标签,绑定端口6070(如果存在debug标签,调试用的API文档路由就会注册到这个对应的端口)。在数据库配置部分,我们指定了数据表创建时的namespace
前缀。对于MySQL连接的配置,密码可以先写成明文,实际运行之后终端里会提示你改成框架加密之后的。需要注意的是,zino框架的ORM是惰性连接的,也就是说只有在需要查询的时候才会去连接。对于配置文件的加载,可以通过cargo run -- --env=prod
来指定,也可以设置环境变量ZINO_APP_ENV
。如果不指定,运行cargo run
时默认为dev
,运行cargo run --release
时默认为prod
。 下面我们在model
目录下定义两个模型:User
和Tag
。这个写法都是模式化的,我们只以Tag
模型为例:
use serde::{Deserialize, Serialize};
use zino::prelude::*;
use zino_derive::{ModelAccessor, ModelHooks, Schema};
#[derive(Debug, Clone, Default, Serialize, Deserialize, Schema, ModelAccessor, ModelHooks)]
#[serde(default)]
pub struct Tag {
// Basic fields.
#[schema(primary_key, auto_increment, readonly)]
id: i64,
#[schema(not_null, index_type = "text")]
name: String,
#[schema(default_value = "Active", index_type = "hash")]
status: String,
#[schema(index_type = "text")]
description: String,
// Info fields.
#[schema(not_null)]
category: String,
#[schema(reference = "Tag")]
parent_id: Option<i64>,
// Revisions.
#[schema(readonly, default_value = "now", index_type = "btree")]
created_at: DateTime,
#[schema(default_value = "now", index_type = "btree")]
updated_at: DateTime,
version: u64,
}
impl Model for Tag {
fn read_map(&mut self, data: &Map) -> Validation {
let mut validation = Validation::new();
if let Some(result) = data.parse_i64("id") {
match result {
Ok(id) => self.id = id,
Err(err) => validation.record_fail("id", err),
}
}
if let Some(name) = data.parse_string("name") {
self.name = name.into_owned();
}
if let Some(category) = data.parse_string("category") {
self.category = category.into_owned();
}
if let Some(result) = data.parse_i64("parent_id") {
match result {
Ok(parent_id) => self.parent_id = Some(parent_id),
Err(err) => validation.record_fail("parent_id", err),
}
}
validation
}
}
虽然代码略有点长,但思路其实挺简单:就是定义一个结构体,通过自动推导或手动实现这7个trait:Default
, Serialize
, Deserialize
, Schema
, ModelAccessor
, ModelHooks
, Model
。实际上,大部分情况下我们往往都只需要手动实现Model
,这个决定了怎么从前端传过来的数据来构造模型。当然,Model这个trait也是可以自动推导的,只是目前还没那么完善。模型里的字段是和数据表对应的,如果数据表不存在则会自动创建,如果新增字段也会自动添加(当然,数据表里的字段可以比模型里的多)。一旦定义好模型,原则上我们可以不用写任何controller
就能直接得到增删改查之类的接口,这是通过DefaultController
来实现的:
使用起来也很简单,直接在router/mod.rs
里引用就行:
当然,具体路由的path
和method
还是要自己指定的,这里用到的就是axum框架标准的路由写法,你也可以选择自行实现一部分controller
。如果你还需要一些中间件,也是按照axum框架的标准写法,在这里加载。 最后把这个路由注册到main()函数里就行了:
fn main() {
zino::Cluster::boot()
.register(router::routes())
.run(Vec::new())
}
这样你就已经生成了14个API接口!至于API文档,我们是和实现分离的,需要写到独立的文件里,这个放到以后再讲。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。