Обновите sh токен доступа с URLSession после получения кода ответа 401 и запроса на повторную попытку - PullRequest
3 голосов
/ 19 января 2020

Я работаю над созданием сетевого клиента для моего iOS приложения, использующего методы авторизации OAuth 2.0 (Access & Refresh Token). Для моего сетевого клиента есть функция, которую я пытался реализовать:

  • Когда возникает ошибка 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
    }
}

Это правильный подход, чтобы обновить sh токен и повторить вызов API или есть лучший способ?

...