網(wǎng)站首頁 編程語言 正文
正文
到目前為止,我們已經(jīng)看了一個簡單的Request/Response的結構體和實現(xiàn)。接下來,我們來討論一下發(fā)送請求和接收響應。
如果我們回想一下第一節(jié),我們會用HTTP回調(diào)給他,我們發(fā)送了一個請求,并且最終得到了一個響應(忽略Error)沒有任何“任務”或者代理亦或其他什么東西。我們發(fā)送(或加載)一個請求,最終都會得到一個響應。
如果我們用一個方法來描述那個功能,那么這個方法如下所示
func load(request: HTTPRequest, completion: @escaping (HTTPResponse) -> Void)
我們發(fā)送了一個請求,在未來的某個節(jié)點,閉包將會被執(zhí)行,表里涵蓋的是我們要的響應。當然,單個方法并不是我們想要的;換句話說,我們想要表述的是一個接口。所以,我們會把他封裝進一個protocol中:
public protocol HTTPLoading { func load(request: HTTPRequest, completion: @escaping (HTTPResponse) -> Void) }
當然,我們可能會得到一個error響應。所以,我們要用自己的“results”重命名(typealiase)來替換HTTPResponse:
func load(request: HTTPRequest, completion: @escaping (HTTPResult) -> Void)
讓我們停下來欣賞一下。 這個方法就是我們定義網(wǎng)絡框架的核心功能所需要的全部。 就是這樣:一個方法。 棒極了。
遵循HTTPLoading協(xié)議
URLSession
就像“路上的橡膠”,這是在我們的請求通過空中(或有線)發(fā)送到我們指定的服務器之前需要清除的最后一個障礙。 因此,我們在 URLSession
上實現(xiàn) HTTPLoading
是為了將 HTTPRequest
轉換為會話需要的 URLRequest
,這是有道理的:
extension URLSession: HTTPLoading { public func load(request: HTTPRequest, completion: @escaping (HTTPResult) -> Void) guard let url = request.url else { // we couldn't construct a proper URL out of the request's URLComponents completion(.failure(...)) return } // construct the URLRequest var urlRequest = URLRequest(url: url) urlRequest.httpMethod = request.method.rawValue // copy over any custom HTTP headers for (header, value) in request.headers { urlRequest.addValue(value, forHTTPHeaderField: header) } if request.body.isEmpty == false { // if our body defines additional headers, add them for (header, value) in request.body.additionalHeaders { urlRequest.addValue(value, forHTTPHeaderField: header) } // attempt to retrieve the body data do { urlRequest.httpBody = try request.body.encode() } catch { // something went wrong creating the body; stop and report back completion(.failure(...)) return } } let dataTask = session.dataTask(with: urlRequest) { (data, response, error) in // construct a Result<HTTPResponse, HTTPError> out of the triplet of data, url response, and url error let result = HTTPResult(request: request, responseData: data, response: response, error: error) completion(result) } // off we go! dataTask.resume() } }
這應該很容易理解。 我們正在執(zhí)行從 HTTPRequest
值中提取信息并將其應用于 URLRequest
的步驟。 如果在任何時候出現(xiàn)問題,那么我們將報告錯誤。 (您需要自己填寫 ... 部分以構造適當?shù)?HTTPError
值)
假設構建一切順利,我們最終會得到一個 URLRequest
,我們可以將其轉換為 URLSessionDataTask
并執(zhí)行它。 當它完成時,我們將獲取響應值,將它們轉換為 HTTPResult
,并通過完成塊報告回來。
創(chuàng)建Result
在傳輸過程中的任何時候,我們的請求都可能失敗。 如果我們處于飛行模式或其他“未連接”狀態(tài),則請求可能永遠不會發(fā)送。 如果我們正在發(fā)送請求(在我們得到響應之前),網(wǎng)絡連接可能會斷開。 或者它可能會在我們發(fā)送后但在我們收到回復之前掉線。 或者它可能會在我們開始收到響應之后但在完全接收到響應之前下降。
這就是我們在定義請求和響應類型時創(chuàng)建 HTTPError
結構的原因,這意味著我們需要更加努力地構建我們的結果,而不是簡單地檢查“我是否得到了一些數(shù)據(jù)”。
在高層次上,初始化 HTTPResult
的邏輯大致如下所示:
var httpResponse: HTTPResponse? if let r = response as? HTTPURLResponse { httpResponse = HTTPResponse(request: request, response: r, body: responseData ?? Data()) } if let e = error as? URLError { let code: HTTPError.Code switch e.code { case .badURL: code = .invalidRequest case .unsupportedURL: code = ... case .cannotFindHost: code = ... ... default: code = .unknown } self = .failure(HTTPError(code: code, request: request, response: httpResponse, underlyingError: e)) } else if let someError = error { // an error, but not a URL error self = .failure(HTTPError(code: .unknown, request: request, response: httpResponse, underlyingError: someError)) } else if let r = httpResponse { // not an error, and an HTTPURLResponse self = .success(r) } else { // not an error, but also not an HTTPURLResponse self = .failure(HTTPError(code: .invalidResponse, request: request, response: nil, underlyingError: error)) }
用法
HTTPLoading 使用方法:
public class StarWarsAPI { private let loader: HTTPLoading = URLSession.shared public func requestPeople(completion: @escaping (...) -> Void) { var r = HTTPRequest() r.host = "swapi.dev" r.path = "/api/people" loader.load(request: r) { result in // TODO: interpret the result completion(...) } } }
我想在這里指出,我們在任何時候都不會解釋Response的狀態(tài)代碼。 獲得 500 Internal Server Error
或 404 Not Found
響應是成功的響應。 在這一層,“成功”意味著“我們得到了回應”,而不是“回應表明某種語義錯誤”。 解釋狀態(tài)代碼是特定于應用程序的邏輯。 在未來的帖子中,我們將允許基于狀態(tài)代碼的可定制的、特定于應用程序的行為(例如跟隨重定向或重試請求)。
我們定義的這個單一方法看似簡單,但也不完整。 我們還沒有指出任何主動取消請求的方法,我們需要調(diào)整我們的 HTTPLoading
協(xié)議以添加更多功能。 我們還將把它從協(xié)議轉換為類,原因我將在以后的帖子中解釋。
盡管存在這些小漏洞,該協(xié)議在其簡單性方面仍然很漂亮,它展示了一個好的問題概念化如何能夠產(chǎn)生強大而美麗的東西。
簡單是最終的復雜。
在下一篇文章中,我們將研究使用 HTTPLoading
協(xié)議來簡化單元測試。
原文鏈接:https://juejin.cn/post/7196888211758759996
相關推薦
- 2022-07-13 淺談Redis中的自動過期機制_Redis
- 2022-07-19 Ribbon負載均衡深入探究
- 2022-11-08 Go中init()執(zhí)行順序詳解_Golang
- 2024-03-21 【Spring Boot】Spring Boot 配置文件詳解(application.yml、ap
- 2022-10-01 使用C++實現(xiàn)插件模式時的避坑要點(推薦)_C 語言
- 2022-04-01 k8s記一次kubelet啟動后master無法獲取node信息
- 2022-07-16 SpringMVC 傳遞參數(shù)
- 2021-12-05 Linux系統(tǒng)運行級別詳細介紹_Linux
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細win安裝深度學習環(huán)境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結構-簡單動態(tài)字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支