使用Vapor和Fluent创建REST API
斯威夫特很棒。 是的,它已经成熟(现在有了5.0,我们有了ABI稳定性,万岁!)。 您拥有OOP,POP,功能和命令式编程的强大功能。
如今,您几乎可以在Swift中做任何事情。 如果您想成为既了解后端又了解前端的全栈开发人员,那么本文适合您。
用Swift编写的最著名的Web框架是Kitura和Vapor。
Vapor现在是第3版(于2018年5月发布),是开源的,您可以轻松创建REST API,Web应用程序或很棒的网站。
在本教程中,您将学习:
- 如何开始使用蒸气
- 创建您的第一个REST API
- 如何使用Fluent ORM Framework
- 如何在Fluent中将1:M和M:M db关系转换为父子或兄弟姐妹关系
- 将您所学的内容应用到真实的案例中
如果要跳过这一部分,可以在GitHub上找到整个项目:
radude89 / footballgather-ws
通过在GitHub上创建一个帐户为radude89 / footballgather-ws开发做出贡献。 github.com
先决条件
对于本教程,您将需要:
- Xcode 10.2
- 迅捷知识
- REST API的基础知识
- Swift Package Manager的一些知识
入门
首先,您需要从Mac App Store安装Xcode。
您可以使用brew安装Vapor Toolbox。 这很有用,因此我们可以为常见操作运行命令行任务。
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
冲泡水龙头/自来水
冲泡安装蒸气/抽头/蒸气
您准备好出发了!
足球聚会-iOS应用示例
FootballGather是一个演示项目,供朋友们聚在一起并尽可能快地参加足球比赛。
您可以通过查看以下样机(用Balsamiq创建)来想象客户端应用程序:
特征:
- 坚持球员
- 能够添加玩家
- 设置比赛倒数计时器
- 在离线模式下使用应用程序
- 坚持球员
数据库结构
让我们使用下图所示的数据库模式:
通过这种方式,我们可以举例说明用户和收集之间的1:M关系,其中一个用户可以创建多个收集,而M:M播放器到集合,其中一个收集可以具有多个玩家,一个玩家可以在多个收集中进行游戏。
控制器清单
如果我们查看iOS应用程序,我们将创建以下控制器:
用户控制器
- POST / api / users / login-用户的登录功能
- POST / api / users-注册新用户
- GET / api / users —获取用户列表
- GET / api / users / {userId}-按ID获取用户
- DELETE / api / users —获取用户列表
PlayerController
- GET / api / players —获取玩家列表
- GET / api / players / {playerId}-按ID获取玩家
- GET / api / players / {playerId} / gathers-获取玩家的筹码列表
- POST / api / players —添加新的播放器
- DELETE / api / players / {playerId}-删除具有给定ID的玩家
- PUT / api / players / {playerId}-通过其ID更新玩家
GatherController
- GET / api / gathers-获取收集列表
- GET / api / gathers / {gatherId}-按其ID获取收集
- GET / api / gathers / {gatherId} / players —获取ID指定的集合中的球员列表
- POST / api / gathers / {gatherId} / players / {playerId}-将玩家添加到聚会
- POST / api / gathers-添加新集合
- DELETE / api / gathers / {gatherId}-删除具有给定id的聚集
- PUT / api / gathers / {gatherId}-通过其ID更新收集
应用程式结构
打开您在上一节中创建的Xcode项目。
类型:
蒸气xcode -y
可能还要等一下。
这是生成的文件:
├──公众
├──资料来源
│├──应用
││├──控制器
││├──型号
││├──boot.swift
││├──configure.swift
││└──route.swift
│└──跑
│└──main.swift
├──测试
│└──AppTests
└──Package.swift
您将在此项目中涉及的内容:
Package.swift
这是项目的清单,它定义了我们应用程序的所有依赖关系和目标。
我正在使用蒸气3.3.0。 您可以如下更改Package.swift:.package(URL:“ https://github.com/vapor/vapor.git”,来自:“ 3.3.0”)
上市
您想要公开的所有资源,例如图像。
资源
在这里,您可以看到两个单独的模块:App和Run。
通常,您必须将所有开发的代码放入“ App”中。 运行文件夹包含main.swift文件。
楷模
在这里添加您的Fluent模型。 在我们的应用中:用户,播放器,收集。
控制器
控制器是您编写REST API逻辑(例如CRUD操作)的地方。
与iOS ViewControllers类似,但是它们处理请求并管理模型。
route.swift
用于查找传入请求的适当响应。
configure.swift
在应用初始化之前调用。 注册路由器,中间件,数据库和模型迁移。
实现UserController
在开始实施我们的用户控制器之前,请删除生成的Todo相关代码:
控制器文件夹中的TodoController.swift。
来自Models的Todo.swift。
来自configure.swift的line migrations.add(model:Todo.self,数据库:.sqlite))
在routes中找到的所有内容都来自route.swift。
用户将由用户名和密码定义。 主键的类型为UUID,代表唯一的String。
创建一个新文件User.swift并将其添加到Models文件夹中。 在文件中添加一个名为User的类。
使它符合以下协议:
- 可编码:将服务的参数映射到实际的类参数。
- SQLiteUUIDModel:便利帮助程序协议,使模型成为
- 以UUID作为主键的SQLite Model类。 用于引用属性的编译安全性。
- 内容:用于通过蒸气轻松解码信息
- 迁移:告诉Fluent如何配置数据库。
- 参数:用于带有参数的请求,例如GET / users / {userId}。
现在您有了用户模型。 让我们创建UserController。
在Controllers文件夹中创建一个名为UserController的新结构。 使它符合RouteCollection协议。
暂时保留启动功能。
接下来,我们将为用户添加CRUD操作。
获取所有用户
func getHandler(_ req:Request)抛出-> Future {
返回尝试req.parameters.next(User.self)
}
这将从请求中提取用户ID,并查询数据库以返回用户。
创建用户
func createHandler(_ req:Request,user:User)抛出-> Future {
返回user.save(on:req).map {user in
var httpResponse = HTTPResponse()
httpResponse.status = .created
如果让userId = user.id?.description {
让location = req.http.url.path +“ /” + userId
httpResponse.headers.replaceOrAdd(名称:“位置”,值:位置)
}
let response = Response(http:httpResponse,使用:req)
返回响应
}
}
我们将使用保存功能返回用户对象。 遵循REST API标准,我们提取用户创建的UUID并作为响应的Location标头的一部分返回。
删除用户
func deleteHandler(_ req:Request)抛出-> Future {
返回尝试req.parameters.next(User.self).flatMap(to:HTTPStatus.self){
返回user.delete(on:req).transform(to:.noContent)
}
}
首先,我们从请求参数中提取用户ID。 我们对用户执行删除功能,并返回不包含任何内容的HTTPStatus。
实现PlayerController
PlayerController遵循与UserController相同的模式。 在这种情况下,附加功能由更新部分组成。
func updateHandler(_ req:Request)抛出-> Future {
return try flatMap(to:HTTPStatus.self,req.parameters.next(Player.self),req.content.decode(Player.self)){播放器,updatedPlayer在
player.age = UpdatedPlayer.age
player.name = UpdatedPlayer.name
player.preferredPosition = UpdatedPlayer.preferredPosition
player.favouriteTeam = UpdatedPlayer.favouriteTeam
player.skill = UpdatedPlayer.skill
返回player.save(on:req).transform(to:.noContent)
}
}
如果我们看一下此功能,我们首先要提取要执行更新的播放器的播放器ID。
接下来,我们提取所有属性并将它们映射到Player对象。 我们执行更新,您可能猜到了,我们将其称为save方法。
注册功能
func boot(router:Router)抛出{
让playerRoute = router.grouped(“ api”,“ players”)
playerRoute.get(使用:getAllHandler)
playerRoute.get(Player.parameter,使用:getHandler)
playerRoute.post(Player.self,使用:createHandler)
playerRoute.delete(Player.parameter,使用:deleteHandler)
playerRoute.put(Player.parameter,使用:updateHandler)
playerRoute.get(Player.parameter,“ gathers”,使用:getGathersHandler)
}
对于GatherController,我们坚持使用与UserController相同的模式。
1:M和M:M关系
为了实现两个模型类之间的关系,我们将必须创建一个Pivot类。
最终课程PlayerGatherPivot:SQLiteUUIDPivot {
var ID:UUID?
var playerId:Player.ID
var collectId:Gather.ID
var小组:字串
typealias Left =玩家
typealias右=聚集
静态变量leftIDKey:LeftIDKey = \ PlayerGatherPivot.playerId
静态变量rightIDKey:RightIDKey = \ PlayerGatherPivot.gatherId
init(playerId:Player.ID,collectId:Gather.ID,team:String){
self.playerId = playerId
self.gatherId = collectId
self.team =团队
}
}
// Player.swift
扩展播放器{
var收集:兄弟姐妹 {
返回兄弟姐妹()
}
}
// Gather.swift
扩展集合{
var player:兄弟姐妹 {
返回兄弟姐妹()
}
}
上面的实现描述了玩家和聚会之间的M:M关系。 我们使用左键作为玩家表的主键,并使用右键作为聚会的主键。
这类似于由FK / PK组成的M:M关系的主键。 ‘team’属性描述了玩家在当前聚会中所属的团队。
我们将必须在模型类中指定同级。 这是使用Swift的泛型原理完成的。
对于1:M关系,我们可以看一下User v Gather:
最后一堂课:可编码{
var userId:User.ID
...
}
扩展集合{
var user:Parent {
返回parent(\。userId)
}
}
在我们的控制器类中,方法如下所示:
扩展GatherController {
func getPlayersHandler(_ req:Request)抛出-> Future {
返回尝试req.parameters.next(Gather.self).flatMap(to:[Player] .self){
返回尝试collect.players.query(on:req).all()
}
}
}
扩展PlayerController {
func getGathersHandler(_ req:Request)抛出-> Future {
返回try req.parameters.next(Player.self).flatMap(to:[Gather] .self){
返回try player.gathers.query(on:req).all()
}
}
}
注册路由并配置数据库
打开routes.swift并添加以下内部路由功能:
让userController = UserController()
尝试router.register(collection:userController)
让playerController = PlayerController()
尝试router.register(collection:playerController)
让collectController = GatherController()
尝试router.register(collection:collectController)
这些行将注册您的所有控制器。
在configure.swift中,将所有模型添加到MigrationsConfig中:
migrations.add(模型:User.self,数据库:.sqlite)
migrations.add(模型:Player.self,数据库:.sqlite)
migrations.add(模型:Gather.self,数据库:.sqlite)
migrations.add(模型:PlayerGatherPivot.self,数据库:.sqlite)
而已。 构建并运行。