使用retryWhen根据http错误代码更新令牌

tsk*_*bru 16 ios swift alamofire rx-swift moya

我在如何使用moya和rxswift刷新oauth令牌时找到了这个例子,我必须稍微修改才能编译.这段代码适用于我的场景80%.它的问题是它将运行所有http错误,而不仅仅是401错误.我想要的是将我的所有其他http错误传递为错误,以便我可以处理其他地方而不是在这里吞下它们.

使用此代码,如果我得到一个HttpStatus 500,它将运行3次验证码,这显然不是我想要的.

我试图改变这个代码来处理只处理401错误,但似乎无论我做什么我都无法得到编译的代码.它总是抱怨错误的返回类型,"Cannot convert return expression of type Observable<Response> to return type Observable<Response>"这对我来说没有意义..

我想要的:处理401,但停止所有其他错误

import RxSwift
import KeychainAccess
import Moya

public extension ObservableType where E == Response {

  /// Tries to refresh auth token on 401 errors and retry the request.
  /// If the refresh fails, the signal errors.
  public func retryWithAuthIfNeeded() -> Observable<E> {
    return self.retryWhen {
      (e: Observable<ErrorType>) in
      return Observable.zip(e, Observable.range(start: 1, count: 3), resultSelector: { $1 })
        .flatMap { i in
          return AuthProvider.sharedInstance.request(
            .LoginFacebookUser(
              accessToken: AuthenticationManager.defaultInstance().getLoginTokenFromKeyChain(),
              useFaceBookLogin: AuthenticationManager.defaultInstance().isFacebookLogin())
            )
            .filterSuccessfulStatusCodes()
            .mapObject(Accesstoken.self)
            .catchError {
              error in
              log.debug("ReAuth error: \(error)")
              if case Error.StatusCode(let response) = error {
                if response.statusCode == 401 {
                  // Force logout after failed attempt
                  log.debug("401:, force user logout")
                  NSNotificationCenter.defaultCenter().postNotificationName(Constants.Notifications.userNotAuthenticated, object: nil, userInfo: nil)
                }
              }
              return Observable.error(error)
            }.flatMapLatest({
              token -> Observable<Accesstoken> in
              AuthenticationManager.defaultInstance().storeServiceTokenInKeychain(token)
              return Observable.just(token)
            })
      }
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

sol*_*ell 16

编译错误

哪一行有编译错误?在我看来,这将是这条线:

.catchError {
    error in
    //...
    return Observable.error(error)  // is this the line causing the compilation error?
}
Run Code Online (Sandbox Code Playgroud)

如果是这样,那可能是因为catchError期望块返回Observable<Response>一个错误的情况下它可以继续,而不是一个Observable<ErrorType>.

在任何一种情况下,它都有助于使用更多类型来注释您的代码,以便您可以查明这样的问题,以及帮助Swift编译器,它通常无法自己解决这些类型的问题.所以这样的事情会对你有所帮助:

.catchError {
    error -> Observable<Response> in
    //...
    return Observable.error(error)  // Swift should have a more accurate and helpful error message here now
}
Run Code Online (Sandbox Code Playgroud)

请注意,我只是向您展示错误是什么以及如何让Xcode为您提供更好的错误消息.你想要归还的是不正确的.

只有重试 401

我不确定为什么你期望这个代码401区别对待(除了发布到通知中心和日志记录).实际上,你正在捕捉错误,但是你总是在结尾处返回Observable一个Error事件(return Observable.error(error)),所以它永远不会重试.

401重试,您应该ObservableretryWhen块中返回一个,它将发送一个 Next事件(表示您要重试).对于所有其他状态代码,即Observable应发送Error(因为您目前正在做),这将意味着,你希望重试,和你想的错误传播.

所以像这样:

.retryWhen { errorObservable -> Observable<ErrorType> in
    log.debug("ReAuth error: \(error)")
    if case Error.StatusCode(let response) = error where response.statusCode == 401 {
        log.debug("401:, force user logout")
        NSNotificationCenter.defaultCenter().postNotificationName(Constants.Notifications.userNotAuthenticated, object: nil, userInfo: nil)
        // If `401`, then return the `Observable<ErrorType>` which was given to us
        // It will emit a `.Next<ErrorType>`
        // Since it is a `.Next` event, `retryWhen` will retry.
        return errorObservable
    }
    else {
        // If not `401`, then `flatMap` the `Observable<ErrorType>` which
        // is about to emit a `.Next<ErrorType>` into
        // an `Observable<ErrorType>` which will instead emit a `.Error<ErrorType>`.
        // Since it is an `.Error` event, `retryWhen` will *not* retry.
        // Instead, it will propagate the error.
        return errorObservable.flatMap { Observable.error($0) }
    }
}
Run Code Online (Sandbox Code Playgroud)