[CN]What’s New in Vapor 4

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 替换原生的URLSessionAsync 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

~ END ~