Handling error response in Retrofit android - android

I am using Retrofit as my networking library. Everything is running smoothly.
I just have a new requirement, Server send me a response in Json, in case of failure.
I want to grab that whole response, right now RetrofitError in failure callback, doesn't give my server response in any of the RetrofitError attributes.
Any Kind of help would be appreciated. Thanks.

I guess what you are trying to get are the fields from the json response returned from your api during a failure. I think the process for a failure is pretty similar to the process for a success. First, your response object needs to have a field to map to the json error field, or any field that you want to extract from the response (success or failure). In the success case you get the converted response back directly, but in the case of a failure, you need to call getResponse() on the returned RetofitError object to get the converted response, and then extract the returned data.
Here is an example.
I have a rails backend that responds with a token when successful or a list of errors if not successful. Here is the rails controller method if that helps, but the bottom line is there is a json entry errors that I want to examine if the create fails.
def create
#user = User.new(user_params)
if #user.save
sign_in_api(#user)
render json: { token: #issued_remember_token }, status: 201
else
render json: { errors: #user.errors.full_messages } , status: 406
end
end
Here is my retrofit call
#POST(API_URL + "/signup")
void signup(#Body UserRequestParams requestParams,
Callback<SignupResponse> tokenParms);
and here is my SignupResponse object
public class SignupResponse extends BaseResponse {
private String token;
public String getToken() {
return token;
}
}
public class BaseResponse {
private List<String> errors = new ArrayList<String>();
private Boolean mSuccessful = false;
public Response getRawResponse() {
return rawResponse;
}
public void setRawResponse(Response rawResponse) {
this.rawResponse = rawResponse;
}
private Response rawResponse;
public BaseResponse() {
super();
}
public List<String> getErrors() {
return errors;
}
public void setSuccessful(Boolean successful) {
mSuccessful = successful;
}
public Boolean isSuccessful() {
return mSuccessful;
}
}
The result is that retrofit fills in the errors field as part of the callback process, same as if it were a success. Now I can just check to see if the response was successful or not and call the appropriate methods on the response object. errors will be null if it were successful but valid if not.
For completeness here are the other two methods/classes involved in my example.
#Subscribe
public void onSignup(SignupRequest event) {
System.out.println("inside api repo - making signup request");
mRailsApi.signup(event, new RailsApiCallback<SignupResponse>(mBus, new SignupResponse()));
}
public class RailsApiCallback<T extends BaseResponse> implements Callback<T> {
private Bus mBus;
private T mResponse;
public RailsApiCallback(Bus bus, T response) {
super();
mBus = bus;
mResponse = response;
}
#Override
public void failure(RetrofitError retrofitError) {
System.out.println(retrofitError.toString());
T response = retrofitError != null && retrofitError.getBody() != null ? (T) retrofitError.getBody() : mResponse ;
response.setRawResponse(retrofitError.getResponse());
response.setSuccessful(false);
System.out.println("posting response to bus");
mBus.post(response);
}
#Override
public void success(T convertedResponse, Response rawResponse) {
System.out.println(rawResponse.getBody());
T response = convertedResponse != null ? convertedResponse : mResponse ;
response.setSuccessful(true);
response.setRawResponse(rawResponse);
mBus.post(response);
}
}

Related

Http GET request not happened after OnError in retrofit

I am new to Retrofit and using com.squareup.retrofit:retrofit:1.9.0. I am having form filling in android application which can enter address and when clicking on save button
it will make http GET request using flatmap operator in retrofit, and it will give a error responses if user enter the wrong pin code saying that "invalid pin code"
Once after I receive the error response, It will go OnError() callback and I am allowing the user to edit the pin code again and clicking on save button again , my retrofit API is not making a http request. How to resolve this issue?
ApiClient.createAddress(contact).subscribe(mObserver );
mObserver = new Subscriber<Contact>() {
#Override public void onCompleted() {}
#Override public void onError(Throwable e) {
onNetworkError(e);
}
#Override public void onNext(Contact contact) {
saveContact(contact);
}
};
public static Observable<Contact> createAddress(Contact mAddress)
{
final String name = mAddress.getName();
final String company = mAddress.getCompany();
final String country = mAddress.getCountry();
final Observable<Contact> createAddressObservable =
sService.createAddress(name,company,country)
.flatMap(new Func1<AddressResponse, Observable<? extends Contact>>() {
#Override
public Observable<? extends Contact> call(
AddressResponse addressCreateResponse) {
if (addressCreateResponse.isSuccess()) {
return Observable.just(addressCreateResponse.getAddress());
}
else {
final String errorMessage = addressCreateResponse.getMessage();
return null;
}
});
return createAddressObservable.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
In the above code when I enter wrong pin code it goes to the call back onError , then I allow user to edit the pincode and click on save button , it will call ApiClient.createAddress(contact, null).subscribe(mObserver)
which is not making http get request again. Please help on this.

Can't figure out what to check for in OkHttp's onResponse method

I know there are lots of tutorials for OkHttp, but basically all of them do something different in the onResponse method and most don't bother to explain why. Some check for if (response.isSuccessful), some surround it with try/catch, some don't do any of this at all.
This is my example project. What is the proper way to handle the onResponse method?
public class MainActivity extends AppCompatActivity {
private TextView textViewResult;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textViewResult = findViewById(R.id.text_view_result);
OkHttpClient client = new OkHttpClient();
String url = "https://reqres.in/api/users?page=2";
Request request = new Request.Builder()
.url(url)
.build();
client.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
#Override
public void onResponse(Call call, Response response) throws IOException {
final String myResponse = response.body().string();
MainActivity.this.runOnUiThread(new Runnable() {
#Override
public void run() {
textViewResult.setText(myResponse);
}
});
}
});
}
}
Update
onResponse of okhttp runs on background thread. So, yes, it's necessary to do MainActivity.this.runOnUiThread(...).
Original answer
onResponse callback already runs on ui thread AFAIK. So, you don't actually need to do MainActivity.this.runOnUiThread(...).
And everyone's onResponse is different because everyone has different needs. Use try/catch if your operations in onResponse might give error and you don't want it to crash.
For some network requests you may need to check if response is successful for other you may not. It all depends on use cases. Do what works for you best.
I'd suggest you surround your code in onResponse in a try/catch block because the user might close the app before the network request is finished. And when you set the textview text in onResponse it will crash because the activity and that textview doesn't exist anymore.
Adding to the answer from rafid. There are basically three cases you want to check.
response.isSuccessful() => status code between 200 and 300
response.code() => to manually check after response is not successful
onFailure() => Network error or parsing error etc.
Ideally your callback would handle those cases something like
client.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
// network error or parsing error
}
#Override
public void onResponse(Call call, Response response) {
if (response.isSuccessful()) {
// do stuff all good
} else {
// handle different cases for different status codes or dump them all here
}
}
});
The reason you need a try-catch is because OkHttp is trying to parse the response. This is the case for example for response.errorBody().string();. Another case would be if your Callback<T> has actually a type parameter. Again OkHttp will try to parse the response to that type. If it fails it will result in a callback onto the onFailure method.
I think you need to make sure you know the legal response from the request, like an json or File. if it's just a json, use like below:
#Override
public void onResponse(Call call, Response response) throws IOException {
final String myResponse = response.body().string();
if (response.isSuccessful() && !TextUtils.isEmpty(myResponse)) {
MainActivity.this.runOnUiThread(new Runnable() {
#Override
public void run() {
textViewResult.setText(myResponse);
}
});
}
}
Edit: To be more clear.
Callback is running in mainThread so there is no need to call runOnUiThread.
If response is not successful you can try to parse error body as below. If response is successful you can parse with Gson as i show.
String message = "";
if (response.errorBody() != null) {
try {
message = response.errorBody().string();
} catch (IOException ignored) {
Log.e("OkHttp IOException", "error while parsing response");
}
Log.d("Error Message", message);
}
I recommend you to use Gson Library. First you should create your pojo class. You can use http://www.jsonschema2pojo.org/ to create your pojo class. Then you can parse body like below
Gson gson = new Gson();
MyPojo myPojo = gson.fromJson(response.body().charStream(), MyPojo.class);

