简介:Swift —— 不仅仅是移动应用
许多开发者习惯将 Swift 仅仅与 iOS 和 macOS 关联起来。然而,这门语言早已超越了 Apple 生态系统的范畴。随着 Vapor 和 Kitura 等服务端框架的出现,Swift 已成为构建后端的成熟工具。在本文中,我们将在 30 分钟内使用 Vapor 框架编写一个完整的 REST API。您将学习如何配置项目、创建端点、连接数据库以及处理错误。准备好了吗?开始吧!
步骤 1:安装与项目配置
首先,请确保您已安装 Swift 5.5 或更高版本。可以通过以下命令检查版本:
swift --version
接下来,安装 Vapor Toolbox —— 用于管理项目的实用工具:
brew install vapor
现在,创建一个新项目:
vapor new MyAPI --template=api
此命令将创建一个包含现成 API 模板的文件夹结构。进入项目文件夹并启动构建:
cd MyAPI
vapor build
如果构建成功,启动服务器:
vapor run
默认情况下,服务器将在地址 http://127.0.0.1:8080 上可用。打开浏览器并访问此地址 —— 您应该会看到一条欢迎消息。
Vapor 项目结构
- Sources/App —— 应用程序代码的主目录
- Sources/App/Controllers —— 用于处理请求的控制器
- Sources/App/Models —— 数据模型
- Sources/App/routes.swift —— 用于注册路由的文件
- configure.swift —— 应用程序和数据库的配置
步骤 2:创建数据模型与数据库
作为示例,我们将创建一个用于管理任务列表 (Todo) 的简单 API。从数据模型开始。在 Sources/App/Models 文件夹中创建文件 Todo.swift:
import Fluent
import Vapor
final class Todo: Model, Content {
static let schema = "todos"
@ID(key: .id)
var id: UUID?
@Field(key: "title")
var title: String
@Field(key: "is_completed")
var isCompleted: Bool
init() {}
init(id: UUID? = nil, title: String, isCompleted: Bool = false) {
self.id = id
self.title = title
self.isCompleted = isCompleted
}
}
现在,为表创建迁移。在文件 Sources/App/Migrations/CreateTodo.swift 中:
import Fluent
struct CreateTodo: Migration {
func prepare(on database: Database) -> EventLoopFuture<Void> {
database.schema("todos")
.id()
.field("title", .string, .required)
.field("is_completed", .bool, .required)
.create()
}
func revert(on database: Database) -> EventLoopFuture<Void> {
database.schema("todos").delete()
}
}
不要忘记在 configure.swift 中注册迁移:
app.migrations.add(CreateTodo())
为简单起见,我们将使用 SQLite(内置数据库)。在 configure.swift 中添加:
app.databases.use(.sqlite(.file("db.sqlite")), as: .sqlite)
步骤 3:创建 REST API 端点
现在是最有趣的部分 —— 实现 CRUD 操作。在 Sources/App/Controllers 文件夹中创建控制器 TodoController.swift:
import Vapor
struct TodoController: RouteCollection {
func boot(routes: RoutesBuilder) throws {
let todos = routes.grouped("api", "todos")
todos.get(use: index)
todos.post(use: create)
todos.group(":todoID") { todo in
todo.get(use: show)
```