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