本软件包为 Swift 提供 curl 的支持。本软件包使用 Swift Package Manager 构建,并且是 Perfect 项目的一部分。
请确保您已安装并激活最新的 Swift 4.0+ 工具链。
将此项目作为依赖项添加到您的 Package.swift 文件中。
.Package(url: "https://github.com/PerfectlySoft/Perfect-CURL.git", majorVersion: 3)
请确保您已安装 libcurl。
sudo apt-get install libcurl4-openssl-dev
import PerfectCURL
本软件包使用简单的请求/响应模型来访问 URL 内容。首先创建一个 CURLRequest
对象并根据您的需求进行配置,然后要求它执行请求并返回响应。响应由 CURLResponse
对象表示。
请求可以同步执行 - 阻塞调用线程直到请求完成;异步执行 - 将响应传递给回调函数;或者通过 Promise 对象执行 - 在后台线程上执行请求,让您能够链式执行其他任务、轮询或等待完成。
可以使用 URL 和一系列选项创建 CURLRequest
对象,也可以创建空白对象,然后完全配置。CURLRequest
提供以下初始化器
open class CURLRequest {
// Init with a url and options array.
public convenience init(_ url: String, options: [Option] = [])
/// Init with url and one or more options.
public convenience init(_ url: String, _ option1: Option, _ options: Option...)
/// Init with array of options.
public init(options: [Option] = [])
}
可以使用 Array<Option>
或可变参数提供选项。也可以在执行请求之前将选项直接添加到 CURLRequest.options
属性。
CURLRequest
选项由 CURLRequest.Option
枚举表示。每个枚举案例将具有零个或多个关联值,这些值指示特定选项的参数。例如,请求的 URL 可以使用选项 .url("https://httpbin.org/post")
指示。curl 提供的大多数选项都在 CURLRequest.Option
枚举中表示。可用选项的完整列表在本文档末尾附近给出。
POST 字段数据,包括文件上传,以与其他选项相同的方式添加到请求中。.postField(POSTField)
、.postData([UInt8])
和 .postString(String)
枚举案例将设置请求的 POST 内容。可以将 .postField
案例多次添加到请求中,因为每个实例代表一组名称/值对。.postData
和 .postString
案例应被认为是与其他 post 案例互斥的,因为添加任何一个都将覆盖任何先前设置的 POST 内容。添加任何类型的 POST 内容数据将自动将 HTTP 方法设置为 POST。
CURLRequest.POSTField
结构定义如下
open class CURLRequest {
public struct POSTField {
/// Init with a name, value and optional mime-type.
public init(name: String, value: String, mimeType: String? = nil)
/// Init with a name, file path and optional mime-type.
public init(name: String, filePath: String, mimeType: String? = nil)
}
}
下面的示例创建了一个 POST 请求,并添加了几个名称/值对以及一个文件。执行的请求将自动具有 "multipart/form-data" 内容类型。
let json = try CURLRequest(url, .failOnError,
.postField(.init(name: "key1", value: "value1")),
.postField(.init(name: "key2", value: "value2")),
.postField(.init(name: "file1", filePath: testFile.path, mimeType: "text/plain")))
.perform().bodyJSON
要执行请求,请调用 CURLRequest.perform
或 CURLRequest.promise
函数之一。如果请求成功,您将获得一个 CURLResponse
对象,该对象可用于获取响应数据。如果请求失败,将抛出 CURLResponse.Error
。如果无法连接、超时、收到格式错误的响应或收到 HTTP 响应的状态码等于或大于 400(当给定 .failOnError
选项时),则请求可能会失败。如果未给出 .failOnError
选项,则任何有效的 HTTP 响应都将成功,无论响应状态码如何。
用于获取响应的三个函数如下
public extension CURLRequest {
/// Execute the request synchronously.
/// Returns the response or throws an Error.
func perform() throws -> CURLResponse
/// Execute the request asynchronously.
/// The parameter passed to the completion callback must be called to obtain the response or throw an Error.
func perform(_ completion: @escaping (CURLResponse.Confirmation) -> ())
/// Execute the request asynchronously.
/// Returns a Promise object which can be used to monitor the operation.
func promise() -> Promise<CURLResponse>
}
第一个 CURLRequest.perform
函数在调用线程上同步执行请求。函数调用将阻塞,直到请求成功或失败。失败时,将抛出 CURLResponse.Error
。
第二个 CURLRequest.perform
函数根据需要在后台线程上异步执行请求。传递给此函数的参数是一个回调,一旦请求完成或失败,将给出 CURLResponse.Confirmation
。从回调中调用 confirmation 参数将返回 CURLResponse
或抛出 CURLResponse.Error
。
第三个函数 CURLRequest.perform
将返回一个 Promise<CURLResponse>
对象,该对象可用于链式执行进一步的活动并轮询或等待请求完成。与其他响应生成函数一样,如果发生错误,将抛出 CURLResponse.Error
。有关 Promise 对象的常规信息,可以在 Perfect-Thread 文档中找到。
以下三个示例显示了每个函数的使用方式。每个函数都将执行一个请求,并将生成的响应正文从 JSON 转换为 [String:Any] 字典。
• 同步获取 API 终结点并从 JSON 解码
let url = "https://httpbin.org/get?p1=v1&p2=v2"
let json: [String:Any] = try CURLRequest(url).perform().bodyJSON
• 异步获取 API 终结点并从 JSON 解码
let url = "https://httpbin.org/post"
CURLRequest(url).perform {
confirmation in
do {
let response = try confirmation()
let json: [String:Any] = response.bodyJSON
} catch let error as CURLResponse.Error {
print("Failed: response code \(error.response.responseCode)")
} catch {
print("Fatal error \(error)")
}
}
• 使用 Promise 异步获取 API 终结点并从 JSON 解码
let url = "https://httpbin.org/get?p1=v1&p2=v2"
if let json = try CURLRequest(url).promise().then { return try $0().bodyJSON }.wait() {
...
}
根据效率排序的三个可用函数将按以下顺序排列
perform
promise
perform
在高流量服务器上执行 CURL 请求时,建议使用异步响应函数之一。
可以通过调用 .reset
函数,将 CURLRequest
对象重用于后续连接。重置请求将清除任何先前设置的选项,包括目标 URL。.reset
函数接受一个可选参数,用于重新配置请求的新选项。
重置声明如下
public extension CURLRequest {
/// Reset the request. Clears all options so that the object can be reused.
/// New options can be provided.
func reset(_ options: [Option] = [])
/// Reset the request. Clears all options so that the object can be reused.
/// New options can be provided.
func reset(_ option: Option, _ options: Option...)
}
重置请求将使任何先前执行的 CURLResponse
对象无效。应重新执行重新配置的请求以获得更新的 CURLResponse
。
CURLResponse
对象提供对响应的内容正文的访问,可以作为原始字节、字符串或作为 JSON 解码的 [String:Any] 字典。此外,可以检索元信息,例如响应 HTTP 标头和状态码。
响应正文数据通过一系列只读 CURLResponse
属性提供
public extension CURLResponse {
/// The response's raw content body bytes.
public var bodyBytes: [UInt8]
/// Get the response body converted from UTF-8.
public var bodyString: String
/// Get the response body decoded from JSON into a [String:Any] dictionary.
/// Invalid/non-JSON body data will result in an empty dictionary being returned.
public var bodyJSON: [String:Any]
/// Get the response body decoded from JSON into a decodable structure
/// Invalid/non-JSON body data will throw errors.
public func bodyJSON<T: Decodable>(_ type: T.Type) throws -> T
}
可以通过调用 CURLResponse.get
函数之一并传入与所需数据对应的枚举值来检索其余的响应数据。指示这些值的枚举被分为三组,每组根据将返回的数据类型;String、Int 或 Double。枚举类型为 CURLResponse.Info.StringValue
、CURLResponse.Info.IntValue
和 CURLResponse.Info.DoubleValue
。此外,还提供了 get
函数,用于直接从响应中提取标头值。
public extension CURLResponse {
/// Get an response info String value.
func get(_ stringValue: Info.StringValue) -> String?
/// Get an response info Int value.
func get(_ intValue: Info.IntValue) -> Int?
/// Get an response info Double value.
func get(_ doubleValue: Info.DoubleValue) -> Double?
/// Get a response header value. Returns the first found instance or nil.
func get(_ header: Header.Name) -> String?
/// Get a response header's values. Returns all found instances.
func get(all header: Header.Name) -> [String]
}
为了方便起见,添加了属性,用于从响应中提取常用的请求数据,例如 url
和 responseCode
。
以下示例显示了如何从请求中提取标头和其他元数据
// get the response code
let code = response.get(.responseCode)
// get the response code using the accessor
let code = response.responseCode
// get the "Last-Modified" header from the response
if let lastMod = response.get(.lastModified) {
...
}
发生故障时,将抛出 CURLResponse.Error
对象。此对象提供生成的 CURL 错误代码(注意,这与任何 HTTP 响应代码都不同,并且是 CURL 特有的)。它还提供对错误消息字符串的访问,以及可用于进一步查询生成错误的 CURLResponse 对象。
请注意,根据请求上设置的选项,在发生错误后获得的响应对象可能没有任何关联的内容正文数据。
CURLResponse.Error
定义如下
open class CURLResponse {
/// An error thrown while retrieving a response.
public struct Error: Swift.Error {
/// The curl specific request response code.
public let code: Int
/// The string message for the curl response code.
public let description: String
/// The response object for this error.
public let response: CURLResponse
}
}
以下是可以设置的众多 CURLRequest
选项的列表。每个枚举案例指示选项的参数类型。这些枚举值可以在创建新的 CURLRequest
对象时使用,也可以通过将它们添加到现有对象的 .options
数组属性来使用。
CURLRequest.Option 枚举案例 | 描述 |
---|---|
.url(String) | 请求的 URL。 |
.port(Int) | 覆盖请求的端口。 |
.failOnError | 在 http 错误代码 >= 400 时失败。 |
.userPwd(String) | 冒号分隔的用户名/密码字符串。 |
.proxy(String) | 代理服务器地址。 |
.proxyUserPwd(String) | 代理服务器用户名/密码组合。 |
.proxyPort(Int) | 代理服务器的端口覆盖。 |
.timeout(Int) | 请求完成的最长时间(以秒为单位)。默认超时时间为从不超时。 |
.connectTimeout(Int) | 请求连接阶段的最长时间(以秒为单位)。默认超时时间为 300 秒。 |
.lowSpeedLimit(Int) | 传输的平均速度(以字节/秒为单位),在该速度之下,传输应在 .lowSpeedLimit 秒内低于此速度,请求才会太慢而被中止。 |
.lowSpeedTime(Int) | 传输速度应低于 .lowSpeedLimit 的时间(以秒为单位),在该时间之后,请求将被认为太慢而被中止。 |
.range(String) | 范围请求值,格式为 "X-Y",其中 X 或 Y 可以省略,X 和 Y 是字节索引 |
.resumeFrom(Int) | 请求应从该偏移量(以字节为单位)开始。 |
.cookie(String) | 为请求设置一个或多个 cookie。应采用 "name=value" 格式。用分号分隔多个 cookie:"name1=value1; name2=value2"。 |
.cookieFile(String) | 保存请求的 cookie 数据的文件的名称。 |
.cookieJar(String) | 将接收到的 cookie 写入的文件名。 |
.followLocation(Bool) | 指示请求应遵循重定向。默认为 false。 |
.maxRedirects(Int) | 请求应遵循的最大重定向数。默认为无限制。 |
.maxConnects(Int) | 可以为请求缓存的最大同时打开的持久连接数。 |
.autoReferer(Bool) | 启用后,当请求遵循 Location: 重定向时,请求将自动在 HTTP 请求中设置 Referer: 标头字段 |
.krbLevel(KBRLevel) | 设置 FTP 的 kerberos 安全级别。值应为以下值之一:.clear、.safe、.confidential 或 .private。 |
.addHeader(Header.Name, String) | 向请求添加标头。 |
.addHeaders([(Header.Name, String)]) | 向请求添加一系列标头。 |
.replaceHeader(Header.Name, String) | 添加或替换标头。 |
.removeHeader(Header.Name) | 删除默认内部添加的标头。 |
.sslCert(String) | 客户端 SSL 证书的路径。 |
.sslCertType(SSLFileType) | 指定客户端 SSL 证书的类型。默认为 .pem 。 |
.sslKey(String) | 客户端私钥文件的路径。 |
.sslKeyPwd(String) | 如果 SSL 密钥文件受密码保护,则要使用的密码。 |
.sslKeyType(SSLFileType) | 指定 SSL 私钥文件的类型。 |
.sslVersion(TLSMethod) | 强制请求使用特定版本的 TLS 或 SSL。 |
.sslVerifyPeer(Bool) | 指示请求是否应验证对等方的证书的真实性。 |
.sslVerifyHost(Bool) | 指示请求是否应验证服务器证书是否用于它所知的服务器。 |
.sslCAFilePath(String) | 包含一个或多个证书的文件的路径,这些证书将用于验证对等方。 |
.sslCADirPath(String) | 包含一个或多个证书的目录的路径,这些证书将用于验证对等方。 |
.sslCiphers([String]) | 覆盖用于 SSL 连接的密码列表。由一个或多个以冒号分隔的密码字符串组成。逗号或空格也可以作为分隔符,但通常使用冒号。“!”、“-” 和 “+” 可以用作运算符。 |
.sslPinnedPublicKey(String) | 指向已固定的公钥的文件路径。在协商 TLS 或 SSL 连接时,服务器会发送一个证书来表明其身份。从该证书中提取一个公钥,如果该公钥与提供给此选项的公钥不完全匹配,curl 将在发送或接收任何数据之前中止连接。 |
.ftpPreCommands([String]) | 在文件传输之前要运行的 (S)FTP 命令列表。 |
.ftpPostCommands([String]) | 在文件传输之后要运行的 (S)FTP 命令列表。 |
.ftpPort(String) | 指定主动 FTP 传输的本地连接端口。 |
.ftpResponseTimeout(Int) | 请求等待 FTP 服务器响应的秒数。 |
.sshPublicKey(String) | 用于 SSH 连接的公钥文件路径。 |
.sshPrivateKey(String) | 用于 SSH 连接的私钥文件路径。 |
.httpMethod(HTTPMethod) | 用于请求的 HTTP 方法。 |
.postField(POSTField) | 向请求添加单个 POST 字段。通常,为一个请求添加多个 POST 字段。 |
.postData([UInt8]) | 用于 POST 请求的原始字节数据。 |
.postString(String) | 用于 POST 请求的原始字符串数据。 |
.mailFrom(String) | 执行 SMTP 请求时,指定发送者的地址。 |
.mailRcpt(String) | 执行 SMTP 请求时,指定接收者。可以通过多次使用此选项来指定多个接收者。 |
以下列表描述了与 CURLResponse.get
函数一起使用的 CURLResponse.Info
用例,以检索响应信息。 这些列表根据将返回的数据类型进行分组; 分别为 StringValue
、IntValue
和 DoubleValue
。
CURLResponse.Info.StringValue 枚举用例 | 描述 |
---|---|
.url | 请求/响应的有效 URL。 这最终是响应数据来源的 URL。 在重定向的情况下,这可能与请求的 URL 不同。 |
.ftpEntryPath | 在登录到 FTP 服务器后,请求最终到达的初始路径。 |
.redirectURL | 请求本来会重定向到的 URL。 |
.localIP | 请求最近使用的本地 IP 地址。 |
.primaryIP | 请求最近连接到的远程 IP 地址。 |
.contentType | 请求的内容类型。 这是从 "Content-Type" 标头读取的。 |
CURLResponse.Info.IntValue 枚举用例 | 描述 |
---|---|
.responseCode | 最后接收到的 HTTP、FTP 或 SMTP 响应代码。 |
.headerSize | 所有接收到的标头的总大小(以字节为单位)。 |
.requestSize | 发出的请求的总大小(以字节为单位)。 在重定向的情况下,这将指示所有已发送请求的累计总数。 |
.sslVerifyResult | SSL 证书验证的结果。 |
.redirectCount | 已跟踪的重定向总数。 |
.httpConnectCode | 最后收到的对 CONNECT 请求的 HTTP 代理响应代码。 |
.osErrno | 可能触发失败的操作系统级别的 errno。 |
.numConnects | 请求必须进行的连接数才能生成响应。 |
.primaryPort | 请求最近连接到的远程端口。 |
.localPort | 请求最近使用的本地端口。 |
CURLResponse.Info.DoubleValue 枚举用例 | 描述 |
---|---|
.totalTime | 先前请求的总时间(以秒为单位)。 |
.nameLookupTime | 从开始到名称解析完成的总时间(以秒为单位)。 |
.connectTime | 从开始到与远程主机或代理的连接完成的总时间(以秒为单位)。 |
.preTransferTime | 从开始到文件传输即将开始所花费的时间(以秒为单位)。 |
.sizeUpload | 已上传的总字节数。 |
.sizeDownload | 已下载的总字节数。 |
.speedDownload | 平均下载速度(以字节/秒为单位)。 |
.speedUpload | 平均上传速度(以字节/秒为单位)。 |
.contentLengthDownload | 下载的内容长度。 此值是从 Content-Length 标头字段获取的。 |
.contentLengthUpload | 指定的上传大小。 |
.startTransferTime | 从请求开始到收到第一个字节所花费的时间(以秒为单位)。 |
.redirectTime | 在最终事务开始之前,所有重定向步骤(包括名称查找、连接、预传输和传输)所花费的总时间(以秒为单位)。 |
.appConnectTime | 从开始到完成与远程主机的 SSL/SSH 连接/握手所花费的时间(以秒为单位)。 |
有关 Perfect 项目的更多信息,请访问 perfect.org。