Alamofire 二次封装

须导入 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) -> 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社区丰富起来吧

你可能感兴趣的:(Alamofire 二次封装)