使用retryWhen根据http错误代码更新令牌 [英] Using retryWhen to update tokens based on http error code

查看:745
本文介绍了使用retryWhen根据http错误代码更新令牌的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I found this example on How to refresh oauth token using moya and rxswift which I had to alter slightly to get to compile. This code works 80% for my scenario. The problem with it is that it will run for all http errors, and not just 401 errors. What I want is to have all my other http errors passed on as errors, so that I can handle them else where and not swallow them here.

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

With this code, if I get a HttpStatus 500, it will run the authentication code 3 times which is obviously not what I want.

我试图改变这个代码只处理句柄 401 错误,但似乎无论我做什么我都无法获得编译代码。它总是抱怨错误的返回类型,无法将Observable类型的返回表达式< Response>转换为返回类型Observable< Response>这对我没有任何意义..

Ive tried to alter this code to handle only handle 401 errors, but it seem that no matter what I do I can't get the code to compile. It's always complaining about wrong return type, "Cannot convert return expression of type Observable<Response> to return type Observable<Response>" which makes no sense to me..

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

What I want: handle 401, but stop on all other errors

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)
            })
      }
    }
  }
}


推荐答案

编译错误



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

Compilation Error

Which line has the compilation error? It seems to me that it would be this line:

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

如果是这样,可能是因为 catchError 期望该块返回 Observable< Response> ,如果出现错误,它可以继续使用,而不是 Observable< ErrorType>

If so, it's probably because catchError is expecting the block to return an Observable<Response> with which it can continue in case of an error, and not an Observable<ErrorType>.

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

In either case, it helps to annotate your code with more types so that you can pinpoint problems like this, as well as help the Swift compiler, which often can't figure out these kinds of things on its own. So something like this would have helped you:

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

请注意,我只是向您展示错误是什么以及如何让Xcode为您提供更好的错误消息。您尝试返回的内容仍然不正确。

Note that I'm only showing you what the error is and how to get Xcode to give you better error messages. What you're trying to return still isn't correct.

我不确定为什么你期望这段代码以不同方式处理 401 (除了发布到通知中心和记录) 。实际上,您正在捕获错误,但是您总是返回 Observable ,其中包含错误事件结束(返回Observable.error(错误)),因此它永远不会重试。

I'm not sure why you're expecting this code to treat 401 differently (other than posting to the notification center and logging). As it is, you're catching the error, but you're always returning an Observable with an Error event at the end (return Observable.error(error)), so it will never retry.

要获得 401 要重试,你应该从 retryWhen 块返回 Observable ,它将发送 Next 事件(表示您要重试)。对于所有其他状态代码, Observable 应发送错误(正如您目前所做的那样),这将表示您想要重试,并且您希望传播错误。

To get 401 to retry, you should return an Observable from the retryWhen block, which will send a Next event (signifying that you want to retry). For all other status codes, that Observable should send an Error (as you're currently doing), which will signify that you don't want to retry, and that you'd like the error propagated.

所以像这样:

.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) }
    }
}

这篇关于使用retryWhen根据http错误代码更新令牌的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