Loco 指南
指南假设
这是一个“绕远路”的教程。它很长且深入,这是故意的,它向你展示了如何手动和使用生成器自动构建事物,以便你学习构建的技能以及事物是如何运作的。
为什么叫这个名字?
Loco
这个名字来源于 locomotive(火车头),以此向 Rails 致敬,并且 loco
比 locomotive
更容易输入 :-) 。此外,在某些语言中,它的意思是“疯狂”,但这并非最初的意图(或者,在 Rust 上构建 Rails 是疯狂的吗?只有时间会证明!)。
我需要了解多少 Rust 知识?
你需要熟悉 Rust,达到初学者水平即可,但不需要超过中级初学者水平。你需要知道如何构建、测试和运行 Rust 项目,使用过一些流行的库,例如 clap
、regex
、tokio
、axum
或其他 Web 框架,不需要太花哨的东西。Loco 中没有疯狂的生命周期扭曲或复杂/过于神奇的宏,你不需要知道它们是如何工作的。
什么是 Loco?
Loco 的灵感强烈来源于 Rails。如果你了解 Rails 和 Rust,你会感到宾至如归。如果你只了解 Rails 并且是 Rust 新手,你会发现 Loco 令人耳目一新。我们不假设你了解 Rails。
Loco 是一个用于 Rust 的 Web 或 API 框架。它也是一个面向开发人员的生产力套件:它包含了构建业余项目或你的下一个初创公司所需的一切。它也受到了 Rails 的强烈启发。
- 你拥有 MVC 模型的变体,这消除了选择悖论。你专注于构建你的应用程序,而不是为使用哪种抽象做出学术决策。
- 胖模型,瘦控制器。模型应该包含你的大部分逻辑和业务实现,控制器应该只是一个轻量级的路由器,它理解 HTTP 并传递参数。
- 命令行驱动,以保持你的势头和流畅性。生成内容而不是复制粘贴或从头开始编码。
- 每个任务都“为基础设施准备就绪”,只需插入你的代码并连接起来:控制器、模型、视图、任务、后台作业、邮件程序等等。
- 约定优于配置:决策已经为你完成——文件夹结构很重要,配置形状和值很重要,以及应用程序的连接方式对于应用程序的运行方式以及你如何最有效率至关重要。
创建新的 Loco 应用
你可以按照本指南进行循序渐进的“自下而上”的学习,或者你可以跳转并使用 快速浏览 来进行更快速的“自上而下”的介绍。
安装
创建新的 Loco 应用
现在你可以创建你的新应用(选择 “SaaS app” 以获得内置身份验证)。
)
)
()
)
以下是 Loco 默认为你创建的内容的概要:
文件/文件夹 | 用途 |
---|---|
src/ | 包含控制器、模型、视图、任务等 |
app.rs | 主要组件注册点。在这里连接重要的部分。 |
lib.rs | 你的组件的各种 Rust 特定的导出。 |
bin/ | 包含你的 main.rs 文件,你无需担心它 |
controllers/ | 包含控制器,所有控制器都通过 mod.rs 导出 |
models/ | 包含模型,models/_entities 包含自动生成的 SeaORM 模型,models/*.rs 包含你的模型扩展逻辑,这些逻辑通过 mod.rs 导出 |
views/ | 包含基于 JSON 的视图。是可以 serde 并通过 API 作为 JSON 输出的结构体。 |
workers/ | 包含你的后台工作程序。 |
mailers/ | 邮件程序逻辑和模板,用于发送电子邮件。 |
fixtures/ | 包含数据和自动 fixture 加载逻辑。 |
tasks/ | 包含你日常面向业务的任务,例如发送电子邮件、生成业务报告、数据库维护等。 |
tests/ | 你的应用范围测试:模型、请求等。 |
config/ | 基于阶段的配置文件夹:development、test、production |
Hello, Loco!
让我们快速获得一些响应。为此,我们需要启动服务器。
你现在可以切换到 myapp
:
启动服务器
现在,让我们看看它是否活着:
}
内置的 _ping
路由将告知你的负载均衡器一切正常。
让我们看看所需的所有服务是否都已启动:
}
_health
路由将告诉你,你已正确配置了你的应用程序:它可以成功建立与你的数据库和 Redis 实例的连接。
对 Loco 说 “Hello”
让我们为我们的服务添加一个快速的 hello 响应。
这是生成的控制器主体:
use *;
use debug_handler;
pub async
更改 index
处理程序主体:
// replace (替换)
empty
// with this (用这个替换)
text
启动服务器:
现在,让我们测试一下:
Loco 具有强大的生成器,这将使你的生产力提高 10 倍,并在构建应用程序时推动你的势头。
如果你想稍作娱乐,让我们“艰难地学习”,并手动添加一个新的控制器。
添加一个名为 home.rs
的文件,并在 mod.rs
中 pub mod home;
它:
src/
controllers/
auth.rs
home.rs <--- add this file (添加这个文件)
users.rs
mod.rs <--- 'pub mod home;' the module here (在这里 'pub mod home;' 模块)
接下来,设置一个 hello 路由,这是 home.rs
的内容:
// src/controllers/home.rs
use *;
// _ctx contains your database connection, as well as other app resource that you'll need
// _ctx 包含你的数据库连接,以及你需要的其他应用程序资源
async
最后,在 app.rs
中注册这个新的控制器路由:
src/
controllers/
models/
..
app.rs <---- look here
在 routes()
中添加以下内容:
// in src/app.rs
// 在 src/app.rs 中
就是这样。杀死服务器并再次启动它:
并访问 /home/hello
:
你可以使用以下命令查看你的所有路由:
$ cargo loco routes
..
..
[POST] /api/auth/login
[POST] /api/auth/register
[POST] /api/auth/reset
[POST] /api/auth/verify
[GET] /home/hello <---- this is our new route! (这是我们的新路由!)
..
..
$
/api
下,因为它已为客户端做好准备,并且我们在脚手架中使用了 --api
选项。当使用像 React Router 这样的客户端路由时,我们希望将后端路由与客户端路由分开:浏览器将使用
/home
而不是 /api/home
,后者是后端路由,你可以从客户端调用 /api/home
而无需担心。然而,路由:/_health
和 /_ping
是例外,它们保留在根目录。
MVC 与你
传统的 MVC(模型-视图-控制器)起源于桌面 UI 编程范式。 然而,它在 Web 服务中的适用性导致了它的快速采用。MVC 的黄金时代大约在 2010 年代初期,从那时起,许多其他范式和架构涌现出来。
MVC 仍然是一个非常强大的原则和架构,可以遵循以简化项目,这也是 Loco 所遵循的。
尽管 Web 服务和 API 没有 视图 的概念,因为它们不生成 HTML 或 UI 响应,我们声称 稳定、安全 的服务和 API 确实具有视图的概念 —— 那就是序列化的数据、它的形状、它的兼容性和它的版本。
// a typical loco app contains all parts of MVC
// 一个典型的 loco 应用包含 MVC 的所有部分
src/
controllers/
users.rs
mod.rs
models/
_entities/
users.rs
mod.rs
users.rs
mod.rs
views/
users.rs
mod.rs
这是一个重要的 认知 原则。该原则声称,只有当你将 API 响应视为一个独立的、独立管理的东西时,你才能创建安全、兼容的 API 响应 —— 因此在 Loco 中有了 MVC 中的 'V'。
生成模型
Loco 中的模型表示数据 和 功能。通常,数据存储在你的数据库中。你的应用程序的大部分(如果不是全部)业务流程都将在模型上(作为 Active Record)或作为几个模型的编排进行编码。
让我们创建一个名为 Article
的新模型:
数据库迁移
保持你的 schema 诚实是通过迁移完成的。迁移是对你的数据库结构的单一更改:它可以包含完整的表添加、修改或索引创建。
// this was generated into `migrations/` from the command:
// (这是通过以下命令生成到 `migrations/` 中的:)
//
// $ cargo loco generate model article title:string content:text
//
// it is automatically applied by Loco's migrator framework.
// (它由 Loco 的迁移器框架自动应用。)
// you can also apply it manually using the command:
// (你也可以使用以下命令手动应用它:)
//
// $ cargo loco db migrate
//
你可以通过将迁移按顺序应用到新的数据库来重建完整的数据库 —— 这由 Loco 的迁移器(它派生自 SeaORM)自动完成。
当生成新模型时,Loco 将:
- 生成一个新的 “up” 数据库迁移
- 应用迁移
- 从数据库结构中反映实体,并生成回你的
_entities
代码
你将在 models/_entities/
中找到你的新模型作为实体,它与你的数据库结构同步:
src/models/
├── _entities
│ ├── articles.rs <-- sync'd from db schema, do not edit (从数据库 schema 同步,不要编辑)
│ ├── mod.rs
│ ├── prelude.rs
│ └── users.rs
├── articles.rs <-- generated for you, your logic goes here. (为你生成,你的逻辑写在这里。)
├── mod.rs
└── users.rs
使用 playground
与数据库交互
你的 examples/
文件夹包含:
playground.rs
- 一个尝试和实验你的模型和应用程序逻辑的地方。
让我们使用你的模型,使用 playground.rs
获取数据:
// located in examples/playground.rs
// (位于 examples/playground.rs 中)
// use this file to experiment with stuff
// (使用此文件来试验内容)
use ;
// to refer to articles::ActiveModel, your imports should look like this:
// (要引用 articles::ActiveModel,你的导入应该像这样:)
use ;
async
返回文章列表
在示例中,我们使用以下代码返回列表:
let res = find.all.await.unwrap;
要查看如何运行更多查询,请转到 SeaORM 文档。
要执行你的 playground,请运行:
$ cargo playground
现在,让我们插入一个项目:
async
并再次运行 playground:
我们现在准备将其插入到 articles
控制器中。首先,生成一个新的控制器:
编辑 src/controllers/articles.rs
:
use *;
use crate articles;
pub async
现在,启动应用程序:
并发出请求:
}
构建 CRUD API
接下来,我们将了解如何获取单篇文章、删除和编辑单篇文章。按 ID 获取文章是使用来自 axum
的 Path
提取器完成的。
将 articles.rs
的内容替换为以下内容:
// this is src/controllers/articles.rs
// 这是 src/controllers/articles.rs
use *;
use ;
use crate;
async
pub async
pub async
pub async
pub async
pub async
一些需要注意的事项:
Params
是一个强类型的必需参数数据持有者,其概念类似于 Rails 的 strongparams,只是更安全。Path(id): Path<i32>
从 URL 中提取:id
组件。- 提取器的顺序很重要,并且遵循
axum
的文档(参数、状态、主体)。 - 创建一个
load_item
辅助函数并在所有单项路由中使用它总是更好。 - 虽然
use loco_rs::prelude::*
引入了构建控制器所需的任何内容,但你应该注意导入crate::models::_entities::articles::{ActiveModel, Entity, Model}
以及Serialize, Deserialize
用于参数。
#[debug_handler]
宏可以通过打印出更好的错误消息来提供帮助。有关提取器的更多信息,请参见 axum 文档。
你现在可以测试它是否工作,启动应用程序:
添加一篇新文章:
}
获取列表:
} }
添加第二个模型
让我们添加另一个模型,这次是:Comment
。我们想要创建一个关系——评论属于一篇文章,并且每篇文章可以有多个评论。
我们不手动编写模型和控制器,而是要创建一个 comment scaffold,它将生成一个完全可用的 CRUD API 评论。我们还将使用特殊的 references
类型:
references:<table>
也可用。用于当你想要为你的列使用不同的名称时。
如果你查看新的迁移,你会在 articles 表中发现一个新的数据库关系:
..
..
.col
.foreign_key
..
..
现在,让我们按以下方式修改我们的 API:
- 可以通过浅路由添加评论:
POST comments/
- 评论只能在嵌套路由中获取(强制文章存在):
GET posts/1/comments
- 评论无法更新、单独获取或删除
在 src/controllers/comments.rs
中,删除不需要的路由和函数:
还要调整 src/controllers/comments.rs
中的 Params 和 update 函数,方法是更新标有 <- add this
的脚手架代码
现在我们需要在 src/controllers/articles.rs
中获取关系。添加以下路由:
并实现关系获取:
// to refer to comments::Entity, your imports should look like this:
// (要引用 comments::Entity,你的导入应该像这样:)
use crate;
pub async
现在再次启动应用程序:
向文章 1
添加评论:
}
并且,获取关系:
}
这结束了我们全面的 Loco 指南。如果你坚持到了这里,恭喜你!
任务:导出数据报告
真实世界的应用程序需要处理真实世界的情况。假设你的一些用户或客户需要某种报告。
你可以:
- 连接到你的生产数据库,发出临时的 SQL 查询。或使用某种数据库工具。这是不安全、不保险、容易出错且无法自动化的。
- 将你的数据导出到 Redshift 或 Google 之类的东西,并在那里发出查询。这是资源浪费、不安全、无法正确测试且速度慢。
- 构建一个管理后台。这是耗时的和浪费的。
- 或者在 Rust 中构建一个临时的任务,它编写快速、类型安全、受编译器保护、快速、环境感知、可测试且安全。
这就是 cargo loco task
的用武之地。
首先,运行 cargo loco task
以查看当前任务:
)
生成一个新任务 user_report
在 src/tasks/user_report.rs
中,你将看到为你生成的任务。将其替换为以下内容:
// find it in `src/tasks/user_report.rs`
// 在 `src/tasks/user_report.rs` 中找到它
use *;
use Vars;
use crate users;
;
你可以根据需要修改此任务。使用 app_context
访问模型或任何其他环境资源,并获取通过 CLI 使用 vars
给定的变量。
运行此任务使用:
$ cargo loco task user_report var1:val1 var2:val2 ...
args: Vars
!!! user_report: listing users !!!
------------------------
done: 0 users
如果你之前没有添加用户,则报告将为空。
要添加用户,请查看 快速 Loco 之旅 的 注册新用户 章节。
记住:这是环境相关的,因此你编写一次任务,然后根据需要在开发或生产环境中执行。任务被编译到主应用程序二进制文件中。
身份验证:验证你的请求
如果你选择了 SaaS App
启动器,你应该在应用程序中内置了一个完全配置的身份验证模块。
让我们看看如何在添加评论时要求身份验证。
返回 src/controllers/comments.rs
并查看 add
函数:
pub async
要要求身份验证,我们需要以这种方式修改函数签名:
async