How To Intercept a response that requires user action before replaying desired response to user

I have a rather unique problem I have to solve. I am working on an app that uses a homegrown networking layer with the legacy okhttp client in Android and we would like to use Retrofit and OKHttp3 instead.
The problem we have is that our services may return a response on a user request that contains one or more actions a user must take before the server will allow the original request to execute and thus return the desired response.
It goes kinda like this. User tries to log in, but they are a new user, instead services responds with a series of information collection actions. If the user cancels they do not log in, but if the user executes the series of actions than the original login request is replayed and the user logs in.
I am taking a close look at the Interceptor interface provided by OkHttp3 as the means of achieving this. Now I know I can intercept any response and modify it or do other things, but the interface method must return a value of type Response in the end. However I need the ability to "hold/prevent" that return from taking place before the user either A: Cancels the actions required by services, or B: Successfully completes those actions.
This is what I have so far.
private static class ServerActionInterceptor implements Interceptor {
#Override
public Response intercept(Chain chain) throws IOException {
Response response = chain.proceed(chain.request());
Gson gson = new Gson();
ServicesResponse serviceResponse = null;
if (response.body() != null) {
serviceResponse = gson.fromJson(response.body().string(), ServicesResponse.class);
}
if (serviceResponse != null) {
if (serviceResponse.getServerActions() != null && serviceResponse.getServerActions().size() > 0) {
//handle server actions
handleServerActions(serviceResponse);//this must complete before we can return chain.proceed IF there are actions the user must take.
}
}
return chain.proceed(chain.request());
}
//this method may end up returning a value
private void handleServerActions(ServicesResponse serviceResponse) {
//process server actions
}
}
Its not a whole lot, the majority will happen in handleServerActions and that honestly is pretty particular to the app. But the core of the issue, is what method should I employ so that the return will happen if there are no serverActions for the user request OR there are and the user has completed the actions and thus the true response is ready to be delivered to the client?
Thanks.
Due to a comment, I thought I would share my solution.
It involved a custom abstract class that implements Callback<T>.
This is just a general example and the actual implementation would be up to the business rules for your project.
public abstract class CustomCallback<T extends CustomResponse> implements Callback<T> {
private CustomExcetption encounteredException;
private Class<T> clazz;
public CustomCallback(Class<T> clazz) {
this.clazz = clazz;
}
#Override
public void onResponse(#NonNull Call<T> call, #NonNull Response<T> response) {
if (response.body() != null) {
T body = response.body();
i
if (body != null && body.hasCustomActions() != null && body.hasCustomActions().size() > 0) {
handleCustomAction(body);
} else {
onSuccessfulResponse(body);
}
} else if (response.errorBody() != null) {
onFailureResponse(exception);
}
}
#Override
public void onFailure(#NonNull Call<T> call, #NonNull Throwable throwable) {
onNetworkFailure(call, exception);
}
public abstract void onSuccessfulResponse(#Nullable T response);
public abstract void onFailureResponse(CustomException exception);
public abstract void onNetworkFailure(Call<T> call, CustomException exception);
public void handleCustomAction(#NonNull final T body, final String path) {
//handle custom actions.
if(customActionsHandledSuccessfully) {
onSuccessfulResponse(body);
} else {
//create your exception somehow
CustomException createdException = new CustomException();
onFailureResponse(createdException);
}
}
}
Ussage example:
Keep in ming the reference to SomeResponse.class is something I needed for my implementation and may not be needed in others. It was used for gson serialization purposes as I needed to know the concrete class of the response to serialize it properly.
service.someApi().enqueue(new CustomCallback<SomeResponse>(SomeResponse.class) {
#Override
public void onSuccessfulResponse(#Nullable SomeResponse response) {
}
#Override
public void onFailureResponse(CustomException exception) {
}
#Override
public void onNetworkFailure(Call<SomeResponse> call, CustomException exception) {
}
});

Converting JSON response to POJO with Retrofit using JSend format?

I have to work with an API which using JSend format.
Long story short it is using HTTP status codes which indicates status like:
200 is Success
406 is Unauthorized
Which is good because I can determine from this whether my API request is succeed or not.
BUT:
As JSend format has it's own thing, it has ALSO have a little status indicator at response just like this:
{
status : "success",
data : { "post" : { "id" : 2, "title" : "Another blog post", "body" : "More content" }}
}
So it has a 'status' field which ALSO shows whether the API request is succeed or not.
PROBLEM:
Retrofit made to parse the response to POJO so it assumes that the responses contains ONLY the Model and no indicators for success, just like this for example: (A post Model instance)
{ "id" : 2, "title" : "Another blog post", "body" : "More content" }
My question is:
Is there a solution for this?
Can I pre-parse the status indicators, split the 'data' (Model) part of the response and give it to retrofit for parse only that part?
If not I would have to add a "status" attribute to each of my models which is clearly not a walkable way, I won't do that.
Should I just stick with manual parsing and use ResponseBody instead of my Models at
void onResponse(Call<T> call, Response<T> response); for T type paramter?
Because in that way I can use .string() and convert the string to JSON and after that I can parse my Models manually like writing the parser for them.
I would really like to use Retrofit's feature for automatic parsing because with JSend I just cannot imagine how could be this properly done if anyhow at all.
I cannot change the API it's going to be this way.
Here is my Response class.
public class Response<T> implements Serializable {
private T data;
private String status;
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
}
Here is my api call.
Callback<com.shippn.api.models.Response<T>> callback = new Callback<com.shippn.api.models.Response<T>>() {
#Override
public void onResponse(Call<com.shippn.api.models.Response<T>> call, Response<com.shippn.api.models.Response<T>> response) {
checkStatus(response.body());
}
#Override
public void onFailure(Call<com.shippn.api.models.Response<T>> call, Throwable t) {
fail();
}
};
call.enqueue(callback);
T parameter is type of your data from api. I mean if you want to get data in Post type put Post insted of T. If you want to get ArrayList<Post> put ArrayList<Post>.
If data field is empty or null in your api response, Response.data will be null or empty, it wont throw an exception.
When you get response from api, first check Response.status if it is "success" or not. If it is "success" get data from Response, if it is not take your error actions.
One possible way is to create a wrapper for your models like
public class Example<T> {
String status;
T data;
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
What you can do is you can have the status of the post request without using this.
The problem is that the parameter names are not string and will not be parsed in POJO.But you can get the status of you request like this.
#Override
public void onResponse(Call<Object> call, retrofit2.Response<Object> response) {
alertDialog.dismiss();
if (response.isSuccessful() && response.body() != null) {
//if the response is received here it means it was success
}else if(response.errorBody()!=null){
//if the response is received here it means it was error
}
}
#Override
public void onFailure(Call<Object> call, Throwable t) {
//if the response is received here it means it was failure
t.printStackTrace();
}
});
EDIT
In this way you can have the status with using pojo and you can parse the error simply as in this link
EDIT 1
Moreover for getting the proper error code you can extend your callback class to a class like
public class ErrorValues {
public int code=0;
public String error;
}
Then if the request was completed with a error.Inside
else if(response.errorBody()!=null){
int error=response.body.code == 404 ....
}
Like this you can check which error code was thrown by the request.

