Error Management with paging library android - android

I am using paging library version 2.1.0 in with java. How I can manage false status or empty array from API. I tried finding some solution but didn't get any.

If you're using Retrofit, you could use Interceptor to intercept error code.
Below is the code to handle response code error 401. Similarly, you can handle any response code.
var retrofit:Retrofit = Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.baseUrl(BASE_URL)
.client(getOkHttpClient())
.build()
private fun getOkHttpClient() : OkHttpClient{
val okHttpCLient = OkHttpClient.Builder()
.addInterceptor(object : Interceptor{
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
val response = chain.proceed(request)
if(response.code() == 401){
Log.e(TAG,"Un-Authorized user")
}
return response
}
})
return okHttpCLient.build()
}
//OkHttp Library in Gradle file:
implementation 'com.squareup.okhttp3:okhttp:3.12.0'

Related

Android Retrofit OkHttpClient Interceptor Add Header Gets Error "HTTP 403 Forbidden"

So, my goal is to embed the api key into my Retrofit object so that I don't need to manually append it as query parameter inside each request function, then I did the following (learn from: https://proandroiddev.com/headers-in-retrofit-a8d71ede2f3e):
private val interceptor = Interceptor { chain ->
val newRequest = chain.request().newBuilder().run {
addHeader("api_key", Constants.API_KEY)
build()
}
chain.proceed(newRequest)
}
private val okHttpClient = OkHttpClient.Builder().run {
connectTimeout(15, TimeUnit.SECONDS)
readTimeout(15, TimeUnit.SECONDS)
addInterceptor(interceptor) //<- apply Interceptor
build()
}
//apply the okHttpClient to my Retrofit object...
But it failed and gave this error: HTTP 403 Forbidden.
PS: Before adding this Interceptor everything works fine
Before:
#GET("neo/rest/v1/feed")
suspend fun getAsteroidsResult(
#Query("start_date") startDate: String,
#Query("end_date") endDate: String,
#Query("api_key") apiKey: String = Constants.API_KEY
): ResponseBody
Could you please add log interceptor and set the log level and provide a log?
compile 'com.squareup.okhttp3:logging-interceptor:3.2.0'
And sth like this :
OkHttpClient.Builder okBuilder = new OkHttpClient.Builder();
okBuilder.addInterceptor(new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BASIC).setLevel
(HttpLoggingInterceptor.Level.BODY).setLevel(HttpLoggingInterceptor.Level.HEADERS))

How to add header to retrofit response in Android

In my application i want get data from server and for this i should add some header such as Accept and Content_Type .
For connect to server i used Retrofit library.
For set headers i use okHttp client and i write below codes, but not set header to api response!
My Client codes:
class ApiClient() {
private val apiServices: ApiServices
init {
//Gson
val gson = GsonBuilder()
.setLenient()
.create()
//Http log
val loggingInterceptor = HttpLoggingInterceptor()
loggingInterceptor.level =
if (BuildConfig.DEBUG) HttpLoggingInterceptor.Level.BODY else HttpLoggingInterceptor.Level.NONE
//Http Builder
val clientBuilder = OkHttpClient.Builder()
clientBuilder.interceptors().add(loggingInterceptor)
clientBuilder.addInterceptor { chain ->
val request = chain.request()
request.newBuilder().addHeader(
CONTENT_TYPE,
APPLICATION_JSON
).build()
chain.proceed(request)
}
clientBuilder.addInterceptor { chain ->
val request = chain.request()
request.newBuilder().addHeader(
ACCEPT,
APPLICATION_JSON
).build()
chain.proceed(request)
}
//Http client
val client = clientBuilder
.readTimeout(CONNECTION_TIMEOUT, TimeUnit.SECONDS)
.writeTimeout(CONNECTION_TIMEOUT, TimeUnit.SECONDS)
.connectTimeout(CONNECTION_TIMEOUT, TimeUnit.SECONDS)
.callTimeout(CONNECTION_TIMEOUT, TimeUnit.SECONDS)
.retryOnConnectionFailure(true)
.build()
//Retrofit
val retrofit = Retrofit.Builder()
.baseUrl(BASE_URL + BASE_URP_PREFIX)
.client(client)
.addConverterFactory(GsonConverterFactory.create(gson))
.addConverterFactory(ScalarsConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.createWithScheduler(Schedulers.io()))
.build()
//Init mapApiServices
apiServices = retrofit.create(ApiServices::class.java)
}
companion object {
private var apiClient: ApiClient? = null
fun getInstance(): ApiClient =
apiClient ?: synchronized(this) {
apiClient
?: ApiClient().also {
apiClient = it
}
}
}
}
How can i fix it?
The first option to add a static header is to define the header and respective value for your API method as an annotation. The header gets automatically added by Retrofit for every request using this method. The annotation can be either key-value-pair as one string or as a list of strings.
The example above shows the key-value-definition for the static header:
Further, you can pass multiple key-value-strings as a list encapsulated in curly brackets {} to the #Headers annotation.
How you can pass multiple key-value-strings as a list encapsulated in curly brackets:
A more customizable approach are dynamic headers. A dynamic header is passed like a parameter to the method. The provided parameter value gets mapped by Retrofit before executing the request.
Define dynamic headers where you might pass different values for each request:
Happy Coding!! 😎

