收到401响应码和重试请求后,使用URLSession刷新访问令牌

时间:2020-01-19 20:44:37

标签: ios swift urlsession

我正在为使用OAuth 2.0授权技术(AccessRefresh Token)的iOS应用构建网络客户端。我一直在努力实现的网络客户端功能:

  • 当发生401错误时,这意味着Access Token已过期,我需要将Refresh Token发送到服务器以获得新的Access Token
  • 获得新的Access Token后,我需要重做上一个收到401错误的请求。

到目前为止,我已经为我的网络客户端编写了以下代码:

typealias NetworkCompletion = Result<(Data, URLResponse), FRNetworkingError>

/// I am using a custom result type to support just an Error and not a Type object for success
enum NetworkResponseResult<Error> {
    case success
    case failure(Error)
}

class FRNetworking: FRNetworkingProtocol {
    fileprivate func handleNetworkResponse(_ response: HTTPURLResponse) -> NetworkResponseResult<Error> {
        switch response.statusCode {
        case 200...299: return .success
        case 401: return .failure(FRNetworkingError.invalidAuthToken)
        case 403: return .failure(FRNetworkingError.forbidden)
        case 404...500: return .failure(FRNetworkingError.authenticationError)
        case 501...599: return .failure(FRNetworkingError.badRequest)
        default: return .failure(FRNetworkingError.requestFailed)
        }
    }

    func request(using session: URLSession = URLSession.shared, _ endpoint: Endpoint, completion: @escaping(NetworkCompletion) -> Void) {
        do {
            try session.dataTask(with: endpoint.request(), completionHandler: { (data, response, error) in
                if let error = error {
                    print("Unable to request data \(error)")
                    // Invoke completion for error
                    completion(.failure(.unknownError))
                } else if let data = data, let response = response {
                    // Passing Data and Response into completion for parsing in ViewModels
                    completion(.success((data, response)))
                }
            }).resume()
        } catch {
            print("Failed to execute request", error)
            completion(.failure(.requestFailed))
        }
    }
}

端点只是构建URLRequest的结构:

struct Endpoint {
    let path: String
    let method: HTTPMethod
    let parameters: Parameters?
    let queryItems: [URLQueryItem]?
    let requiresAuthentication: Bool

    var url: URL? {
        var components = URLComponents()
        components.scheme = "http"
        components.host = "127.0.0.1"
        components.port = 8000
        components.path = "/api\(path)"
        components.queryItems = queryItems
        return components.url
    }

    func request() throws -> URLRequest {
        /// Creates a request based on the variables per struct
    }
}

我在哪里放置允许FRNetworking.request()获得新令牌并重试请求的代码?

我在else if let data = data, let response = response语句中做了以下操作:

if let response = response as? HTTPURLResponse {
    let result = self.handleNetworkResponse(response)
    switch result {
    case .failure(FRNetworkingError.invalidAuthToken):
        break
    // TODO: Get new Access Token and refresh?
    default:
        break
    }
}

这是刷新令牌并重做API调用的正确方法,还是有更好的方法?

0 个答案:

没有答案