Ретранслятор Alamofire 4 и адаптер не могут видеть измененный доступ - PullRequest
0 голосов
/ 08 мая 2018

Я получаю новый токен доступа с ретранслятором alamofire и адаптирую протокол. Я могу получить новый токен, но иногда, когда другой поток вызывает тот же метод, он не работает, и запрос не выполняется, даже когда генерируется новый токен доступа.

Я только что изменил пример, и теперь я использую синхронный запрос для получения токена доступа, поскольку я не хочу отправлять дополнительный запрос в Adapt, если узнаю, что токен недействителен.

Странная проблема заключается в том, что когда я печатаю ответ на неудавшийся запрос, я вижу, что в запросе все еще был старый токен в заголовке. Что мне здесь не хватает?

func isTokenValid() -> Bool {
    return Date() < self.expiryTime
}

func adapt(_ urlRequest: URLRequest) throws -> URLRequest {
    var urlRequest = urlRequest
    urlRequest = processRequest(urlRequest: urlRequest)
    return urlRequest
}

func processRequest(urlRequest: URLRequest) -> URLRequest {
    DDLogInfo("******access token : \(self.accessToken)***************")
    DDLogInfo("***** expiry Time: \(self.expiryTime)***************")
    var urlRequest = urlRequest
    lock.lock(); defer {   DDLogInfo( "Thread UnLocked ⚡️: \(Thread.current)\r" + "?: \(OperationQueue.current?.underlyingQueue?.label ?? "None")\r")
        lock.unlock()
    }
    DDLogInfo( "Thread Locked ⚡️: \(Thread.current)\r" + "?: \(OperationQueue.current?.underlyingQueue?.label ?? "None")\r")

    if !isTokenValid() {
        let _ = self.refreshAccessToken()
    }

    urlRequest = self.appendToken(urlRequest: urlRequest)
    DDLogInfo("here \(urlRequest)")
    return urlRequest
}

func appendToken(urlRequest: URLRequest) -> URLRequest {
    if let urlString = urlRequest.url?.absoluteString, urlString.hasPrefix(baseURLString) {
        var urlRequest = urlRequest
        DDLogInfo("token appended : \(self.accessToken)")
        urlRequest.setValue(self.accessToken, forHTTPHeaderField: Constants.KeychainKeys.accessToken)
    }
    return urlRequest
}

// MARK: - RequestRetrier

 func handleFailedRequest(_ completion: @escaping RequestRetryCompletion) {
    requestsToRetry.append(completion)
    if !isRefreshing {
        lock.lock()
        print( "Thread Locked⚡️: \(Thread.current)\r" + "?: \(OperationQueue.current?.underlyingQueue?.label ?? "None")\r")

        let succeeded = self.refreshAccessToken()
        self.requestsToRetry.forEach {
            print("token fetched \(succeeded): \(self.accessToken)")
            $0(succeeded, 0.0)
        }
        self.requestsToRetry.removeAll()
        DDLogInfo( "Thread UnLocked⚡️: \(Thread.current)\r" + "?: \(OperationQueue.current?.underlyingQueue?.label ?? "None")\r")
        lock.unlock()
    }
}


func should(_ manager: SessionManager, retry request: Request, with error: Error, completion: @escaping RequestRetryCompletion) {

    if let response = request.task?.response as? HTTPURLResponse, response.statusCode == 401, request.retryCount < 3 {
        handleFailedRequest(completion)
    } else {
        completion(false, 0.0)
    }
}

func updateTokens (accessToken: String, refreshToken: String, accessTokenExpiresIn: Double) {
    self.accessToken = accessToken
    self.refreshToken = refreshToken
    let expiryDate = Date(timeIntervalSinceNow: accessTokenExpiresIn - Constants.KeychainKeys.expirationBuffer)
    AppSettings.sharedInstance.tokenExpiryTime = expiryDate
    self.expiryTime = expiryDate
    do {try keychainWrapper.save(values: ["accessToken": self.accessToken, "refreshToken": self.refreshToken])} catch {
        DDLogError("unable to save accessToken")
        }
}

// MARK: - Private - Refresh Tokens

  fileprivate func refreshAccessToken() -> Bool {
    DDLogInfo("^^^^^^^^")
    Thread.callStackSymbols.forEach {  DDLogInfo($0) }

    var success = false
    guard !isRefreshing else { return success }
    let refreshRequest = URLRequestConfigurations.configRefreshProviderAgent(refreshToken: self.refreshToken)
    let result = URLSession.shared.synchronousDataTask(with: refreshRequest)

    self.isRefreshing = false
    do {
        if let data = result.0, let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String : Any] {
            if let accessToken = json["accessToken"] as? String, let refreshToken = json["refreshToken"] as? String, let time = json["accessTokenExpiresIn"] as? Double {
                updateTokens(accessToken: accessToken, refreshToken: refreshToken, accessTokenExpiresIn: time)
                success = true
            } else {
                DDLogError("unable to find tokens/expiryInterval from refresh request")
            }
        } else {
            DDLogError("unable to receive data from refresh request")
        }
    } catch {
        DDLogError("unable to parse json response from refersh token request")
    }
    return success
}

Ответы [ 3 ]

0 голосов
/ 18 мая 2018

Я уже сталкивался с той же проблемой с AFNetworking. к сожалению, причины этого очень глупы После 2-3 дней исследований я обнаружил, что это происходит из-за coockies и caches.

Давайте разберемся с основными понятиями Alamofire

Alamofire, по сути, является оберткой вокруг NSURLSession. Его менеджер использует значение по умолчанию NSURLSessionConfiguration, вызывая `defaultSessionConfiguration ()

NSURLSessionConfiguration ссылка для defaultSessionConfiguration() говорит:

В конфигурации сеанса по умолчанию используется постоянный дисковый кэш (кроме случаев, когда результат загружается в файл) и сохраняет учетные данные в связке ключей пользователя. Он также хранит куки (по по умолчанию) в том же общем хранилище файлов cookie, что и NSURLConnection, и NSURLDскачать классы.

Просто отключите кеширование для своего API, используя приведенный ниже код.

Отключение URLCache

let manager: Manager = {
    let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
    configuration.URLCache = nil
    return Manager(configuration: configuration)
}()

Или вы также можете настроить политику кэширования запросов

let manager: Manager = {
    let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
    configuration.requestCachePolicy = .ReloadIgnoringLocalCacheData
    return Manager(configuration: configuration)
}()

Решение: просто отключите сцепление для своих API. Это сработает для меня. В вашем случае это может быть для вас.

0 голосов
/ 27 августа 2018

Я нашел ответ на проблему.

В адаптационном методе мне пришлось использовать другую переменную для urlRequest, так как он не изменял запрос при использовании того же имени переменной. Как вы можете видеть ниже, я изменил переменную на «mutableRequest»

func appendToken(urlRequest: URLRequest) -> URLRequest {
    var mutableRequest = urlRequest
    if let urlString = urlRequest.url?.absoluteString, urlString.hasPrefix(baseURLString) {
        DDLogInfo("token appended : \(self.accessToken)")
        mutableRequest.setValue(self.accessToken, forHTTPHeaderField: Constants.KeychainKeys.accessToken)
    }
    return mutableRequest
}
0 голосов
/ 14 мая 2018

Можете ли вы проверить, создав синглтон-класс для сетевой активности. Шаблон singleton гарантирует, что будет создан только один экземпляр класса. Вот так: -

open class NetworkHelper {

class var sharedManager: NetworkHelper {
    struct Static{
        static let instance: NetworkHelper = NetworkHelper()
    }
    return Static.instance
}

.... Put you network call method here


}
...