0

The project work with OAuth2 protocol. But there is token in request body, not in header. So I couldn't find a way send request again with new token. Flow: request -> fail -> refresh token -> request

Request exp:
{"request": "asd", "token":"...", "request_data":{} } I'm sending same request with same token when error occurs, as follows:

requestExp().flatMap(resource -> {
          return Single.just(new Object());
    }).retryWhen(th-> {
        AtomicInteger counter = new AtomicInteger();
        return th.takeWhile(e -> {
            if (Objects.equals(e.getMessage(), "test")) {
                return counter.getAndIncrement() != 1;
            } 
        });
    });

Any help?

3
  • Are you using retrofit/okhttp? If yest, you can set an authenticator to the client. When you get error 401, the authenticator will be invoked. In the authenticator you can get the new token and then add it to your response body. Commented Jun 17, 2021 at 12:50
  • @DominikSetniewski Can authenticator add token to request body? Can you show sample? thanks your advance Commented Jun 17, 2021 at 14:28
  • I bellieve it's possible, but looks nasty.:/ Commented Jun 17, 2021 at 16:14

1 Answer 1

2

You can try something like this:

    .retryWhen {
            return@retryWhen it.flatMapSingle { throwable ->
                if (throwable is HttpException && throwable.code() == 401) {
                    // when 401 error, let's update the token and retry after that
                    println("Invalid token")
                    return@flatMapSingle refreshToken() // token source will be updated
                }
                // other error, no retry
                return@flatMapSingle Single.error<Any>(throwable)
            }
        }

Example:

fun main() {
    class Params(val token: String)
    class Result

    val tokenSource = BehaviorSubject.create<String>()
    tokenSource.onNext("invalid_token")

    fun request(params: Params): Single<Result> {
        if (params.token != "valid_token") {
            val responseBody = "".toResponseBody("application/json".toMediaTypeOrNull())
            val response = retrofit2.Response.error<Any>(401, responseBody)
            return Single.error(HttpException(response))
        }
        return Single.just(Result())
    }

    fun refreshToken(): Single<String> {
        return Single.fromCallable {
            val token = "valid_token"
            tokenSource.onNext(token)
            return@fromCallable token
        }
    }  

    val s = tokenSource.firstOrError() // get latest token
            .flatMap { request(Params(it)) }
            .retryWhen {
                return@retryWhen it.flatMapSingle { throwable ->
                    if (throwable is HttpException && throwable.code() == 401) {
                        println("Invalid token")
                        return@flatMapSingle refreshToken() // token source will be updated
                    }
                    return@flatMapSingle Single.error<Any>(throwable)
                }
            }
            .subscribe(
                    {
                        println("Success")
                    },
                    {
                        println("Error $it")
                    }
            )

}

Sign up to request clarification or add additional context in comments.

2 Comments

"Retrywhen" sends request with same values although refresh token :( I tried this
Retry resubscribes to the upstream observable. If you use a processor like in the example, you gonna get the latest values. If you used an observable, which wraps the token in Rx assembly time, you gonna get old values. There's also different approach with onErrorResumeNext. You can simply put refresh token observable chained with your original API request observable there

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.