Alamofire 二次封装
2022-09-05 本文已影响0人
西风那个吹呀吹
须导入 Alamofire HandyJSON
1. 基类模型
struct CoscoBaseModel: HandyJSON {
/// 错误码
var code: Int?
var status: Int?
/// 数据
var data: Any?
/// 提示信息
var message: String?
/// 成功 true 失败 false
var success: Bool = false
}
2. token 模型
struct AccessToken: HandyJSON, Identifiable, Codable {
var id = UUID()
var access_token: String?
var token_type: String?
var expires_in: Double?
var storageTimestamp: TimeInterval = Date().timeIntervalSince1970
}
3. 拦截器
class CoscoRequestInterceptor: RequestInterceptor {
func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (Result<URLRequest, Error>) -> Void) {
let request = sign(request: urlRequest)
completion(.success(request))
}
func retry(_ request: Request, for session: Session, dueTo error: Error, completion: @escaping (RetryResult) -> Void) {
guard let response = request.task?.response as? HTTPURLResponse, (response.statusCode == 401) else {
/// 这个请求没有因为 401 token 过期
/// 则进行原始请求,不重试
return completion(.doNotRetryWithError(error))
}
// 确保只重试一次,否则会无限重试下去
guard request.retryCount == 0 else {
return completion(.doNotRetry)
}
HttpClient.getAccessToken { token in
// 缓存token
let json = try? JSONEncoder().encode(token)
UserDefaults.standard.set(json, forKey: "coscoToken")
// 1秒后进行重试
completion(.retryWithDelay(1))
} errorCompletion: { msg in
// 刷新token失败放弃重试
completion(.doNotRetryWithError(error))
}
}
// header 添加 token
private func sign(request: URLRequest) -> URLRequest {
guard let urlString = request.url?.absoluteString else {
return request
}
if urlString == Api.accessTokenUrl() {
return request
}
var retRequest = request
if let model = UserDefaults.standard.value(forKey: "coscoToken") {
let token = try? JSONDecoder().decode(AccessToken.self, from: model as! Data)
retRequest.setValue(token?.access_token ?? ""), forHTTPHeaderField: "Authorization")
}
return retRequest
}
}
4. 调用类和方法
class HttpClient {
class func get(_ url: String, params: [String : Any]? = nil, encoding: ParameterEncoding = URLEncoding.default) {
AF.request(url, method: .get, parameters: params, encoding: encoding, headers: nil, interceptor: CoscoRequestInterceptor(), requestModifier: nil).validate({ request, response, data in
// 401 这种返回,按照请求成功处理。所以不会触发 Retrier. 此处对特定状态码401返回错误
let statusCode = response.statusCode
if statusCode != 401 {
return .success(())
} else {
return .failure(AFError.responseValidationFailed(reason: .unacceptableStatusCode(code: 401)))
}
}).responseString(encoding: .utf8) { response in
// 用utf8 编码,否则中文会是乱码
switch response.result {
case .success(let json):
guard let baseModel = CoscoBaseModel.deserialize(from: json) else {
return print("请求出错~")
}
print("数据--:", baseModel)
case .failure(let error):
print("错误", error)
}
}
}
// 获取token
class func getAccessToken(success: @escaping (_ token : AccessToken) -> (), errorCompletion: @escaping (_ msg : String) -> ()) {
// token 请求地址
let url = Api.accessTokenUrl()
let params = 此处为获取token的参数
let headers: HTTPHeaders = HTTPHeaders(["Content-Type" : "application/x-www-form-urlencoded;charset=UTF-8"])
// 注意:此处是用 URL 编码参数的 POST 请求方式,不同后端开发或许不同的参数方式,具体情况具体对待
AF.request(url, method: .post, parameters: params, encoder: URLEncodedFormParameterEncoder.default, headers: headers, interceptor: nil, requestModifier: nil).responseJSON { response in
switch response.result {
case .success(let json):
guard let tokenModel = AccessToken.deserialize(from: json as? Dictionary) else {
return errorCompletion("获取accessToken失败")
}
success(tokenModel)
case .failure(let error):
print(error)
errorCompletion("服务器连接出错啦~")
}
}
}
}
注意:
用了.responseString
这个闭包,必须加上参数(encoding: .utf8)
,否则中文会乱码,没有中文的数据,加不加都无所谓。
PS: 让Swift社区丰富起来吧