Retrofit2 authentication error to IBM's Speech to Text

I am trying to access IBM's Speech to Text service without using the library. I am using Retrofit with GSON.
The issue is in the authentication, which apparently does not occur correctly, returning code 401. From the official documentation, the HTTP request should come in this format
curl -X POST -u "apikey:{apikey}" \
--header "Content-Type: audio/flac" \
--data-binary #{path_to_file}audio-file.flac \
"{url}/v1/recognize"
When I test the curl command with my credentials, the service works fine.
This is the interface I'm using
interface SpeechToTextApi {
#Multipart
#POST("v1/recognize")
fun speechToText(
#Header("Authorization") authKey: String,
#Part("file") filename: RequestBody,
#Part voiceFile: MultipartBody.Part
): Call<List<SpeechToText>>
}
where I have the following data classes
data class SpeechToText(val results: List<SttResult>)
data class SttResult(val alternatives: List<RecognitionResult>, val final: Boolean)
data class RecognitionResult(val confidence: Float, val transcript: String)
and this is how I set up Retrofit
private val retrofit = Retrofit.Builder()
.baseUrl(STT_BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
private val service = retrofit.create(SpeechToTextApi::class.java)
while calling the actual service looks like this
val requestFile = RequestBody.create(MediaType.parse("audio/mp3"), file.name)
val body = MultipartBody.Part.createFormData("file", file.name, requestFile)
service
.speechToText(getString(R.string.stt_iam_api_key), requestFile, body)
.enqueue(object: Callback<List<SpeechToText>> {
override fun onResponse(call: Call<List<SpeechToText>>, response: Response<List<SpeechToText>>) {
val listOfStts = response.body()
Log.d(TAG, "Response code: ${response.code()}")
if (listOfStts != null) {
for (stt in listOfStts) {
for (res in stt.results) {
Log.d(TAG, "Final value: ${res.final}")
for (alt in res.alternatives) {
Log.d(TAG, "Alternative confidence: ${alt.confidence}\nTranscript: ${alt.transcript}")
Toast.makeText(this#MainActivity, alt.transcript, Toast.LENGTH_SHORT).show()
}
}
}
}
}
override fun onFailure(call: Call<List<SpeechToText>>, t: Throwable) {
Log.d(TAG, "Error: ${t.message}")
t.printStackTrace()
}
})
Recordings are MP3 files, for which I am sure they are stored correctly and accessible. I have replaced audio/flac with audio/mp3 as well.
Issue seems to be in the way authentication works. Prior to the code I have shown above, I've used
private val retrofit = Retrofit.Builder()
.baseUrl(STT_BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.client(OkHttpClient.Builder()
.addInterceptor { chain ->
val request = chain.request()
val headers = request
.headers()
.newBuilder()
.add("Authorization", getString(R.string.stt_iam_api_key))
.build()
val finalRequest = request.newBuilder().headers(headers).build()
chain.proceed(finalRequest)
}
.build())
.build()
but the same response code 401 persisted. Of course, the interface method lacked the #Header parameter.
Any sort of help is much appreciated.
I am kind of saddened by the fact nobody was able to solve this one sooner, but here's the solution I came across by accident when working on a different project altogether.
As you can see from the curl command, authentication comes in the form of username: password pattern, in this case, username being apikey string and password is your API key.
So the way you should tackle this is by building your Retrofit instance this way:
fun init(token: String) {
//Set logging interceptor to BODY and redact Authorization header
interceptor.level = HttpLoggingInterceptor.Level.BODY
interceptor.redactHeader("Authorization")
//Build OkHttp client with logging and token interceptors
val okhttp = OkHttpClient().newBuilder()
.addInterceptor(interceptor)
.addInterceptor(TokenInterceptor(token))
.build()
//Set field naming policy for Gson
val gsonBuilder = GsonBuilder()
gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
//Build Retrofit instance
retrofit = Retrofit.Builder()
.baseUrl(IBM_BASE_URL)
.addConverterFactory(GsonConverterFactory.create(gsonBuilder.create()))
.client(okhttp)
.build()
}
and create this custom interceptor
class TokenInterceptor constructor(private val token: String) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val original = chain.request()
val requestBuilder = original
.newBuilder()
.addHeader("Authorization", Credentials.basic("apikey", token))
.url(original.url)
return chain.proceed(requestBuilder.build())
}
}
You need to use Credentials.basic() in order to encode credentials.
I really hope somebody with a similar issue stumbles across this and saves themselves some time.

Retrofit + RxJava Not making API Request

I have an issue with Retrofit and RxJava2 in Kotlin.
Here is my build.gradle:
// Retrofit
implementation 'com.squareup.retrofit2:retrofit:2.6.1'
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.6.1' implementation 'com.squareup.retrofit2:converter-moshi:2.6.1'
implementation 'com.squareup.okhttp3:okhttp:4.2.0'
implementation 'com.squareup.moshi:moshi-kotlin:1.8.0'
// RxJava
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
implementation 'io.reactivex.rxjava2:rxjava:2.2.11'
I have the following Retrofit interface with a single request, login. I also have a simple data class called User.
data class User (
private val firstName: String
)
interface ApiService {
#POST("auth/login")
fun login(#Body body: String) : Observable<User>
}
When I try to make a request and subscribe to it, no request is being sent. I have checked my server logs and the server is not receiving anything at all. There are also no errors in the app logs. I'm not sure what I am doing wrong since I've looked at many articles/tutorials and they all said to do it this way.
val client = Retrofit.Builder()
.baseUrl("https://example.com/api/")
.addCallAdapterFactory(RxJava2CallAdapaterFactory.create())
.build()
.create(ApiService::class.java)
client.login(JSONObject().put("email", ...).toString())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe()
Could anyone explain to me what I'm actually doing wrong?
Edit:
I have tried the follow code and I still get the same result. No request is made.
val okHttpClient = OkHttpClient.Builder().addInterceptor(
HttpLoggingInterceptor(HttpLoggingInterceptor.Logger.DEFAULT)
).build()
val client = Retrofit.Builder()
.baseUrl("https://example.com/api/")
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.client(okHttpClient)
.build()
.create(ApiClient::class.java)
client.login(JSONObject().put("email", ...).toString())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ user -> println(user) }, { error -> println(error) })
You are missing to add a Moshi converter to your retrofit instance.
.addConverterFactory(MoshiConverterFactory.create())
You should add a logging interceptor to see the logs.
val httpClient = OkHttpClient.Builder()
.addInterceptor(loggingInterceptor)
.build()
val builder = Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(RxJava2CallAdapaterFactory.create())
val apiService = builder.client(httpClient).build().create(ApiService::class.java)
I assume that you add INTERNET permission to AndroidManifest.xml
As #sonnet suggests, you should add callbacks to your requests
apiService.login(JSONObject().put("email", ...).toString())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ user -> ... }, { error -> ... })

