I am using Retrofit. Using Kotlin. I need to know the resonse status code. Like is it 200 or 500. How can I get it from the response ?
My Api class:
interface Api {
#POST("user/code/check")
fun checkSmsCode(#Body body: CheckCodeBody): Single<Response<Void>> }
This is how I am calling Api. But note that SERVE DOES NOT RETURN CODE FIELD IN RESPONSE BODY!
api.checkSmsCode(
CheckCodeBody(
code = code
)
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
//HOW TO CHECK STATUS RESPONSE STATUS CODE HERE???
},
{ e ->
when (e) {
is IOException -> view?.showNoNetworkAlert()
else -> view?.invalidCodeError()
}
}
).also {}
As I understood, in Java it was a easy peasy thing.
You just use response.code() or something similar and that's it. But how to achieve it in Kotlin?
so your on response should look something like this
override fun onResponse(call: Call<MyModel>?, response: Response<MyModel>?) {
//
}
})
then inside that you should just to able to do
override fun onResponse(call: Call<MyModel>?, response: Response<MyModel>?) {
response.code()
}
})
is this what your talking about?
yo need to use it
interface OnlineStoreService{
#Headers("Content-Type: application/json","Connection: close")
#POST
fun getDevices(
#Url url: String,
#Header("Authorization") token: String,
#Body apiParams: APIParams
): Observable<OnlineStoresInfo>
}
.subscribe({ onlineStoresInfo -> // or it -> where "it" it's your object response, in this case is my class OnlineStoresInfo
loading.value = false
devices.value = onlineStoresInfo.devices
}, { throwable ->
Log.e(this.javaClass.simpleName, "Error getDevices ", throwable)
loading.value = false
error.value = context.getString(R.string.error_information_default_html)
})
.subscribe({ it ->
// code
}, { throwable ->
//code
})
If you haven't configure your retrofit request method to return a Response<*> you won't be able to have the response code. Example:
interface SomeApi{
#POST("user/code/check")
fun checkSmsCode(#Body body: CheckCodeBody): Single<Response<String>>
}
And after you finish your request:
.subscribe({
//access response code here like : it.code()
//and you can access the response.body() for your data
//also you can ask if that response.isSuccessful
})
Related
I have a list of URLs that I need to call and if I got success response for all of them, I can continue.
I can simply do it with RxJava and Retrofit like:
#PUT
fun uploadFile(#Url url: String, #Body file: RequestBody): Single<Response<Void>>
Observable.fromIterable(uploadUrls)
.flatMapSingle {
val requestBody = InputStreamRequestBody(contentResolver, uri)
upsellServiceApi.uploadFile(url, requestBody)
}
.toList()
.subscribeOn(schedulerProvider.io())
.observeOn(schedulerProvider.ui())
.subscribe(
{ responses ->
if (responses.all { it.isSuccessful }) {
// continue
} else {
//error
}
},
{
// error
}
Now I need to do the same thing without retrofit and by only using okhttpclient. How should I do it?
You could use a library like https://github.com/liujingxing/rxhttp to keep using RxJava. Or implement it yourself Using RxJava and Okhttp
If you don't want to use RxJava, then just enqueue and handle callback methods yourself.
Solved it
Observable.fromIterable(uploadUrls)
.map {
val request = Request.Builder().url(url).put(InputStreamRequestBody(contentResolver!!, uri)).build()
okHttpClient.newCall(request).execute()
}
.collect(
{
mutableListOf<Response>()
},
{
list: MutableList<Response>, response: Response -> list.add(response)
}
)
.subscribeOn(schedulerProvider.io())
.observeOn(schedulerProvider.ui())
.subscribe(
{ responses ->
if (responses.all { it.isSuccessful }) {
// continue
} else {
// error
}
},
{
// error
}
).also { compositeDisposable.add(it) }
I'm trying to get data using Retrofit call.enqueue ( DrawCircles() ), when i debug the values are there, but i think it does not waits and my function proceeds with the rest of lines of code. So the problem when I run it, the List of results (myListCoord ) is always null, how to make synchronous calls.
here is my code:
doAsync {
var a = DrawCircles()
myListCoord = a.runCircles()
}
fun runCircles(): List<Coordinates>? {
val request = ServiceBuilder.buildService(TmdbEndpoints::class.java)
val call = request.getCorrdinates()
call.enqueue(object : Callback<MyList> {
override fun onResponse(call: Call<MyList>, response: Response<MyList>) {
if (response.isSuccessful){
Toast.makeText(this#DrawCircles, "Succès", Toast.LENGTH_LONG).show()
myListCoord = response.body()!!.locations
}
}
override fun onFailure(call: Call<MyList>, t: Throwable) {
Toast.makeText(this#DrawCircles, "${t.message}", Toast.LENGTH_SHORT).show()
}
})
return myListCoord
}
Have you tried using call.execute() instead of call.enqueue() ?
From the docs:
void enqueue(Callback<T> callback)
Asynchronously send the request and notify callback of its response or if an error occurred talking to the server, creating the request, or processing the response.
Response<T> execute() throws IOException
Synchronously send the request and return its response.
(Emphasis mine)
I am developing an Android application using Kotlin. In my application, I am consuming GraphQL API using Apollo Client. What I am trying to do now is that I want to retrieve a response field of the response.
This is my code
protected fun _handleLoginButtonClick(view: View) {
val apolloClient = ApolloClient.builder()
.serverUrl("https://app.herokuapp.com/graphql")
.okHttpClient(OkHttpClient())
.build()
val loginMutation = LoginMutation.builder()
.identity(view.etf_email.text.toString())
.password(view.etf_password.text.toString())
.build()
view.tv_login_error_message.text = "Started making request"
apolloClient.mutate(loginMutation).enqueue(object: ApolloCall.Callback<LoginMutation.Data>() {
override fun onFailure(e: ApolloException) {
view.tv_login_error_message.text = e.message
}
override fun onResponse(response: Response<LoginMutation.Data>) {
//here I dont know how to retrieve a field, accessToken
}
})
}
As you can see the comment in the onResponse callback, I cannot figure out how to retrieve the accessToken field. How can I retrieve it?
OnResponse Contains response Object and it has data object from where you can get your fields.
apolloClient.mutate(loginMutation).enqueue(object: ApolloCall.Callback<LoginMutation.Data>() {
override fun onFailure(e: ApolloException) {
view.tv_login_error_message.text = e.message
}
override fun onResponse(response: Response<LoginMutation.Data>) {
//here you can use response to get your model data like accessToken
response.data.(here you can get data from your model. eg accessToken)
}
})
I have REST POST call create order as Observable<OrderResponse>, when order create call is successful everything is fine, but then server returns error I get com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was BEGIN_ARRAY at line 1 column 2 path $ because Gson does not know how to handle json that have different fields than my response model
Server error response:
[{
"code": 99,
"message": "Please check your request"
}]
OrderResponse
data class OrderResponse(
#Expose
var orderId: String,
#Expose
var redirectUrl: String,
#Expose
var validUntil: Long
)
RxJava subscription
repository.requestCreateNewOrder(newOrder)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : Observer<OrderResponse> {
override fun onComplete() {
}
override fun onSubscribe(d: Disposable) {
compositeDisposable.add(d)
}
override fun onNext(t: OrderResponse) {
}
override fun onError(t: Throwable) {
//HERE I GET JsonSyntaxException
}
})
Retrofit Service
interface OrderService {
#Headers(
"Content-Type: application/x-www-form-urlencoded ",
"Connection: Keep-Alive",
"Accept-Encoding: application/json",
"User-Agent: Fiddler"
)
#FormUrlEncoded
#POST("/createOrder")
fun createOrder(#Field("orderId") orderId: String,
#Field("payCurrency") payCurrency: String,
#Field("payAmount") payAmount: Double,
#Header("Content-Length") length: Int): Observable<OrderResponse>}
Anyone have any suggestions for how to pass retrofit or gson the error model to know how to handle it
As you are using GSON to parse the JSON.
Your JSON sucessfull response will be something like
{
"orderId": 1,
"redirectUrl": "url",
"validUntil": 12414194
}
while for error response your JSON response start with Array.
[{
"code": 99,
"message": "Please check your request"
}]
So tell to server guy to not add the error response in Array [].
If you are getting the response as Array then you have to use list.
repository.requestCreateNewOrder(newOrder)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : Observer<List<OrderResponse>> {
override fun onComplete() {
}
override fun onSubscribe(d: Disposable) {
compositeDisposable.add(d)
}
override fun onNext(t: OrderResponse) {
}
override fun onError(t: Throwable) {
//HERE I GET JsonSyntaxException
}
})
In Observer response you have to add as List if your JSON response start with Array like error.
Solution : Correct it from backend for not adding Error Response in ARRAY.
As per your code you are only handling the success response
but you need to handle your error response too for this you need to ensure that your API should send you error codes different then 200 (success response generally greater then 200 and less then 300 i.e code >= 200 && code < 300 ) because retrofit consider 200-299 as success
You can achieve this simply by changing your observable return type to
Observable<Response< OrderResponse>>
and after receiving response from server simply check
if (orderResponse.isSuccessful()) {
//here you can handle success by using orderResponse.getbody()
} else {
// here you can display error message and if you further want
// to parse error response from server then use below function
errorOrderResponseHandling(orderResponse);
}
you want to further parse response into error model(in this example OrderAPIError is model class for error response) then below is the function
private void errorOrderResponseHandling(Response<OrderResponse> orderResponse) {
OrderAPIError orderAPIError = null;
try {
orderAPIError = new Gson().fromJson(String.valueOf(new
JSONObject(orderResponse.errorBody().string())), OrderAPIError.class);
// further orderAPIError object you can fetch server message and display
it to user as per your requirement
} catch (JSONException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
Above example is in android not in kotlin but you can get idea and use function accordingly in kotlin
I am quite new to RxJava and I am tryign to implement and OAuth2 flow with refresh tokens in my Android/Kotlin app.
I got a bit stuck with refreshing my access tokens inside an interceptor (I append access token for each API request inside an OAuthAuthenticator). The problem is that I would like to wait with populating the request until the token request is completed.
Could someone hint me how to achieve this?
This is my authenticator class:
class OAuthAuthenticator(
private val authStateManager: AuthStateManager,
private val authRepository: AuthRepository): Authenticator {
override fun authenticate(route: Route?, response: Response?): Request? {
// handle invalid token
if (response != null && response.code() == 401) {
println("Invalid access token")
authRepository.refreshToken()
.subscribe { _ ->
println("Token refreshed")
}
}
// this should happen after the token request is completed since it would fail again otherwise
return response?.request()!!
.newBuilder()
.header("Authorization", "Bearer " + authStateManager.current.accessToken)
.build()
}
}
You are making an asyncronous call inside a synchronous method. That won't work. You will have to put your code that you want to be executed after token request has been completed inside the rx chain.
class OAuthAuthenticator(
private val authStateManager: AuthStateManager,
private val authRepository: AuthRepository) : Authenticator {
override fun authenticate(route: Route?, response: Response?): Single<Request> {
// handle invalid token
if (response != null && response.code() == 401) {
return Single.error(InvalidAccessTokenException("Invalid access token"))
}
return Single.fromCallable { authRepository.refreshAccessToken() }
.subscribeOn(Schedulers.computation())
.map {
return#map response?.request()!!
.newBuilder()
.header("Authorization", "Bearer " +
authStateManager.current.accessToken)
.build()
}
}
}
And then you can call it like this:
val authenticator = OAuthAuthenticator(authStateManager, authRepository)
val disposable = authenticator.authenticate(route, response)
.subscribe({ _ ->
println("Token refreshed")
}, { error ->
if (error is InvalidAccessTokenException) {
println("Invalid access token")
} else {
println("Could not refresh token. Signing out...")
authStateManager.signOut()
}
})
You can use blockingGet() to make the Single synchronous. But that's not advised. But since you create the Single locally with
Single.fromCallable { authRepository.refreshAccessToken() }
you could just call the appropriate method directly without wrapping it with Rx.
if (response != null && response.code() == 401) {
println("Invalid access token")
try {
authRepository.refreshAccessToken()
println("Token refreshed")
} catch(error: Throwable) {
println("Could not refresh token. Signing out...")
authStateManager.signOut()
return null
}
}