Vapor奇幻之旅(05 Fluent)

在上一篇Vapor奇幻之旅(04Routing)中我介绍了Routing的写法,作为一个web应用,数据库是必不可少的,而Fluent则是管理数据的一个抽象层,可以支持数据库的增删改查等操作,默认的FluentProvider支持sqlite数据库,也就是说在没有任何数据库配置的情况下,可以通过Fluent Provider中的内存数据库来快速加载SQLite数据库,这样做的好处是可以轻松的进行接口测试。

目前Vapor支持的数据库如下:

数据库类型

Key

Package

Class

是否来自官方

Memory

memory

Fluent Provider

Fluent.MemoryDriver

Yes

SQlite

sqlite

Fluent Provider

Fluent.SQLiteDriver

Yes

MySQL

mysql

MySQLProvider

MySQLDriver.Driver

Yes

PostgreSQL

postgresql

PostgreSQLProvider

PostgreSQLDriver.Driver

No

MongoDB

N/A

MongoProvider

N/A

No

对于大型数据库官方只有支持到MySQL,稍显遗憾,开发团队最近都在进行Vapor 3的开发,相信不久后就可以有更多的数据库类型支持了,而且由于Fluent的抽象的特性,只要有相应的驱动,适配任何数据库我想只是时间问题。

既然是抽象层,我们先不管用啥数据库,可以先把我们的数据模型搭建起来。

我想给我的网站加一段名人名言,于是我创建一个名为Quotes的模型,代码如下:

import Vapor
import FluentProvider
import HTTP

/// 名人名言
final class Quotes: Model {
    
    // 这个属性能让Fluent存储额外的信息,如这个model的id
    let storage = Storage()
    
    //***下面是表中的属性***
    
    /// 作者
    let author: String
    /// 内容
    let content: String
    /// 描述
    let description: String
    
    /// 数据库中列的名字
    struct Keys {
        static let id = "id"
        static let author = "author"
        static let content = "content"
        static let description = "description"
    }
    
    // MARK: 初始化Fluent
    
    /// 初始化Quotes
    required init(row: Row) throws {
        author = try row.get(Quotes.Keys.author)
        content = try row.get(Quotes.Keys.content)
        description = try row.get(Quotes.Keys.description)
    }
    
    // 序列化Quotes到数据库
    func makeRow() throws -> Row {
        var row = Row()
        try row.set(Quotes.Keys.author, author)
        try row.set(Quotes.Keys.content, content)
        try row.set(Quotes.Keys.description, description)
        return row
    }

}

我们的model有了,下面就该联系一下数据库了,Fluent 提供了一个Preparation协议,源码如下:

/// A preparation prepares the database for
/// any task that it may need to perform during runtime.
public protocol Preparation {

    /// The prepare method should call any methods
    /// it needs on the database to prepare.
    static func prepare(_ database: Database) throws

    /// The revert method should undo any actions
    /// caused by the prepare method.
    ///
    /// If this is impossible, the `PreparationError.revertImpossible`
    /// error should be thrown.
    static func revert(_ database: Database) throws
}

其中prepare方法是让数据库做好准备的方法,比如新建table,而revert方法则是对prepare做的操作进行回滚操作,比如删除table。

另外,JSON也是网络通讯常用的数据格式,模型通常也需要转换为JSON串,或者需要解析json串到模型。JSON库为我们提供了JSONConvertible协议,demo如下

extension Quotes: JSONConvertible {
    convenience init(json: JSON) throws {
        self.init(
            author: try json.get(Quotes.Keys.author),
            content: try json.get(Quotes.Keys.content),
            description: try json.get(Quotes.Keys.description)
        )
    }
    
    func makeJSON() throws -> JSON {
        var json = JSON()
        try json.set(Quotes.Keys.id, id)
        try json.set(Quotes.Keys.author, author)
        try json.set(Quotes.Keys.content, content)
        try json.set(Quotes.Keys.description, description)
        return json
    }
}

在写这个extension之前,还需要在mode里添加一个初始化方法:

/// 名人名言
final class Quotes: Model {
    ...
    // MARK: 初始化Fluent
    init(author: String, content: String, description: String) {
        self.author = author
        self.content = content
        self.description = description
    }
   ...
}

模型已经建好了,那么作为一个数据库模型,怎么能少了增删改查呢,药药药,切克闹,增删改查来一套:

这里我们需要开始写Controller了,在controller文件夹内创建一个QuotesController.swift的文件:

import Vapor
import FluentProvider

struct QuotesController {
    
    func addRoutes(to drop: Droplet) {
        let quots = drop.grouped("api","quots")
    }
}

然后在Config+Setup.swift中准备好新创建的model:

private func setupPreparations() throws {
        preparations.append(Quotes.self)
}

接下来在创建一个Routers+Quotes.swift的文件并添加QuotesController的routs. Routers+Quotes.swift:

import Vapor

extension Droplet {
    
    func setupQuotes() {
        let quotsController = QuotesController()
        quotsController.addRoutes(to: self)
    }
    
}

最后在Droplet+Setup.swift中添加setupQuotes()方法:

@_exported import Vapor

extension Droplet {
    public func setup() throws {
        setupQuotes()        
    }
}

现在就可以在我们的controller里面写增删改查了:

import Vapor
import FluentProvider

struct QuotesController {
    