Retrofit - Different Response

I am consuming an API in Android using Retrofit. The success response looks different to the error/failure response. How can I therefore achieve this?
I currently have something to this affect:
Call<AuthenticateUserResponse> authenticateUser(String id);
You can extend your Responses with a base Response and check if there's an error or it's succeed. Here's an example base response bean below:
public class BaseResponse {
#SerializedName("ResponseCode")
private int code;
#SerializedName("ResponseMessage")
private String message;
#SerializedName("ResponseText")
private String text;
}
My api returns ResponseCode, ResponseMessage and ResponseText in every responser and i extend my responses from BaseResponse bean and check if there's an error.
You can modify your responses as your api's return schemes.
Edit: Here's your Response for your Api:
public class Error {
#SerializedName("ResponseCode")
private int code;
#SerializedName("ResponseMessage")
private String message;
#SerializedName("ResponseText")
private String text;
}
public class YourWrapperResponse {
#SerializedName("Error")
private Error error;
#SerializedName("AuthenticateUserResponse")
private AuthenticateUserResponse authenticateUserResponse;
}
And your call will be like:
Call<YourWrapperResponse> authenticateUser(String id);
The example above i gave you is an example of handle business errors which you get in your every successful response. Successful means Http Status 200. Also you do not need to return this Error object in your every response. If there's an error you can return in your response.
In Retrofit 2.0+ you need to check if your request is succeed. Here's an example below about it:
Call<User> auth = YourApiProvider.getInstance().getServices().auth(userName, passowrd, grantType);
auth.enqueue(new Callback<User>() {
#Override
public void onResponse(Call<User> call, Response<User> response) {
if (response.isSuccessful()) {
// Here you get a 200 from your server.
}
} else {
// Here you get an authentication error.
// You can get error with response.code();
// You can get your error with response.errorBody();
// or you can get raw response with response.raw()
}
}
#Override
public void onFailure(Call<User> call, Throwable t) {
// Here you get error such as TimeOut etc.
}
});
I hope this'll help you. Good Luck!
Edit: You can also use generics to handle base api responses. Here's my another answer about handling generic api responses.

Categories

Resources