I'm trying to upgrade to Retrofit 2.0 and add RxJava in my android project. I'm making an api call and want to retrieve the error code in case of an error response from the server.
Observable<MyResponseObject> apiCall(#Body body);
And in the RxJava call:
myRetrofitObject.apiCall(body).subscribe(new Subscriber<MyResponseObject>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(MyResponseObject myResponseObject) {
//On response from server
}
});
In Retrofit 1.9, the RetrofitError still existed and we could get the status by doing:
error.getResponse().getStatus()
How do you do this with Retrofit 2.0 using RxJava?
Instead of declaring the API call like you did:
Observable<MyResponseObject> apiCall(#Body body);
You can also declare it like this:
Observable<Response<MyResponseObject>> apiCall(#Body body);
You will then have a Subscriber like the following:
new Subscriber<Response<StartupResponse>>() {
#Override
public void onCompleted() {}
#Override
public void onError(Throwable e) {
Timber.e(e, "onError: %", e.toString());
// network errors, e. g. UnknownHostException, will end up here
}
#Override
public void onNext(Response<StartupResponse> startupResponseResponse) {
Timber.d("onNext: %s", startupResponseResponse.code());
// HTTP errors, e. g. 404, will end up here!
}
}
So, server responses with an error code will also be delivered to onNext and you can get the code by calling reponse.code().
http://square.github.io/retrofit/2.x/retrofit/retrofit/Response.html
EDIT: OK, I finally got around to looking into what e-nouri said in their comment, namely that only 2xx codes will to to onNext. Turns out we are both right:
If the call is declared like this:
Observable<Response<MyResponseObject>> apiCall(#Body body);
or even this
Observable<Response<ResponseBody>> apiCall(#Body body);
all responses will end up in onNext, regardless of their error code. This is possible because everything is wrapped in a Response object by Retrofit.
If, on the other hand, the call is declared like this:
Observable<MyResponseObject> apiCall(#Body body);
or this
Observable<ResponseBody> apiCall(#Body body);
indeed only the 2xx responses will go to onNext. Everything else will be wrapped in an HttpException and sent to onError. Which also makes sense, because without the Response wrapper, what should be emitted to onNext? Given that the request was not successful the only sensible thing to emit would be null...
Inside onError method put this to get the code
((HttpException) e).code()
You should note that as of Retrofit2 all responses with code 2xx will be called from onNext() callback and the rest of HTTP codes like 4xx, 5xx will be called on the onError() callback, using Kotlin I've came up with something like this in the onError() :
mViewReference?.get()?.onMediaFetchFinished(downloadArg)
if (it is HttpException) {
val errorCode = it.code()
mViewReference?.get()?.onMediaFetchFailed(downloadArg,when(errorCode){
HttpURLConnection.HTTP_NOT_FOUND -> R.string.check_is_private
else -> ErrorHandler.parseError(it)
})
} else {
mViewReference?.get()?.onMediaFetchFailed(downloadArg, ErrorHandler.parseError(it))
}
Related
So I wanted to implement the example of response from the API like in this video
droidcon NYC 2017 - Advanced Networking with RxJava + Retrofit
And this is my code:
Presenter.java
compositeDisposable.add(RetrofitClient.createService(GetApi.class)
.getResponseFromServer(token)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<ResponseFromServer>() {
#Override
public void accept(ResponseFromServer responseFromServer) throws Exception {
mView.setResponseObject(responseFromServer);
}
}, new Consumer<Throwable>() {
#Override
public void accept(Throwable throwable) throws Exception {
throwable.printStackTrace();
if (throwable instanceof HttpException) {
int responseCode = ((HttpException) throwable).code();
}
}
}));
So here, when I get some 4xx error response from the server, I can go to Throwable and get the response code, and if response is okay I can get my object, and everything is cool.
However, in the video example above, the guy suggest that I wrap my ResponseFromServer with Response like this:
Single<Response<ResponseFromServer>> getResponseFromServer(#Header("X-Authorize") String token); so I can access response codes also, but in that case, my Throwable never gets called, so I can access to the codes only in the first accept method, but in the video he catch the errors in the Throwable section. So, I cant figure it out what I'm doing wrong? Maybe I'm using the wrong Observer?
I think i figured it out, if we wrap our response object with Observable<Response<Object>> all of the response code will be caught in the regular accept method, so we kinda need to extract the codes manually and do the checks. However, if we keep the Observable<Object>, errorCode < 200 || errorCode > 400 will be caught in the onError method.
When Response from Server is code < 200 || code >= 300 in those cases onError() will be invoked. and other cases onNext() will invoke.
Also, If your code from onNext() throws any exception, that will be catch in onError()
I am implementation for Retrofit on api call using images-upload base64Encode string. it is sending data perfect but Retrofit return response Internal Server Error 500 and i am sending request type is Body custom class. Plz help me what i do.
#Headers("Accept:application/json")
#POST(RestClient.postRegister)
Call<RegisterResp> getRegisterResponse(#Body RequestRegisterVo requestRegisterVo);
Call<RegisterResp> call = MyApplication.getRestClient().getApplicationServices().getRegisterResponse(requestRegisterVo);
call.enqueue(new Callback<RegisterResp>() {
#Override
public void onResponse(Call<RegisterResp> call, Response<RegisterResp> response) {
if (Other.isValidResp(response)) {
// success Log.i(TAG,"Register successfully");
} else {
hideDialog();
}
}
#Override
public void onFailure(Call<RegisterResp> call, Throwable t) {
hideDialog();
showToast(t.getMessage());
}
});
The same issue I had to face it, I got a solution in my case-
there is parameter issue, I was sending parameters in String and at the backend, they required Integer parameters.
You also checkout may be there is the issue with parameters or second reason is the URL issue so check it URL also.
I have a request call which can return 200 if an user has been subscribed to an event, 204 if the user is not subscribed or 404 if the event no longer exist.
I'm using retrofit 2 and Observables for calling the server.
how can I check If I have a code 200 or 204?
If I got a 404 I know it is an error and I easily deal with it, but I the response is different I don't know how to get the actual code.
mApiEvents.isSubscribed(idEvent, uniqueId )
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.compose(getLifecycleProvider())
.flatMap( data-> {
return ?? //How do I check if I got 200 or 204?
}
.subscribe(new LifecycleObserver<Boolean>(){
#Override
public void onNext(Boolean aBoolean) {
}
#Override
public void onError(Throwable e) {
//404 or another error
}
}
)
I finally found it, at the end the solution is quite straight forward.
we have to use a Retrofit2 Response class for wrapping our data.
https://square.github.io/retrofit/2.x/retrofit/retrofit2/Response.html
#POST("some/endpoint")
Observable<Response<Data>> getData(...)
then we can manipulate it with a flatMap and check the Code
getData()
....
.flatMap( response -> {
if(response.code() == 200)
//do something
else
//do something else
}
Not really what you asked, but you can check isEmpty(), because as the body of a 204 is empty, retrofit won't emit any item and you'll receive an onComplete without any onNext.
when I user the OkHttp Library with a asynchronous way like this:
call.enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
#Override
public void onResponse(Call call, Response response) throws IOException {
}
});
In the onFailure method, how I get the response status code to distinguish different errors. For example, Network error or Server error ?
As far as I remember, onFailure gets triggered when you get no response. So, if your receive an error, onResponse will be called. You can do something like this in onResponse:
#Override
public void onResponse(Call call, Response response) throws IOException {
switch(response.code()){
//your desired catched codes here.
}
}
And official doc for onResponse method:
Note that transport-layer success (receiving a HTTP response code, headers and body) does not necessarily indicate application-layer success: response may still indicate an unhappy HTTP response code like 404 or 500.
https://github.com/square/okhttp/issues/1769
According to the link, above, onFailure() is called if and only if there were problems with the client.
If the request was successfully delivered but there was a server problem you can check response.isSuccessful(). If it returns false, check response.code() and handle the error.
You check that using response.code()
You can also make use of response.message() to get more info.
I am making call using the following callback method:
Callback<PeopleList> callback = new Callback<PeopleList>() {
#Override
public void onResponse(Response<PeopleList> response) {
Toast.makeText(LoginActivity.this,getString(R.string.login_failed), Toast.LENGTH_SHORT).show();
}
#Override
public void onFailure(Throwable t) {
Toast.makeText(LoginActivity.this,getString(R.string.login_failed), Toast.LENGTH_SHORT).show();
}
};
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://example.com/")
.addConverterFactory(GsonConverterFactory.create())
.build();
retrofit.create(MyService.class).getPeopleData().enqueue(callback);
To the following interface:
public interface MyService {
#Headers("Accept: application/json")
#GET("/data/people/")
Call<PeopleList> getPeopleData();
}
This callback works just fine on successful requests. On unsuccessful ones however it does not give me the opportunity to investigate further as the onFailure method does not allow me to retrieve the http error code that came with the response.
On investigating further I found that according to several stackoverflow threads, the onResponse method should be called even on unsuccessful requests. This however seems to be at odds not only with my personal experience but also with the documentation of the Callback interface, which states that:
Communicates responses from a server or offline requests. One and only one method will be
invoked in response to a given request.
So the question is, how do I get the HTTP error code from a failed response if the onResponse method isn't called?
I think that the onResponse method gets called even if there is a response with an Error so something like this might work(sorry if I did something wrong first attempt to answer anybody :)
#Override
public void onResponse(Response<PeopleList> response) {
if(response.isSuccess()){ //good http request, do something with response.body()....
Toast.makeText(LoginActivity.this,getString(R.string.login_failed), Toast.LENGTH_SHORT).show();
} else { //bad http response do something with error message
try {
Toast.makeText(LoginActivity.this,response.errorBody().string().toString(), Toast.LENGTH_SHORT).show();
} catch (IOException e){
//IOException caught from response.errorBody().string() method
}
}
}
onResponse() will be always called, for failed requests .body() is null. response.isSuccess() is used to quickly distinguish requests with http codes between 200 and 300.
If you want to access http codes you can do the following:
int htppResultCode = response.raw().code();
It accesses the raw Response object which holds information about general outcome of the request.