HTTP简介
HTTP基础结构
HTTP恳求体
HTTP 加载恳求
HTTP 模仿测试
HTTP 链式加载器
HTTP 动态修正恳求
HTTP 恳求选项
HTTP 重置
HTTP 撤销
HTTP 限流
HTTP 重试
HTTP 基础鉴权
HTTP 自动鉴权设置
HTTP 自动鉴权
HTTP 复合加载器
HTTP 脑筋风暴
HTTP 总结
到现在为止,咱们现已编写了足够多的代码来描绘 HTTPLoader
实例链,这些实例能够处理传入的 HTTPRequest
并最终生成 HTTPResult
。
然而,在某些情况下,咱们不希望每个恳求都以相同的办法加载。 上次咱们编写了 ApplyEnvironment
,这是一个 HTTPLoader
子类,它将运用预界说的 ServerEnvironment
值来填充恳求中的任何缺失值。 咱们将以此作为咱们的事例研究。
让咱们幻想一下,咱们决定用星球大战 wiki“Wookieepedia”中的附加信息来补充咱们的 StarWarsAPI
。 当然,咱们知道咱们能够手动设置主机和途径以及每个恳求发出时的所有内容,但最好不要这样做,你值得拥有夸姣的事物。
// it would be unfortunate to have to repeat this a lot
var request = HTTPRequest()
request.host = "starwars.fandom.com"
request.path = "/api/v1/Search/List"
request.queryItems = [
URLQueryItem(name: "query", value: "anakin")
]
相反,让咱们增加在恳求进入链之前指定整个环境的才能,然后教 ApplyEnvironment
加载器寻找它。 或许它看起来像这样:
var request = HTTPRequest()
request.serverEnvironment = .wookieepedia
request.path = "Search/List"
request.queryItems = [
URLQueryItem(name: "query", value: "anakin")
]
这看起来简直和曾经一样多的代码,但我信任它更具表现力。 咱们正在删去更多的魔法字符串(“starwars.fandom.com
”和“/api/v1
”)并在咱们的意图中更具描绘性(“咱们想要‘Wookieepedia’服务器环境”)。
咱们不想做的是回到咱们的 HTTPRequest
界说并为服务器环境增加一个新的存储特点。 并非每个恳求都需求指定一个服务器环境,并且在每个恳求上都为一个服务器环境腾出空间是一种糟蹋。 此外,假如咱们决定要指定其他每个恳求选项,则该办法不能很好地扩展。 (提示:有!)
相反,咱们将在恳求中界说一个私有转储场来存储这些选项,并为它们创建一个类型安全的接口。
从 SwiftUI
中罗致创意
在 Apple
的 SwiftUI
结构中有一个名为 PreferenceKey
的简练小协议。 它基本上是视图在其父视图层次结构中传递类型安全的“首选项值”的一种办法,因此某些先人能够查找并读取它。
咱们将为咱们的恳求运用同样的东西。 咱们将从一个协议开端:
public protocol HTTPRequestOption {
associatedtype Value
/// The value to use if a request does not provide a customized value
static var defaultOptionValue: Value { get }
}
该协议表示“选项”只是一种具有静态 defaultOptionValue
特点的类型,假如恳求未指定,咱们能够运用该特点。
接下来,咱们将教授 HTTPRequest
有关选项的知识:
public struct HTTPRequest {
...
private var options = [ObjectIdentifier: Any]()
public subscript<O: HTTPRequestOption>(option type: O.Type) -> O.Value {
get {
// create the unique identifier for this type as our lookup key
let id = ObjectIdentifier(type)
// pull out any specified value from the options dictionary, if it's the right type
// if it's missing or the wrong type, return the defaultOptionValue
guard let value = options[id] as? O.Value else { return type.defaultOptionValue }
// return the value from the options dictionary
return value
}
set {
let id = ObjectIdentifier(type)
// save the specified value into the options dictionary
options[id] = newValue
}
}
}
这是持有期权价值的基础设施。 现在假定恳求能够专门保存 ServerEnvironment
值:
public struct ServerEnvironment: HTTPRequestOption {
// the associated type is inferred to be "Optional<ServerEnvironment>"
public static let defaultOptionValue: ServerEnvironment? = nil
...
}
咱们的 ServerEnvironment
结构,它包含默许主机、途径前缀等值,也是一个 HTTPRequestOption
。 假如咱们没有在恳求上设置清晰的 ServerEnvironment
,那么恳求“持有”的值是 nil
(默许选项值),意思是“没有自界说的服务器环境”。
咱们能够增加的一件好事是对 HTTPRequest
的扩展,以使其更易于运用:
extension HTTPRequest {
public var serverEnvironment: ServerEnvironment? {
get { self[option: ServerEnvironment.self] }
set { self[option: ServerEnvironment.self] = newValue }
}
}
有了这个,咱们现在有办法在单个 HTTPRequest
上设置恣意数量的自界说值,并以类型安全的办法再次检索它们。
运用选项值
剩下的最后一件事是教您的ApplyEnvironment
程序如何寻找要运用的环境。 假如您还记得,该类现在看起来像这样:
public class ApplyEnvironment: HTTPLoader {
private let environment: ServerEnvironment
public init(environment: ServerEnvironment) {
environment = environment
super.init()
}
override public func load(request: HTTPRequest, completion: @escaping (HTTPResult) -> Void) {
var copy = request
if copy.host.isEmpty {
copy.host = environment.host
}
if copy.path.hasPrefix("/") == false {
// TODO: apply the environment.pathPrefix
}
// TODO: apply the query items from the environment
for (header, value) in environment.headers {
// TODO: add these header values to the request
}
super.load(request: copy, completion: completion)
}
}
咱们只需求对 load(request:completion:)
办法做一个简单的调整:
override public func load(request: HTTPRequest, completion: @escaping (HTTPResult) -> Void) {
var copy = request
// use the environment specified by the request, if it's present
// if it doesn't have one, use the one passed to the initializer
let requestEnvironment = request.serverEnvironment ?? environment
if copy.host.isEmpty {
copy.host = requestEnvironment.host
}
if copy.path.hasPrefix("/") == false {
// TODO: apply the requestEnvironment.pathPrefix
}
// TODO: apply the query items from the requestEnvironment
for (header, value) in requestEnvironment.headers {
// TODO: add these header values to the request
}
super.load(request: copy, completion: completion)
}
就是这样!
咱们现在为咱们增加了一种通过声明选项来自界说单个恳求行为的办法:一个类型安全的值,它随恳求一起带着并由各种加载器查看,以便它们能够针对该特定恳求动态改动它们的行为。
在以后的帖子中,咱们将运用选项来自界说多种行为,包括指定应如何重试恳求、它们的身份验证机制是什么(假如有)、应如何缓存响应(假如有)等等。
在咱们的下一篇文章中,咱们将从自界说加载器完成中退一步,看看“重置”加载器的概念。