Vapor 第一篇 名词解释
in 技术 with 33 comments

Vapor 第一篇 名词解释

in 技术 with 33 comments

前言

最近花了相当长的时间在 Vapor 项目上,在开发 Demo 上遇到很多问题。无奈相关资料实在太少,官方文档也是让人一知半解,结构错乱,毫无循序渐进可言。所以干脆从源码着手,希望能对你有所帮助,在 Vapor 的路上找到一些乐趣。

名词解释

Container

容器,作为 Vapor 项目的 Top Level 对象。常见 Protocol Container 的具体实现有下面几种:

后面两个会在以后的 Router 文章里面详细介绍。先看 Application,它由下面相关组件组成:

//app.swift
let app = try Application(config: config, environment: env, services: services)

Services

所有注册 Service 和 Provider 的集合。包括配置,数据库服务,Router 等等

Services 的注入有两种方式。

1. factories

把 Service 封装为 BasicServiceFactory 或 TypeServiceFactory 两种结构体并存入 factories 数组中。

services.register { container -> CommandConfig in
    return .default()
}
services.register { container -> Commands in
   return try container.make(CommandConfig.self).resolve(for: container)
}
services.register(NIOServerConfig.self)

这里有个特性不得不提,factories 数组中,同一 Service 类型只会覆盖,并不会新增。
所以你可以利用如下代码修改接口 http body 的时候的大小限制。

var nioServerConfig = NIOServerConfig.default()
nioServerConfig.maxBodySize = 4 * 1024 * 1024
services.register(nioServerConfig)

Body Size 请请看:Vapor + Nginx 解除请求大小限制
地址和端口 请查看:Vapor 更改地址和端口的多种方式

2. providers

当 Provider 被注入 Services 的时候,Services 反而被引用注入 Provider 对象中。用来注册 Provider 内部需要注册的 Service 和 Provider。

try provider.register(&self) 

在 &self 被引入后:
当注册的对象为 Service 的时候,则使用第一种 factories 的注入方法。
当注册的对象为 Provider 的时候,则会把 Provider 添加入 providers 数组中。
假如 Provider 内又注入了 Provider,则循环往复。

区别

Service 在被需要的时候才触发初始化。比如我需要 BCryptDigest 加密我的密码。而且如果再次 make 的时候会使用缓存对象。

try req.make(BCryptDigest.self).hash(passContainer.newPassword)

Provider 在 Application 初始化的时候,则被初始化并执行相关操作。比如 PostgreSQL 数据库连接。

VaporProvider 情况暂不考虑,因为它是在初始化之后启动之前。而且暂未找到一个具体的实现类或结构体。

make

Service 的实例化对象,往往都是使用调用 make 方法。最大的好处当然是它会把同 Type 的对象用缓存返回,并不会重新初始化。而且会导致耦合性非常低。这是它的流程:

  1. 要实例化的 Type 是否存在 ServiceCache 中;如果存在则返回缓存,否则去实例化;
  2. 在 Services 中找到需要实例化的 Type 对应的 ServiceFactory,如果找到多个,则根据 Config 配置选择需要的那个。无法确定则报错;
  3. 验证 ServiceFactory 是否与 Config 配置的 requirements 一致。不一致则报错;
  4. 根据 ServiceFactory 协议的具体实现 makeService 方法实例化对象(具体参考前面 factories 的注册方式)。

Config

针对被注册的 Service 的配置

Config 结构体中包含两个私有字典属性。默认都是 [:]。

fileprivate var preferences: [ServiceID: ServiceID]
fileprivate var requirements: [ServiceID: ServiceID]

preferences

当多个 Service 被注入的时候,指定了共同的 serviceSupports。在 container.make(Type) 对象的时候无法区分,则需要 prefer 来确认应该实例化的类型对象

比如加入如下代码:

//configure.swift
try services.register(FluentPostgreSQLProvider())
//boot.swift
_ = try app.make(KeyedCache.self)

则会在启动后报错:

`
Fatal error: Error raised at top level: ⚠️ Service Error: Please choose which KeyedCache you prefer, multiple are available: MemoryKeyedCache, DictionaryKeyedCache.
`

requirements

用于校验 Protocol Service 的实现类型。如果不一致则报错。

假如在上面 preferences 示例的基础上,再在 configure.swift 中加入如下代码:

config.prefer(DictionaryKeyedCache.self, for: KeyedCache.self)
//Value , Key
config.require(MemoryKeyedCache.self, for: KeyedCache.self)

则会在启动后报错:
`
⚠️ [ServiceError.typeRequirement: 'DictionaryKeyedCache' for 'KeyedCache' is not required type 'MemoryKeyedCache'.]
`

Environment

当前 Application 运行的环境。其实就是 --env 的参数值,包括 production,development(默认),testing ,custom(自定义)

# 支持缩写  dev = development
# serve 的由来其实也很有意思,会在后续的源码解析文章中详解
swift run Run serve --env dev
// 当 --env prod 的时候,下列表达式返回 true
req.environment.isRelease

可以根据 Request 的 environment 来确定要响应不同的字段。
当然你也可以在 configure.swift 里面,根据 Application 的 environment 来连接不同的数据库。

结语

期望你能对 Vapor 项目中的这些常见对象,有比较清晰的认识。

Responses