Http GET request not happened after OnError in retrofit - android

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.

Related

Error handling on Zip operator RxJava android

What I want
I wanted to call a
1 webservice that uploads photo to the server and returns the uploaded link.
2 webservice to save the returned link by 1st webservice.
I wanted to combine two observables and get results as same time
My doubt
What happens if my 1st webservice gets fired successfully and 2nd has encountered an error (eg: Network error, Server error etc)
How can I detect that ? and only retry the 2nd webservice
What I can't do
I can't retry both webservice if 2nd one fails, because I will end up in sending duplicate files for the 1st webservice.
My code
// Upload file (photos,documents etc ):
#POST("some link")
#FormUrlEncoded
Observable<UploadFile> uploadFile(#FieldMap HashMap<String, Object> fields);
// Save link (photos,documents etc ):
#POST("some link")
#FormUrlEncoded
Observable<SaveLink> saveLink(#FieldMap HashMap<String, Object> fields);
// Upload file
Observable<UploadFile> observable = retrofitService.uploadFile(map);
subscriptionUploadFile = observable.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(new Subscriber<UploadFile>() {
#Override
public void onCompleted() {
CommonFunction.printDebug(TAG, "completed");
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(UploadFile model) {
}
});
// Save link
Observable<SaveLink> observable = retrofitService.saveLink(map);
subscriptionSaveLink = observable.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(new Subscriber<SaveLink>() {
#Override
public void onCompleted() {
CommonFunction.printDebug(TAG, "completed");
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(SaveLink model) {
}
});
Dependent continuation is typically done via flatMap where you can apply retry to the second Observable:
uploadFile(map)
.subscribeOn(Schedulers.io())
.flatMap(file -> {
map.put("URL", file.getURL());
return saveLink(map).retry(10);
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(...);

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.

Best ERROR RESPONSE I should send from Server to Android?

I am developing a communication's system between an Android App and a Server.
I am using Retrofit API for the Android's communication with the Server.
When I do a GET (from Android side) to get info from the Server, I use a CallBackTask method like this:
public void testGet()
{
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint(UserApi.SERVER)
.build();
final UserApi svc = restAdapter.create(UserApi.class);
if (svc != null) {
CallableTask.invoke(new Callable<Test>() {
#Override
public Test call() throws Exception {
Test g = svc.getTest();
System.out.println("getVdd() = "+g.getVdd()+"+ getResp() = "+g.getResp());
return g;
}
}, new TaskCallback<Test>() {
#Override
public void success(Test result) {
Intent i = new Intent(getApplicationContext(),SplashRapidoActivity.class);
startActivity(i);
}
#Override
public void error(Exception e) {
Toast.makeText(
getApplicationContext(),
"Unable to connect, please try again.",
Toast.LENGTH_SHORT).show();
}
});
}
}
Where the Test.class is a POJO class with variables and his getters and setters:
public class Test {
String vdd;
String resp;
public Test()
{
}
public void setVdd(String vdd) {
this.vdd = vdd;
}
public String getVdd() {
return vdd;
}
public void setResp(String resp)
{}
public String getResp()
{
return resp;
}
}
So, my question is, which is the best ERROR RESPONSE i could send from the server if there aren't valid values for the Test.class in the server?
Actually there is no "the best error response". It depends on your requirements. But there is widely used architecture called REST and Retrofit was designed in according with REST interaction. REST basically is just a set of rules which clients and servers understand without any documentation.
So if you want to retrieve(GET) some object/data from server by REST you can either receive it with status 200 or receive status 404 with appropriate description of error(or without it) inside body.
Here some more to read.
The best error response is one that make sense to you as a developer. I wouldn't mess with the default HTTP status/error codes. Instead I would send a specific response from the server. For example, keep the HTTP response code at 200 but in the data you send to the app set it to "ERROR: no values." or whatever you prefer. Then, in your Android app, check the response to see if it contains values or an error. Something like
if(resp.startsWith("ERROR:")){
// Do error handling //
} else {
// Normal code //
}

Retrofit returns null error message with status 400

I have created an app to use REST api developed by me. Android client is written with retrofit library.
there I have created an interface as ObjectIFaceAsync
public interface ObjectIFaceAsync {
#POST("/")
public void addArea(
#Body Area area,
Callback<JsonElement> data
);
}
then I implemented it in a button click
Area pojoArea = new Area();
pojoArea.setArea(area.getText().toString());
pojoArea.setDistrict(district.getText().toString());
pojoArea.setProvince(province.getText().toString());
pojoArea.setAreaType(areaType.getSelectedItem().toString());
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint(URLs.ENDPOINT + "/restaddarea")
.build();
ObjectIFaceAsync objectIFaceAsync = restAdapter.create(ObjectIFaceAsync.class);
try {
objectIFaceAsync.addArea(
pojoArea, new Callback<JsonElement>() {
#Override
public void success(JsonElement jsonElement,retrofit.client.Response response) {
if (jsonElement.getAsJsonObject()
.get("status").getAsString().equals("s")) {
//show a message
} else {
//show another message
}
}
#Override
public void failure(RetrofitError retrofitError) {
//show a failure message
}
}
);
} catch (Exception e) {
}
I have used the same endpoint to and sent a get request that is not having a JSON #Body. It works fine. Here I always get an error. means it always runs the failure method. I got displayed the
retrofitError.getMessage();
and
retrofitError.getResponse().getStatuse();
They show null and 400 respectively.
Can someone tell what I have to do to get this corrected. or what I have done wrong here.
thanks.
Use
retrofitError.getBody()
to get String or
retrofitError.getBodyAs(MyError.class)
if you want to map the error json to object

Handling error response in Retrofit 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);
}
}

Categories

Resources