使用重试时根据http错误代码更新令牌

我发现这个例子如何刷新oauth令牌使用moya和rxswift ,我不得不稍微改变,以编译。 此代码为我的scheme工作80%。 它的问题是,它会运行所有的HTTP错误,而不仅仅是401错误。 我想要的是把所有其他的http错误作为错误传递,以便我可以在别处处理它们,而不是在这里吞下它们。

有了这个代码,如果我得到一个HttpStatus 500 ,它将运行validation码3次,这显然不是我想要的。

我试图改变这个代码来处理只处理401错误,但似乎不pipe我做了什么,我不能得到的代码编译。 它总是抱怨错误的返回types, "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) }) } } } } 

编译错误

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

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

如果是这样的话,可能是因为catchError希望块返回一个Observable<Response> ,在发生错误时它可以继续,而不是一个Observable<ErrorType>

无论哪种情况,它都有助于用更多types对代码进行注释,以便您可以查明这样的问题,并帮助Swift编译器,而Swift编译器通常无法自行找出这些types的东西。 所以这样的事情会帮助你:

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

请注意,我只向你显示错误是什么,以及如何让Xcode给你更好的错误信息。 你试图返回的东西仍然是不正确的。

只能在401重试

我不知道为什么你期待这个代码对待401不同(除了张贴到通知中心和日志logging)。 事实上,你正在捕获错误,但是你总是在最后返回一个带有Error事件的return Observable.error(error) ),所以它不会再试。

要让401重试,你应该从retryWhen块返回一个Observable ,它将发送一个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) } } } 

当你catchError ,如果它不是一个401错误,那么你只需要throw错误。 这将发送错误的pipe道。