    func addRoutes(to drop: Droplet) {
        let quots = drop.grouped("api","quots")
        //添加一个新的quots
        quots.post("create", handler: createQuots)
        //查询所有的quotes
        quots.get(handler: allQuotes)
        // 更新quotes
        quots.post("update", handler: updateQuotes)
        // 删除quotes
        quots.post("delete", handler: deleteQuotes)

    }

    /// 添加一个新的quots
    func createQuots(_ req: Request) throws -> ResponseRepresentable {
        guard let json = req.json else {
            throw Abort.badRequest
        }
        let quots = try Quotes(json: json)
        try quots.save()
        return quots
    }
    
    /// 查询所有的quots
    func allQuotes(_ req: Request) throws -> ResponseRepresentable {
        let quots = try Quotes.all()
        return try quots.makeJSON()
    }
    /// 更新quotes
    func updateQuotes(_ req: Request) throws -> ResponseRepresentable {
        guard let json = req.json else {
            throw Abort.badRequest
        }
        
        let id: Int = try json.get("id")
        if let quots = try Quotes.find(id) {
            try quots.update(json: json)
        }
        
        return try Quotes.all().makeJSON()
    }
    
    // 删除quotes
    func deleteQuotes(_ req: Request) throws -> ResponseRepresentable {
        guard let json = req.json else {
            throw Abort.badRequest
        }
        let id: Int = try json.get("id")
        if let quots = try Quotes.find(id) {
            try quots.delete()
        }
        
        return try Quotes.all().makeJSON()
    }
    
}

还需要在Quotes中加入一个update方法,并把参数改成var

/// 名人名言
final class Quotes: Model {
    /// 作者
    var author: String
    /// 内容
    var content: String
    /// 描述
    var description: String
    ...
}

extension Quotes {
    
    func update(json: JSON) throws {
        self.author = try json.get(Quotes.Keys.author)
        self.content = try json.get(Quotes.Keys.content)
        self.description = try json.get(Quotes.Keys.description)
        try self.save()
    }
    
}

现在我们的增删改查就已经完成了,下面cmd+r运行程序,用Rested测试接口:

增加一个名言

查询插入的结果

更新刚刚插入的数据

删除刚刚插入的数据

由于默认的数据库是基于内存加载的,重新运行程序则会清空,如果想要保存数据到服务器,你需要使用持续化的数据库,如MySQL、PostgreSQL以及MongoDB,后面我会对这几个数据库操作一一介绍。

关于Vapor其他知识,可以参考以下文章:

Vapor奇幻之旅(01开始) Vapor奇幻之旅(02部署) Vapor奇幻之旅(03上手) Vapor奇幻之旅(04Routing) Vapor奇幻之旅(05 Fluent) Vapor奇幻之旅(06 PostgreSQL) Vapor奇幻之旅(07 连接服务端PostgreSQL) Vapor奇幻之旅(08 连接服务端MongoDB) Vapor奇幻之旅(09 连接MySQL)

希望你对我的教程能够喜欢,你们的赞是我持续的动力,欢迎加入QQ群参与互动:431296189

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏BinarySec

CVE-2016-0095从PoC到Exploit

31120
来自专栏NetCore

[原创]Fluent NHibernate之旅(四)-- 关系(上)

经过了前面三篇的介绍,相信大家对Fluent NHibernate已经有一定的了解了,在我们学习中,Fluent 也已经进入了RTM版本。这次的版本发布离RC版...

22660
来自专栏恰童鞋骚年

ASP.Net MVC开发基础学习笔记:五、区域、模板页与WebAPI初步

  为了方便大规模网站中的管理大量文件,在ASP.NET MVC 2.0版本中引入了一个新概念—区域(Area)。

19520
来自专栏本立2道生

Win32对话框程序(1)

之前学C语言是一直都是在控制台下面操作的,面对的都是黑框框,严重的打击了学习的兴趣。后来在TC下进行C语言课程设计,做了图形界面编程,但都是点线面画的…… 

19910
来自专栏智能大石头

线程池ThreadPool及Task调度机制分析

近1年,偶尔发生应用系统启动时某些操作超时的问题,特别在使用4核心Surface以后。笔记本和台式机比较少遇到,服务器则基本上没有遇到过。

13000
来自专栏高性能服务器开发

关于windows完成端口(IOCP)的一些理解(二)

1 不知道你是否记得前面中说过每消耗一个预先准备客户端的socket,就要补上一个。这个代码现在看来就应该放在连接成功事件里面了: DWORD ThreadFu...

46990
来自专栏Python学习心得

​Python爬虫 --- 2.5 Scrapy之汽车之家爬虫实践

原文链接:https://www.fkomm.cn/article/2018/8/7/32.html

12200
来自专栏技术小讲堂

iBatis.Net(5):Data Map(了解)

总算,总算,能写点示例啦,呵呵,其实前面的几篇,我感觉自己写的也很生硬,没有Demo理解起来是很困难,很多名词,反正我初次接触iBatis的时候,是一点也不理解...

35160
来自专栏张善友的专栏

CLR 4.0 安全模型

在公共语言运行时(CLR)过往的版本中,安全模型一直是最为复杂的模块之一,由于涉及Evidence,CAS策略等机制,难以被用户使用。在Silverlight中...

19780
来自专栏c#开发者

C#开发终端式短信的原理和方法

简介   没发过短信的年轻人肯定是属于那种受保护的稀有动物,通讯发达的今天短信已经成为人们交流的重要手段,其中也蕴含着巨大的市场和经济利益,掌握短信技术的人才也...

46290

扫码关注云+社区

领取腾讯云代金券