18

我在如何使用 moya 和 rxswift 刷新 oauth 令牌上找到了这个示例,我必须稍作改动才能编译。此代码适用于我的场景 80%。它的问题是它将针对所有 http 错误运行,而不仅仅是 401 错误。我想要的是让我所有的其他 http 错误作为错误传递,这样我就可以在其他地方处理它们,而不是在这里吞下它们。

使用此代码,如果我得到 a 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)
            })
      }
    }
  }
}
4

3 回答 3

19

编译错误

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

.catchError {
    error in
    //...
    return Observable.error(error)  // is this the line causing the compilation error?
}

如果是这样,这可能是因为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
}

请注意,我只是向您展示错误是什么以及如何让 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) }
    }
}
于 2016-07-12T12:20:35.160 回答
1

当你catchError,如果它不是 401 错误,那么你只需要throw错误。这会将错误发送到管道中。

于 2016-07-18T01:30:58.953 回答
-2

有一个不同的解决方案可以在不使用 Observable 的情况下解决这个问题。它是在纯 RxSwift 上编写的,并在失败的情况下返回经典错误。

使用 RxSwift 和 Moya 刷新 Auth0 会话令牌的简单方法

该解决方案的主要优点是它可以很容易地适用于类似于 Auth0 的不同服务,允许在移动应用程序中对用户进行身份验证。

于 2018-09-24T12:31:20.450 回答