What’s New in Vapor 4
经过几乎一年的等待,Vapor 4开发接近尾声,第一个alpha版本已经发布,beta版本不久发布,预计2020年2月Vapor 4.0.0发布🎉。
接下来一起看看新版本带来的更新内容:
新的依赖注入API - New Dependency Injection API
新的依赖注入接口,相对于Vapor 3引入的Services
,Vapor 4将会借助Swift编译器让实现更加容易,采用的Swift语言特性extension,通过第三方来提供服务注入,接口更加开放。看下实例代码:
如何改变默认的HTTP端口
// vapor 3
services.register(NIOServerConfig.self) { _ in
return NIOServerConfig.default(port: 1337)
}
Vapor 4, 直接修改 Application
的配置属性:
// vapor 4
app.server.configuration.port = 1337
如何设置Leaf视图渲染器
Vapor 3, 首先向Services注册LeafConfig,然后再通过LeafConfig配置默认选项Leaf。
// vapor 3
services.register(LeafConfig.self) { _ in
return LeafConfig(...)
}
config.prefer(LeafRenderer.self, for: ViewRenderer.self)
Vapor 4,直接向Application添加Leaf的设置属性,并通过app.views.use通知View渲染器使用即可。
// vapor 4
app.leaf.configuration = LeafConfiguration(...)
app.views.use(.leaf)
NIO 2
Vapor 4 更新到 SwiftNIO 2.0.
SSWG
新版本融合了SSWG新标准( Swift Server Working Group).
Async HTTP Client
采用了AsyncHTTPClient 替换原生的URLSession
,Async HTTP Client
是建立在Swift NIO基础上的纯Swift实现客户端,性能佳且更轻量化,尤其适用Linux系统。 鉴于诸多优点,Vapor 4 选择它作为 HTTP默认的客户端。
Vapor4的Toolbox更新
更新了 vapor new
更新后的 toolbox ,提供了更加智能的 vapor new
命令,帮助你创建自定义的项目时主动询问需要附加哪些包,比如你想选择Fluent和JWT,toolbox会这样提示你:
$ vapor new hello-world
Would you like to use Fluent? [y/n]:
New Model API
Fluent 4借助Swift5.1的属性包装器特性重新设计了model API。
声明models时,借助 @Field
声明属性,借助 @ID
声明主键:
final class Galaxy: Model {
@ID(key: "id")
var id: UUID?
@Field(key: "name")
var name: String
}
关系的声明,也将更新,比如: @Parent
, @Children
, 和 @Siblings
:
final class Planet: Model {
@ID(key: "id")
var id: UUID?
@Field(key: "name")
var name: String
@Parent(key: "galaxy_id")
var galaxy: Galaxy
}
final class Galaxy: Model {
...
@Children(for: \.$galaxy)
var planets: [Planet]
}
立即加载 - Eager Loading
Fluent已经可以创建query代码时提前加载modle的关联关系,再比如使用Codable
encoders序列化model时也会立即加载modle之间的关联关系。
参照上面的Modle声明, Fluent
查询Planet
(星球)时会立即加载其父关系的 Galaxy
(银河系),通过.with()
函数:
let planets = try Planet.query(on: db).with(\.$galaxy).all().wait()
for planet in planets {
print(planet.galaxy) // Galaxy
}
通过返回的JSON代码更好理解。
[
{
"id": ...,
"name": "Earth", //地球
"galaxy": {
"id": ...,
"name": "Milky Way" //银河系
}
},
...
]
分片读和分片更新 - Partial Reads & Updates
Fluent提供了新的 API可以实现分片读取和分片更新数据库, 从数据库中获取的models,如果仅仅对其部分信息修改并提交更新,Fluent仅传送修改的那部分内容。
新的测试框架 - XCTVapor
看代码:
import XCTVapor
app.test(.GET, to: "hello") { res in
XCTAssertEqual(res.status, .ok)
XCTAssertEqual(res.body.string, "Hello, world!")
}
默认内存测试,启动HTTP sever并且通过HTTP client测试,使用 testable
:
app.testable(method: .running).test(.GET, to: ...) {
// verify response
}
HTTP/2 & TLS
默认支持 HTTP/2 and TLS. 开启方式如此简单:
app.server.configuration.supportVersions = [.two]
配置TLS configuration启动TLS支持:
app.server.configuration.tlsConfiguration = .forServer(...)
Synchronous Content
Vapor’s Content
APIs now operate synchronously:
let newUser = try req.content.decode(CreateUser.self)
print(newUser) // CreateUser
This improvement is thanks to a new default policy on route handlers to collect streaming HTTP bodies before calling the handler. HTTP body collection can be disabled when registering routes:
app.on(.POST, "streaming", body: .stream) { req in
// req.body.data may be nil
// use req.body.collect
}
Backpressure
优化大文件传输时内存过载问题,及时存盘避免内存过度消耗。
Graceful Shutdown
Application
或其他类型增加了 close()
或 shutdown()
方法,在实例摧毁之前必须执行,以减少Bug出现。
let app = Application()
defer { app.shutdown() }
New Command API
Vapor更新了 Command
命令。有效的属性包装器有 @Argument
, @Option
, @Flag
:
final class ServeCommand: Command {
struct Signature: CommandSignature {
@Option(name: "hostname", short: "H", help: "Set the hostname")
var hostname: String?
@Option(name: "port", short: "p", help: "Set the port")
var port: Int?
@Option(name: "bind", short: "b", help: "Set hostname and port together")
var bind: String?
}
func run(using context: CommandContext, signature: Signature) throws {
print(signature.hostname) // String?
}
}
JWT
增加一个新的签名验证包 JWTKit . JSON Web Token signing and verification (HMAC, RSA, ECDSA) using OpenSSL
Jobs
待发布Jobs
1.0, vapor/jobs.
声明方法:
struct Email: Codable {
var to: String
var message: String
}
struct EmailJob: Job {
func dequeue(_ context: JobContext, _ email: Email) -> EventLoopFuture<Void> {
print("sending email to \(email.to)")
...
}
}
通过 .add()
添加任务
import Jobs
import Vapor
app.jobs.add(EmailJob())
启动任务进程:
swift run Run jobs
通过HTTP激活任务:
app.get("send-email") { req in
req.jobs.dispatch(EmailJob.self, Email(...))
.map { HTTPStatus.ok }
}
或者,定时任务:
// weekly
app.jobs.schedule(Cleanup())
.weekly()
.on(.monday)
.at("3:13am")
// daily
app.jobs.schedule(Cleanup())
.daily()
.at("5:23pm")
// hourly
app.jobs.schedule(Cleanup())
.hourly()
.at(30)
一起期待Vapor 4的发布吧~ 🎉
itreefly.com翻译,原文:https://forums.swift.org/t/whats-new-in-vapor-4/31832