蒸气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 

由于我们不需要工具箱创建的模型和控制器模板,因此请使用以下命令删除“ ModelsControllers文件夹中的所有内容。

  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模型目前将具有三个属性,分别是idnameusername 。 此外,如前所述,我们的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与蒸气书。 它不仅包含许多教程,而且还提供了每种技术的详尽说明。 此外,我完全愿意讨论和反馈,所以请分享您的想法。