How to specify Get-Request encoding (Retrofit + OkHttp)

I'm using Retrofit2 + OkHttp3 in my Android app to make a GET - Request to a REST-Server. The problem is that the server doesn't specify the encoding of the JSON it delivers. This results in an 'é' being received as '�' (the Unicode replacement character).
Is there a way to tell Retrofit or OkHttp which encoding the response has?
This is how I initialize Retrofit (Kotlin code):
val gson = GsonBuilder()
.setDateFormat("d.M.yyyy")
.create()
val client = OkHttpClient.Builder()
.build()
val retrofit = Retrofit.Builder()
.baseUrl(RestService.BASE_URL)
.client(client)
.addConverterFactory(GsonConverterFactory.create(gson))
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build()
val rest = retrofit.create(RestService::class.java)
PS: The server isn't mine. So I cannot fix the initial problem on the server side.
Edit: The final solution
class EncodingInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val response = chain.proceed(chain.request())
val mediaType = MediaType.parse("application/json; charset=iso-8859-1")
val modifiedBody = ResponseBody.create(mediaType, response.body().bytes())
val modifiedResponse = response.newBuilder()
.body(modifiedBody)
.build()
return modifiedResponse
}
}
One way to do this is to build an Interceptor that takes the response and sets an appropriate Content-Type like so:
class ResponseInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val response = chain.proceed(chain.request())
val modified = response.newBuilder()
.addHeader("Content-Type", "application/json; charset=utf-8")
.build()
return modified
}
}
You would add it to your OkHttp client like so:
val client = OkHttpClient.Builder()
.addInterceptor(ResponseInterceptor())
.build()
You should make sure you either only use this OkHttpClient for your API that has no encoding specified, or have the interceptor only add the header for the appropriate endpoints to avoid overwriting valid content type headers from other endpoints.
class FixEncodingInterceptor implements Interceptor {
#Override
public Response intercept(Chain chain) throws IOException {
Response response = chain.proceed(chain.request());
MediaType oldMediaType = MediaType.parse(response.header("Content-Type"));
// update only charset in mediatype
MediaType newMediaType = MediaType.parse(oldMediaType.type()+"/"+oldMediaType.subtype()+"; charset=windows-1250");
// update body
ResponseBody newResponseBody = ResponseBody.create(newMediaType, response.body().bytes());
return response.newBuilder()
.removeHeader("Content-Type")
.addHeader("Content-Type", newMediaType.toString())
.body(newResponseBody)
.build();
}
}
and add to OkHttp:
builder.addInterceptor(new FixEncodingInterceptor());
This post is old but I found a solution that works for me in Kotlin (the answer of #BryanHerbst didn't quite worked for me)
class EncodingInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val response = chain.proceed(chain.request())
var encodedBody = ""
val encoding = InputStreamReader(
response.body?.byteStream(),
Charset.forName("ISO-8859-1")
).forEachLine {
encodedBody += it
}
return response.newBuilder()
.addHeader("Content-Type", "application/xml; charset=utf-8")
.body(encodedBody.toResponseBody())
.build()
}
}

Categories

Resources