How to properly handle text/plain responses with retrofit? - android

I have a webservice call which response is plain text and no json. In the callback below, failure() always gets called even with successful responses because retrofit tries to parse the response as json.
new Callback<String>() {
#Override
public void success(String s, Response response) {
...
}
#Override
public void failure(RetrofitError error) {
...
}
});
The following error occurs:
retrofit.RetrofitError: com.google.gson.JsonSyntaxException: com.google.gson.stream.MalformedJsonException: Use JsonReader.setLenient(true) to accept malformed JSON at line 1 column 16 path $
How can I tell retrofit, that the response is plain text and should not be treated as json?

You are using GsonConverter for deserializing and Gson fails to validate the response as Json format. thats why you are getting MalformedJsonException. You need to supply something like this StringConverter in your RestAdapter

Related

Get json from request body by gson

How I can get json request body?
App.getApi().method(data).enqueue(new Callback<Obj>() {
#Override
public void onResponse(Call<Obj> call, Response<Obj> response){
// print here
}
...
};
If you are trying to print request / response while using retrofit you can enable logging check this article for same.

Unable to get json string as response in Retrofit 2.0 android

This is not a duplicate question.
I am using Retrofit 2.0 and json for network tasks.
Also I am not using GSON to parse json instead I am using simple JsonObject and JsonArray to get model objects from json string.
Firstly guide me which retrofit converter must be used for above scenario.
Secondly, I am not able to get json string as response string.
I tried two approaches -
Approach 1 - I used Call< Void >. In this case the response.body() returns null though status code is 200.
Approach 2 - I used Call< ResponseBody >. In this case call.enqueue methods call 'on failure method' instead of 'onSuccess' and also the response body is null.
The status code is 200 in this case also.
Please suggest how to get the json string as response from retrofit 2.0.
you need to use JsonObject instead of Void or ResponseBody. Your code should be
Call<JsonObject> getCall = request.getDataCall();
getCall.enqueue(new Callback<JsonObject>() {
#Override
public void onResponse(Call<JsonObject> call, Response<JsonObject> response) {
}
#Override
public void onFailure(Call<JsonObject> call, Throwable t) {
}
});
Note : make sure you are using com.google.gson.JsonObject

rx java retrofit 2 error handling

I'm using retrofit 2 along with rx java
Situation:
the app sends some request, then i get the response in json-format that is automatically converted to User dto, then in onNext method of rx java i receive the list of Users. What if i get some message from server like this: {"error":"can't get the list of users"}
how to handle this situation with retrofit 2 and rx?
Subscription subscriptionBranches = model.getRepoBranches(owner, name)
.map(branchesMapper)
.subscribe(new Observer<List<Branch>>() {
#Override
public void onCompleted() {
;
}
#Override
public void onError(Throwable e) {
if (e instanceof retrofit.HttpException) {
HttpException exception = (HttpException) e;
}
showError(e);
}
#Override
public void onNext(List<Branch> list) {
branchList = list;
view.showBranches(list);
}
});
addSubscription(subscriptionBranches);
.....
#Override
public Observable<List<RepositoryDTO>> getRepoList(String name) {
return apiInterface
.getRepositories(name)
.compose(applySchedulers());
}
Depending on the server response you might or might not get into your onError function. If the server returns a non-2XX http status code you'll get into the onError method. If on the other hand you get a 2XX http status code you'll enter onNext.
I'm assuming you can deal with the onNext bit and I'll explain how you can do it in the onError. It's important to realise that there are many ways of doing this and this is just an example that uses okhttp 3 and retrofit 2 beta4.
So retrofit2 says that every non-2XX http responses are HttpExceptions when using rxjava. This you already have it there in your code:
if (e instanceof retrofit.HttpException) {
HttpException exception = (HttpException) e;
}
Now what you want to do is get the body of the response. This you can achieve by calling Response response = exception.response() in the HttpException you have there. With the response, getting the error body is quite straight forward. You just call response.errorBody(). You can then convert the body to a java object or just access it as a string.
Since you have a json error body as an example, here's how you can convert the response body to a java object:
new GsonConverterFactory().responseBodyConverter(type,
new Annotation[0]).convert(response.errorBody());
where type is the class of the java object that represents the error.
So putting it all together, on your onError method you could write something like:
if (e instanceof retrofit.HttpException) {
HttpException exception = (HttpException) e;
Response response = exception.response();
Converter<ResponseBody, MyError> converter = new GsonConverterFactory()
.responseBodyConverter(MyError.class, Annotation[0]);
MyError error = converter.convert(response.errorBody());
}
MyError is a model that represents the error json you have in your question.
I believe in the case you mentioned you will just enter into your onError handling, because retrofit will fail to deserialize your response, as it's not formatted as a List. You could potentially handle your case through that based off of the exception type.
If you can't alter the api to return consistent response types, you will have to look into using TypedInput, and possibly a converter.
Additionally, while it may not be completely relevant/overkill to the situation at hand, TypeAdapters bear mentioning. They'll let you determine how retrofit deserializes gson on a per class basis.
Gson gson = new GsonBuilder()
.registerTypeAdapter(MyClass.class, new MyAdapter())
.create();
RestAdapter adapter = new RestAdapter.Builder()
.setConverter(new GsonConverter(gson))
.build();
I had the same situation and the way I could get the json from the server when an error occurs was something like this:
retrofit2.Response<TokenRefresh> r = call.execute();
String errorMessage = "";
try {
errorMessage = r.errorBody().string();
} catch (IOException e) {
e.printStackTrace();
}
Timber.d("errorMessage: " + errorMessage);

Retrofit: Handling error JSON response

My web server returns JSON in the following form if successful (200 status code):
{"success":true,"data":{"user_id":"20","username":"Bob"}}
But if something went wrong (for example, if the username and password entered was too short in a form), it returns JSON in the following form (400 status code):
{"success":true,"errors":{"username":["Username too short"],"password":["Incorrect password","Password too short"]}}
How do I handle these two different responses in Retrofit? This is my code so far:
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint(URL)
.build();
ApiEndpointInterface api = restAdapter.create(ApiEndpointInterface.class);
api.getToken('1', new Callback<DefaultResponse>() {
#Override
public void success(DefaultResponse json, Response response) {
//
}
#Override
public void failure(RetrofitError response) {
//
}
});
How do I handle these two differently structured JSON responses?
You need to do two things.
First, do not return a 400 from the server. as others have stated you actually have a successful response from the server, so you just need to parse the error on your end.
your server can return a flag indicating login success or failure.
After that, you can simply add the error model to your DefaultResponse model. like so
public class DefaultResponse {
public boolean success;
public DataModel data;
public ErrorModel errors;
public static class DataModel {
private String user_id;
private String username;
}
public static class ErrorModel { ... }
}
You will still need to handle instances where your server responds with statuses other than OK, but typically error responses from a server are simple plain text, like "bad request".. You could of course insert json if you wish which you could extract from RetrofitError.getMessage(), but GSON wouldn't parse that for you automatically. I hope this helps!
You should realize that actually both responses you posted are success from the network request perspective.
The failure status is for the request failure (Network connection error, 404, etc.) and not failure you return after trying to login the user in your backend.
You'll need to parse the result in success method and decide weather the internal result is success from your app logic perspective.
You should offcourse also handle failure().
if you get the JSON from your web service, which mean it's success.
so, in your success method, grab the JSON and handle it
for example, from your JSON format
#Override
public void success(DefaultResponse json, Response response) {
try {
JSONObject json = new JSONObject(json);
JSONObject data = json.get("data"); // if no "data" element, it will throw JSONException
} catch (JSONException e) {
e.printStackTrace();
Toast.makeText(getApplicationContext(),"username to short",Toast.LENGTH_SHORT).show();
}
}
or you can use GSON to handle your JSON data

Android Retrofit Expected BEGIN_OBJECT but was STRING at line 1 column 1 path $

I am using okhttp Retrofit in my Android App to make network requests. On one of the requests I get this error:
com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 1 path $
I see a 201 response in the logs but Retrofit throws this error. Below is my code.
signup(signupParams, new Callback<Member>() {
#Override
public void success(Member member, Response response) {
if (member != null) {
UserAccount userAccount = new UserAccount(member);
userAccount.save();
}
#Override
public void failure(RetrofitError re) {
BusProvider.post(new SignupFailedEvent(re, email));
}
});
signupParams value is --
{"emailAddress":"test#gmail.com","password":"tester123","userSource":"APH"}
I have tested this json with jsonLint and it is a valid json. And this is my Member Class which should be the response ideally.
public class Member {
public String emailAddress;
public String token;
public long id;
public String firstName;
public String lastName;
}
Example of the response should be something like this:
{
"emailAddress": "test#gmail.com",
"id": 1437811,
"token": "sdhshdghsdhhsdbcjhbsjdhc",
"firstName": "John",
"lastName": "Smith"
}
if you sure the postman work, and the model same as the JSON parameter,
maybe you use the "accept-encoding: gzip" or like that in your request header.
retrofit doesn't work with gzip, remove that from the header.
If the source code that you posted for the Member class is accurate, then you are not getting the response JSON that you think you are.
The error message means that the JSON parser found a String where it expected a complex object.
Since you don't have any complex objects in the Member class, the result probably just isn't valid JSON (it doesn't start with an opening curly bracket).
Try either turning on verbose logging in Retrofit as suggested in one of the comments, or posting the same data to the API using a tool like "Postman", and see what the result actually is.
This is cause .your respons is not json formated . it may include with string or expected } . to identify this .you have to check with postman and change view type in body as HTML . there you can see full response and you can validate with *https://jsonlint.com/ . else in the case of Dynamic json . you can use JsonElement as response.

Categories

Resources