蒸气3系列I —带控制器的CRUD
苹果公司在2015年将Swift开源后,我感到非常兴奋,因为这不仅意味着将会有更多有趣的功能,而且我们也能够在Linux机器上运行Swift。 更重要的是,后来的版本使我们可以用Swift编写服务器。 当前,有几个不同的服务器端Swift框架,例如Vapor,Perfect和Kitura。 我之所以选择Vapor 3,是因为它快速支持SwiftNIO。 结果,Vapor 3提供了简洁的异步API,这是练习异步编程的绝佳机会。 出于本文的目的,我将演示如何使用Vapor 3构建简单的RESTful端点。
制备
如果尚未安装Vapor,请按照以下说明正确安装Vapor。 安装成功后,我们可以使用Vapor工具箱的new命令生成新的项目文件夹。
蒸气新的CRUDControllers
由于我们不需要工具箱创建的模型和控制器模板,因此请使用以下命令删除“ Models
和Controllers
文件夹中的所有内容。
cd CRUDControllers
rm -rf来源/应用/模型/
rm -rf来源/应用/控制器/
此外,在尝试构建项目之前,我们应该删除不必要的代码。 首先,打开S ources/App/configure.swift
文件,然后删除以下行。
migrations.add(模型:Todo.self,数据库:.sqlite)
其次,转到Sources/App/router.swift
文件,然后删除以下几行。
//配置控制器的示例
让todoController = TodoController()
router.get(“ todos”,使用:todoController.index)
router.post(“ todos”,使用:todoController.create)
router.delete(“ todos”,Todo.parameter,使用:todoController.delete)
最后,我们可以使用vapor xcode -y
生成Xcode项目文件,此命令将自动打开CRUDControllers.xcodeproj
。 我们可以选择Run
方案”,该项目应成功构建。
在创建模型类型之前,在configure.swift
内部还需要提到一件事。 在本文中,我们将使用内存中的SQLite数据库,因此我们可以保留默认的提供程序FluentSQLiteProvider
和工具箱生成的数据库配置。
模型
最佳实践是在Xcode之外创建文件。 这可以让Vapor工具箱使用的Swift Package Manager确保文件链接到正确的目标。 让我们创建User
模型文件,并使用以下命令重新生成Xcode项目文件。
mkdir来源/应用/模型
触摸Sources / App / Models / User.swift
蒸气xcode -y
我们的User
模型目前将具有三个属性,分别是id
, name
和username
。 此外,如前所述,我们的User
模型将存储在SQLite数据库中。 因此,使用Xcode打开User.swift
,并将以下行写入文件中。
进口蒸气
导入FluentSQLite
最终课程用户:可编码{
var id:Int?
变量名称:字符串
var用户名:字符串
init(名称:字符串,用户名:字符串){
self.name =名称
self.username =用户名
}
}
扩展用户:SQLiteModel {}
扩展用户:迁移{}
我们的User
模型符合Migration
协议的原因是该协议用于在数据库中为模型创建表。 此外,应在应用程序启动时创建表。 让我们切换到configure.swift
,并在services.register(migrations)
之前添加以下行。
migrations.add(模型:User.self,数据库:.sqlite)
一般来说,迁移只能运行一次。 如果它们已在数据库中运行,则将不再执行它们。 但是,由于我们现在正在使用内存数据库,因此迁移将在每次应用程序启动时执行。
考虑到我们的CRUD端点应该能够接收JSON数据作为HTTP正文并以JSON格式返回响应,因此Vapor提供了Content
协议,该协议允许我们将模型转换为JSON格式。 由于我们的User
模型已经符合Codable
协议,因此我们要做的就是在User.swift
的底部添加以下行。
扩展程序用户:内容{}
最后,为了通过我们的端点更轻松地检索User
模型,请在extension User: Content {}
下面添加以下行extension User: Content {}
扩展名用户:参数{}
至此,我们的User
模型完成。 请尝试构建并运行该应用程序,以确保一切正常。
控制者
Vapor为我们提供了控制器来处理来自客户端的交互,例如请求,处理请求并返回响应。 对于我们这里的情况,一个UsersController
将处理User
模型上的CRUD操作。 再次,请切换回Terminal并使用以下命令创建我们的控制器文件。
mkdir来源/应用/控制器
触摸Sources / App / Controllers / UsersController.swift
蒸气xcode -y
让我们一一实现CRUD功能。 首先,我们的UsersController
应该能够创建我们的User
模型。 请把以下几行写到我们的UsersController.swift
文件中。
进口蒸气
最后一个类UsersController {
func createHandler(_ req:Request)抛出-> Future {
返回尝试req.content.decode(User.self).flatMap {(user)在
返回user.save(on:req)
}
}
}
由于我们的User
模型已经符合Content
协议,因此可以使用req.content.decode(User.self)
从HTTP正文的JSON数据生成User
实例。 另外,由于模型也符合SQLiteModel
协议,因此可以使用user.save(on: req)
将实例保存到SQLite数据库中。 我们将这两个操作与flatMap
挂钩,因为它们都是异步的。 这是我们第一次遇到Future
类型。 正如我在本文开头所提到的,Vapor 3提供了异步API,因为它支持SwiftNIO。 如果您想进一步了解Future
类型,请阅读此文档以获取更多详细信息。
其次,我们的UsersController
应该能够检索我们的User
模型。 请在我们刚刚编写的createHandler
方法下面添加以下行。
最后一个类UsersController {
// ...
func getAllHandler(_ req:Request)抛出-> Future {
返回User.query(on:req).decode(User.self).all()
}
func getOneHandler(_ req:Request)抛出-> Future {
返回尝试req.parameters.next(User.self)
}
}
一方面,我们通过查询数据库来检索User
模型的所有实例。 另一方面,由于我们的User
模型符合Parameter
协议,因此req.parameters.next(User.self)
将从数据库中获取具有给定标识符的实例。
下一步是为我们的UsersController
实现更新功能。 让我们在检索方法下面添加以下方法。
最后一个类UsersController {
// ...
func updateHandler(_ req:Request)抛出-> Future {
返回try flatMap(to:User.self,req.parameters.next(User.self),req.content.decode(User.self)){(user,UpdatedUser)在
user.name = UpdatedUser.name
user.username = UpdatedUser.username
返回user.save(on:req)
}
}
}
我们在这里使用的flatMap
函数与上一个函数不同。 它实际上等待req.parameters.next(User.self)
和req.content.decode(User.self)
完成,然后执行该块。 在该块中,我们只是使用新值更新实例,然后将其保存到数据库中。
然后,让我们添加CRUD端点的最后一部分,即删除功能。 与往常一样,请在updateHandler
方法下面附加以下方法。
最后一个类UsersController {
// ...
func deleteHandler(_ req:Request)抛出-> Future {
返回尝试req.parameters.next(User.self).flatMap {(user)在
返回user.delete(on:req).transform(to:HTTPStatus.noContent)
}
}
}
我们使用req.parameters.next(User.self)
检索实例,并使用user.delete(on: req)
从数据库user.delete(on: req)
其删除。 由于没有要返回的内容,因此我们只可以使用transform(to: HTTPStatus.noContent)
提供204 No Content响应,该响应会将Future
转换为Future
。
最后但并非最不重要的一点,我们必须在路由器上注册UsersController
。 要使一切正常运行,有两个必要的条件。 首先,我们的UsersController
应该遵循RouteCollection
协议并实现func boot(router: Router) throws
方法,如下所示。
最后一个类UsersController:RouteCollection {
// ...
func boot(router:Router)抛出{
让usersRoute = router.grouped(“ api”,“ users”)
usersRoute.get(使用:getAllHandler)
usersRoute.get(User.parameter,使用:getOneHandler)
usersRoute.post(使用:createHandler)
usersRoute.put(User.parameter,使用:updateHandler)
usersRoute.delete(User.parameter,使用:deleteHandler)
}
}
在此方法内部,我们告诉路由器应为每个端点使用哪个路径,HTTP方法和处理程序功能。 其次,为了在路由器上正确注册我们的UsersController
,请切换到Sources/App/routes.swift
并编写以下几行。
公共功能路由(_路由器:路由器)抛出{
让usersController = UsersController()
尝试router.register(collection:usersController)
}
此时,我们可以运行我们的应用程序并使用Postman验证实现。
结论
这是整个项目。
尽管它是非常简单的服务器,但是Vapor提供了坚实的结构和简洁的界面以使用Swift编写服务器,这真是太棒了。 当前,没有多少产品采用Vapor作为后端框架。 但是,随着社区的发展和Vapor的强大,越来越多的开发人员愿意尝试一下。 作为iOS开发人员,了解我们正在与之通信的服务器中发生的事情总是很高兴的。 与后端开发人员合作时,拥有一些后端知识也很有帮助,即使他们可能不使用Vapor或Swift。
在以后的文章中,我将基于该项目的实现共享更多功能。 如果您也对服务器端Swift和Vapor感兴趣,建议阅读raywenderlich.com服务器端Swift与蒸气书。 它不仅包含许多教程,而且还提供了每种技术的详尽说明。 此外,我完全愿意讨论和反馈,所以请分享您的想法。