Home > Blockchain >  How to add querey parameter only if there's a value in second request in Retrofit or OkHttpClie
How to add querey parameter only if there's a value in second request in Retrofit or OkHttpClie

Time:09-27

In this app, I used Blogger API, I have this method that gets posts

@GET
suspend fun getPostList(@Url URL: String): Response<PostList>

this is the base URL that I used with it

const val BASE_URL = "https://www.googleapis.com/blogger/v3/blogs/$BLOG_ID/posts/"

when at first request it become like this

https://www.googleapis.com/blogger/v3/blogs/12345678910/posts/maxResults=20&key=API_KEY

when the second request it should be getting a token parameter pageToken=ABCDE in result that I needed to do pagination, so I edit the BASE URL in viewModel and add the pageToken parameter to it then I pass it to the getPostList method again

and it looks like this

https://www.googleapis.com/blogger/v3/blogs/12345678910/posts/maxResults=20&pageToken=ABCDEFG&key=API_KEY

this my network layer

  private val interceptor : HttpLoggingInterceptor = HttpLoggingInterceptor().apply {
        this.level = HttpLoggingInterceptor.Level.BODY
    }

    @Singleton
    @Provides
    fun postAPIService(): PostAPIService {
        return Retrofit.Builder()
            .baseUrl(Constants.BASE_URL)
            .client(
                OkHttpClient.Builder().readTimeout(
                    15, TimeUnit.SECONDS
                ).connectTimeout(15, TimeUnit.SECONDS)
                    .addNetworkInterceptor(interceptor)
                    .addInterceptor { chain ->
                        val url = chain
                            .request()
                            .url
                            .newBuilder()
                            .addQueryParameter("maxResults", Constants.MAX_RESULT)
                            .addQueryParameter("key", Constants.API_KEY)
                            .build()
                        chain.proceed(chain.request().newBuilder().url(url)
                            .build())
                    }.build()

            )
            .addConverterFactory(GsonConverterFactory.create())
            .build().create(PostAPIService::class.java)
    }
}

In ViewModel layer

    /** RETROFIT **/
    var postsResponse: MutableLiveData<NetworkResult<PostList>> = MutableLiveData()
    var postListResponse: PostList? = null

    val label = MutableLiveData<String>()
    var finalURL: MutableLiveData<String> = MutableLiveData()
    private val token = MutableLiveData<String?>()

private suspend fun getPostsSafeCall() {

        postsResponse.postValue(NetworkResult.Loading())

        Log.e(TAG, "getPostsSafeCall finalURL is ${finalURL.value!!}")

        if (hasInternetConnection()) {
            try {

                if (finalURL.value.isNullOrEmpty()) {
                    finalURL.postValue(BASE_URL)
                }

                val response = mainRepository.remoteDataSource.getPostList(finalURL.value!!)
                postsResponse.value = handlePostsResponse(response)

            } catch (exception: Exception) {
                postsResponse.postValue(NetworkResult.Error(exception.message.toString()))
//                Log.e(TAG, e.message   e.cause)
                if (exception is HttpException) {
                    errorCode.postValue(exception.code())
                    Log.e(TAG, "getPostsSafeCall: errorCode $errorCode")
                    Log.e(TAG, "getPostsSafeCall: ${exception.message.toString()}")
                }
            }
        } else {
            postsResponse.postValue(NetworkResult.Error("No Internet Connection."))
        }
    }


private fun handlePostsResponse(response: Response<PostList>): NetworkResult<PostList> {
        if (response.isSuccessful) {

            token.value = response.body()?.nextPageToken

            Log.d(TAG, "handlePostsResponse: token = ${response.body()?.nextPageToken.toString()}")

            response.body()?.let { resultResponse ->
                Log.d(
                    TAG, "handlePostsResponse: old token is: ${token.value} "  
                            "new token is: ${resultResponse.nextPageToken}"
                )

                finalURL.postValue(
                    BASE_URL   "&pageToken=${token.value}"
                )

                if (postListResponse == null) {
                    postListResponse = resultResponse
                } else {
                    val oldPosts = postListResponse?.items
                    val newPosts = resultResponse.items
                    oldPosts?.addAll(newPosts)
                }

                Log.d(TAG, "handlePostsResponse: ${token.value}")
                Log.e(TAG, "handlePostsResponse finalURL is ${finalURL.value!!}")

                for (item in resultResponse.items) {
                    insertItem(item)
                }
                return NetworkResult.Success(postListResponse ?: resultResponse)

            }

        } else  {
            if (token.value == null) {
                errorCode.postValue(400)
            } else {
                errorCode.postValue(response.code())
            }

            Log.d(TAG, "handlePostsResponse: ${response.code().toString()}")
            Log.d(TAG, "handlePostsResponse: ${response.headers().toString()}")
            Log.d(TAG, "handlePostsResponse: ${response.headers().toString()}")
//        Log.d(TAG, "handlePostsResponse: final ${finalURL.value.toString()}")
            return NetworkResult.Error(
                "network results of handlePostsResponse ${response.body().toString()}"
            )
        }
        return NetworkResult.Error(
            "network results of handlePostsResponse ${response.body().toString()}"
        )
    }

WHAT I NEED

a way to inject pageToken parameter dynamically only when there's a value (NOT NULL OR EMPTY) to avoid the following in final/base URL

https://www.googleapis.com/blogger/v3/blogs/4294497614198718393/posts/?maxResults=20&pageToken=null&key=API_KEY

this cause error 400 badRequest

{
  "error": {
    "code": 400,
    "message": "We're sorry, but the value for field pageToken was not valid.",
    "errors": [
      {
        "message": "We're sorry, but the value for field pageToken was not valid.",
        "domain": "global",
        "reason": "invalid"
      }
    ]
  }
}

I tried edit the PostAPIService method to look like that

  @GET("posts")
    suspend fun getPostList(@Query("pageToken") pageToken: String=""): Response<PostList>

and use it in viewModel like this

 val response = if (token.value.isNullOrEmpty()) {
 mainRepository.remoteDataSource.getPostList()
 } else {
mainRepository.remoteDataSource.getPostList(token.value!!)
}

but unfortunately this causes badRequest also because of pageToken=null in BASE_URL

CodePudding user response:

As docs said Retrofit ignores null query values, it means that you just need to remove pageToken from your base url and make corresponding parameter nullable in your function:

 @GET("posts")
 suspend fun getPostList(@Query("pageToken") pageToken: String? = null): Response<PostList
  • Related