介绍 Swift HTTP Types
我们很高兴地宣布一个新的开源软件包,名为 Swift HTTP Types。
Swift HTTP Types 基于来自服务器端 Swift、应用程序开发者和更广泛的 Swift 社区的见解而构建,旨在为 Swift 中的客户端/服务器 HTTP 操作提供一组共享的通用类型。
Swift 中的 HTTP
如今,对于许多用例来说,Swift 中的网络编程无处不在,涵盖了客户端、服务器、中间件以及互联网和其他网络中的许多其他参与者。HTTP 是最流行的网络技术之一,为全球各地的日常体验提供支持。
在 Apple 平台上,系统 HTTP 实现通过 Foundation 框架中的 URLSession
API 公开。对于服务器端 Swift 项目,推荐的 HTTP 堆栈在 SwiftNIO 中实现。
为了在使用 Swift 中的 HTTP 时提供最佳体验,跨多个项目通用的共享通用类型至关重要。
Swift HTTP Types
Swift HTTP Types 提供了 HTTP 消息核心构建块的通用表示。
HTTPRequest
和 HTTPResponse
代表客户端和服务器用例的 HTTP 消息。通过在多个项目中采用它们,可以在客户端和服务器之间共享更多代码,从而消除在使用多个框架时类型转换的成本。
这些类型以 Swift 优先的方式构建,以表示每个有效的 HTTP 消息。它们的表示侧重于现代 HTTP 版本,如 HTTP/3 和 HTTP/2,同时还保留了与 HTTP/1.1 的兼容性。
随着软件包的成熟,我们的目标是替换 SwiftNIO 的 HTTPRequestHead
和 HTTPResponseHead
以及 Foundation 的 URLRequest
和 URLResponse
的 HTTP 消息详细信息。
新的通用类型旨在适用于任何 HTTP 场景,并且不与任何现有框架绑定,从而消除了对重复 HTTP 抽象的需求。
使用示例
该 API 旨在提供符合人体工程学的方式来执行最常见的 HTTP 操作,同时可以干净地扩展以表示高级用例。
HTTP 请求的核心由方法、scheme、authority 和 path 组成
let request = HTTPRequest(method: .get, scheme: "https", authority: "www.example.com", path: "/")
我们也可以从 Foundation URL 创建相同的请求
var request = HTTPRequest(method: .get, url: URL(string: "https://www.example.com/")!)
我们可以在事后更改方法或其他属性
request.method = .post
request.path = "/upload"
创建响应同样简单直接
let response = HTTPResponse(status: .ok)
我们可以使用 headerFields
属性访问和修改标头字段
request.headerFields[.userAgent] = "MyApp/1.0"
常见的标头字段是内置的,我们可以轻松地为自定义标头字段和值提供扩展,因此我们可以在业务逻辑中使用相同的便捷语法
extension HTTPField.Name {
static let myCustomHeader = Self("My-Custom-Header")!
}
request.headerFields[.myCustomHeader] = "custom-value"
我们可以直接设置标头字段的值,包括值数组
request.headerFields[raw: .acceptLanguage] = ["en-US", "zh-Hans-CN"]
访问标头字段的方式大致相同,我们可以使用系统定义的字段或我们自己的扩展
request.headerFields[.userAgent] // "MyApp/1.0"
request.headerFields[.myCustomHeader] // "custom-value"
request.headerFields[.acceptLanguage] // "en-US, zh-Hans-CN"
request.headerFields[raw: .acceptLanguage] // ["en-US", "zh-Hans-CN"]
与 Foundation 集成
使用 URLSession
,我们可以轻松创建一个新的 HTTPRequest
来向 “www.example.com” 发送 POST 请求。在此请求上设置自定义 User-Agent
标头字段值非常直观,并且与类型系统集成以提供自动完成功能
var request = HTTPRequest(method: .post, url: URL(string: "https://www.example.com/upload")!)
request.headerFields[.userAgent] = "MyApp/1.0"
let (responseBody, response) = try await URLSession.shared.upload(for: request, from: requestBody)
guard response.status == .created else {
// Handle error
}
与 SwiftNIO 集成
当此软件包变得稳定时,SwiftNIO 集成将在 swift-nio-extras 软件包中提供。要配置 NIO 通道处理程序以与新的 HTTP 类型一起使用,我们可以在其他通道处理程序之前添加 HTTP2FramePayloadToHTTPServerCodec
NIOTSListenerBootstrap(group: NIOTSEventLoopGroup())
.childChannelInitializer { channel in
channel.configureHTTP2Pipeline(mode: .server) { channel in
channel.pipeline.addHandlers([
HTTP2FramePayloadToHTTPServerCodec(),
ExampleChannelHandler()
])
}.map { _ in () }
}
.tlsOptions(tlsOptions)
我们的示例通道实现同时处理 HTTPRequest
和 HTTPResponse
类型
final class ExampleChannelHandler: ChannelDuplexHandler {
typealias InboundIn = HTTPTypeServerRequestPart
typealias OutboundOut = HTTPTypeServerResponsePart
func channelRead(context: ChannelHandlerContext, data: NIOAny) {
switch unwrapInboundIn(data) {
case .head(let request):
// Handle request headers
case .body(let body):
// Handle request body
case .end(let trailers):
// Handle complete request
let response = HTTPResponse(status: .ok)
context.write(wrapOutboundOut(.head(response)), promise: nil)
context.writeAndFlush(wrapOutboundOut(.end(nil)), promise: nil)
}
}
}
请求和响应主体
HTTP 请求和响应主体目前不属于此软件包的一部分。
请继续使用现有的机制:Foundation 的 Data
和 InputStream
以及 SwiftNIO 的 ByteBuffer
。
我们有兴趣与社区一起探讨关于未来提供主体处理的提案。
参与进来
这个社区的经验和专业知识对于创建 Swift 中出色 HTTP 体验的构建块是无价的。
我们今天发布的 Swift HTTP Types 版本是与社区进行反馈和讨论的起点。
您可以开始通过以下方式参与:
- 试用 swift-http-types 软件包
- 参与 Swift 论坛上 #http 标签 的讨论
- 为您发现的任何问题开启 issue 并做出贡献
我们期待着共同探索 Swift 中 HTTP 的未来。