Retrofit2 NULL response in onResponse even when Data is not NULL - android

So, my problem is fairly straightforward.
This is the response I am getting through the API call.
D/OkHttp: {"status":"error","status_code":"500","error_msg":"There was an error trying to send the password reset email."}
<-- END HTTP (220-byte body)
This is my code to handle the calls
Call<PasswordReset> forgotPasswordCall = apiInterface.Reset(email);
forgotPasswordCall.enqueue(new Callback<PasswordReset>() {
#Override
public void onResponse(Call<PasswordReset> call, Response<PasswordReset> response) {
PasswordReset passwordReset = response.body();
try {
if (passwordReset.getStatusCode() != null && passwordReset.getStatusCode().equals("200")) { //line 133
Log.d("Success", "Password Reset Email Sent");
new CustomToast().showToast(getContext(), view, "Password Email sent");
new LoginActivity().replaceFragment("left");
} else {
String test = passwordReset.getStatusCode();
Log.d("Failure", test);
hideDialog();
}
}
catch (Exception e){
e.printStackTrace();
hideDialog();
}
}
#Override
public void onFailure(Call<PasswordReset> call, Throwable t) {
Log.d("Failure", "Password Reset Email Not Sent");
new CustomToast().showToast(getContext(), view, "Email Not Sent");
new LoginActivity().replaceFragment("left");
hideDialog();
}
});
And this is the exception I am catching
W/System.err: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String in.siddhant.anetpays_customer.POJO.PasswordReset.getStatusCode()' on a null object reference
at in.siddhant.anetpays_customer.Login.Fragments.ForgotPassword$1.onResponse(ForgotPassword.java:133)
How can my response be null if I am getting some data ?
PasswordReset.class
public class PasswordReset {
#SerializedName("data")
private String data;
#SerializedName("status_code")
private String statusCode;
#SerializedName("status")
private String status;
public String getData() {
return data;
}
public void setData(String data) {
this.data = data;
}
public String getStatusCode() {
return statusCode;
}
public void setStatusCode(String statusCode) {
this.statusCode = statusCode;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
}
Retrofit Client
public static Retrofit getClient() {
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient client = new OkHttpClient.Builder().addInterceptor(interceptor).build();
retrofit = new Retrofit.Builder()
.baseUrl("http://xsdf/")
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build();
return retrofit;
API_Interface
#FormUrlEncoded
#POST("/ddd/pwdresetrequest")
Call<PasswordReset>Reset(#Field("email")String email);
P.S - The API itself has some problem and always returns "status":"error" but this shouldn't affect the application, right ? Also I am happy to share more code.
Thanks in advance.
Solution
I am posting the solution as suggested as per the accepted answer, Hope it helps someone who comes looking.
forgotPasswordCall.enqueue(new Callback<PasswordReset>() {
#Override
public void onResponse(Call<PasswordReset> call, Response<PasswordReset> response) {
if (!response.isSuccessful()){
Gson gson = new Gson();
PasswordReset passwordReset1 = gson.fromJson(response.errorBody().charStream(), PasswordReset.class);
if (passwordReset1.getStatusCode().equals("500")){
new CustomToast().showToast(getContext(), view, "Password Email not sent");
hideDialog();
}
else {
Thread.dumpStack();
hideDialog();
}
}
else if (response.isSuccessful()) {
Log.d("Success", "Password Reset Email Sent");
new CustomToast().showToast(getContext(), view, "Password Email sent");
new LoginActivity().replaceFragment("left");
}
As for the theory, onResponse method of retrofit2 is called when we get some response and onFailure is called when the process of establishing and receiving a response is not met. I had overlooked this simple fact.
So, if someone does come looking and reading still, I will suggest you to also check your response.body() if its successful or not.
Happy Coding!

From the retrofit's javadoc for Response you can read that body() returns the deserialized response from a successful response. Unfortunately, seems like you have an unsuccessful response, given that you seem to receive a 500.
errorBody() is what you want to use. However, this returns the raw response, so you'll have to deserialize it yourself.
There's a lot of ways you can do this. One might be using gson to deserialize the body:
new Gson().fromJson(response.errorBody().body().string(), YourModel.class);
PS: Just because you end up on onResponse it doesn't mean you have a successful response. However, from your code it seems you already know this and are checking for the http status 200.

Related

retrofit2 rxjava 2 - how can access to body of response when have error

I use Retrofit with Rxjava together for request to server.
the my server return defined json format that Include data , and defined message.
server return http response. it's ok if server return success code(200).
but I want, if server return other code, i manage the body of that response.
for example:
the server return 401, and I want read body of response for show message of the server.
but when server other code, retrofit call the onError method and I can't use body of response.
how can solve this problem?
this is my mehod
'''
private void login(String username , String password){
view.setLoading();
source.loginUser(username, password)
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new SingleObserver<Response<LoginResult>>() {
#Override
public void onSubscribe(Disposable d) {
disposable.add(d);
}
#Override
public void onSuccess(Response<LoginResult> loginResult) {
if (loginResult.isSuccessful()){
}
else
new AlertConfiguration(view.getViewActivity()).showMessage(loginResult.body().getMessage());
}
#Override
public void onError(Throwable e) {
if there is a problem
}
});
'''
and this is my interface method for retrofit
#POST("...")
Single<Response<LoginResult>> loginUser(#Query("username") String username, #Query("password") String password);
According to info here, since you are already using Response<LoginResult> you should be able to cast the Throwable to a HttpException, and then extract the body of the response.
In Kotlin:
if (e is HttpException) {
val errorBody = (e as HttpException).response().errorBody()
}
In Java:
if (e instanceof HttpException) {
HttpException error = (HttpException)e;
String errorBody = error.response().errorBody().string();
}
Complete example using your Java code:
import retrofit2.HttpException
private void login(String username , String password){
view.setLoading();
source.loginUser(username, password)
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new SingleObserver<Response<LoginResult>>() {
#Override
public void onSubscribe(Disposable d) {
disposable.add(d);
}
#Override
public void onSuccess(Response<LoginResult> loginResult) {
if (loginResult.isSuccessful()){
// Handle success
} else {
// Handle login failure
new AlertConfiguration(view.getViewActivity()).showMessage(loginResult.body().getMessage());
}
}
#Override
public void onError(Throwable e) {
// Get the error response:
if (e instanceof HttpException) {
HttpException error = (HttpException)e;
String errorBody = error.response().errorBody().string();
// Then parse the errorBody and extract the values you need
}
}
});
I find solution
in retrofit onSuccess methode when response code is 200, you should get response from body object.
but when isn't 200, you should get response from errorBody;
new Gson().fromJson(serverResultResponse.errorBody().string(), ServerResult.class);

getting null response if email or password is incorrect in retrofit2

i am implementing login web service. i am getting correct response if user email and password is correct. but i am getting null if email or password is incorrect. i want to get message sent from server if email or password is incorrect. My code is below.
Call<LoginResponse> call = RetrofitClient.getInstance().getApi().userLogin(email, password);
call.enqueue(new Callback<LoginResponse>() {
#Override
public void onResponse(Call<LoginResponse> call, Response<LoginResponse> response) {
LoginResponse loginResponse = response.body();
System.out.println("body " + response.body());
System.out.println("response " + response.errorBody().toString());
sharedPrefManager.cancelDialog();
if (loginResponse != null) {
if (loginResponse.getSuccess()) {
sharedPrefManager.saveUser(loginResponse.getData(), password);
Intent intent = new Intent(SignIn.this, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
} else {
Toast.makeText(SignIn.this, loginResponse.getMessage(), Toast.LENGTH_SHORT).show();
}
} else {
SharedPrefManager.getInstance(SignIn.this).cancelDialog();
Toast.makeText(SignIn.this, response.message(), Toast.LENGTH_SHORT).show();
}
}
#Override
public void onFailure(Call<LoginResponse> call, Throwable t) {
sharedPrefManager.cancelDialog();
t.printStackTrace();
}
});
public class RetrofitClient {
private static final String BASE_URL = "my_base_url";
private static RetrofitClient mInstance;
private Retrofit retrofit;
OkHttpClient client = new OkHttpClient.Builder()
//.addInterceptor(new SpeechRecognitionIntercepter())
.connectTimeout(30, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS).build();
private RetrofitClient() {
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
public static synchronized RetrofitClient getInstance() {
if (mInstance == null) {
mInstance = new RetrofitClient();
}
return mInstance;
}
public Apis getApi() {
return retrofit.create(Apis.class);
}
}
public interface Apis {
#FormUrlEncoded
#POST("login")
Call<LoginResponse> userLogin(#Field("email") String email, #Field("password") String password);
}
Unsuccessful login response is:
{
"success": false,
"message": "Username or Password is incorrect."
}
Successful response is:
{
"success": true,
"message": "",
"data": {
"token": "",
"name": "Gmail",
"picture": "",
"userid": 60,
"phone": "(111) 114-4444",
"email": "tahir123#gmail.com",
"company_name": null,
"st_address": "Gmail account, Satellite",
"location_id": 1,
"account_type": 2
}
}
It's not really clear from your code if you receive a 200 status even if the call fails, but from what you describe, it seems like you get another http status code.
If this is the case, Retrofit still calls the onResponse method, but response.isSuccessful() is false and the body will be accessible through the response.errorBody() method.
An easy way to get this is:
if(response.isSuccessful())
// Do what you are doing
else {
Gson gson = new Gson();
LoginResponse error = gson.fromJson(response.errorBody().string());
// Use the error variable now
}
There's a lot going on here. Let's start with why do you need manual deserialization. Retrofit doesn't automatically convert the error body for you, you need to do it yourself. Here I've chosen to create a Gson instance, which is not elegant, but serves the purpose.
I also chose to use string(). This method reads the entire response into memory and can crash for big responses. Calling it will drain the okhttp buffer, which means you won't be able to call it again (to the best of my knowledge), so keep it in a variable if you want to use it multiple times.
Hope this helps

Fetch string data from JSON response retrofit

I'm working with JSON response which I can get from my server, at the beginning I have to log in at my application so I use such api:
#Headers("Content-type: application/json")
#POST("/v1/login")
Call<Post> auth(#Body Post body);
and also my POJO-class:
public class Post {
#SerializedName("username")
private String username;
#SerializedName("password")
private String password;
public Post(String username, String password) {
this.username = username;
this.password = password;
}
public String getUsername() {
return username;
}
public void setUsername (String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
and after all initialize it at my mainactivity class:
public void sendPost() {
final EditText titleEt = findViewById(R.id.login);
final EditText bodyEt = findViewById(R.id.password);
final String a = titleEt.getText().toString().trim();
final String b = bodyEt.getText().toString().trim();
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient client = new OkHttpClient.Builder().addInterceptor(interceptor).build();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://server/")
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.build();
APIService mAPIService = retrofit.create(APIService.class);
//retrofit.create(APIService.class);
mAPIService.auth(new Post(a, b)).enqueue(new Callback<Post>() {
#Override
public void onResponse(#NonNull Call<Post> call, #NonNull Response<Post> response) {
if (response.isSuccessful()) {
Toast.makeText(LoginActivity.this, "Post submitted to API.", Toast.LENGTH_LONG).show();
Intent intent = new Intent(LoginActivity.this, SecondScreen.class);
findViewById(R.id.btn_submit).getBackground().setColorFilter(Color.parseColor("#1cd000"), PorterDuff.Mode.MULTIPLY);
startActivity(intent);
saveData();
/* try {
String responseString = String.valueOf(response.body());
TextView txt = findViewById(R.id.post);
txt.setText(responseString);
}
catch (Exception e){
e.printStackTrace();
}*/
} else {
Toast.makeText(LoginActivity.this, "Unable to submit post to API.Error!!!", Toast.LENGTH_LONG).show();
findViewById(R.id.btn_submit).getBackground().setColorFilter(Color.parseColor("#FF0000"), PorterDuff.Mode.MULTIPLY);
}
}
#Override
public void onFailure(#NonNull Call<Post> call, #NonNull Throwable t) {
Toast.makeText(LoginActivity.this, "Unable to submit post to API.", Toast.LENGTH_LONG).show();
}
});
}
as you can see I commented my trying to fetch some data from JSON response, in general, I would like to get my access token from the response and I also wouldn't like to create some classes for fetching if it is possible, because I have already initialized my retrofit at my MainActivity class. After logging in a can get my received and sent messages in JSON too, but I would like to insert this data into the simple listview. So I hope that somebody at this forum will help me with my problem. Sorry for my maybe bad English.
You have to understand that each Retrofit request is asynchronous, that means, it will eventually be executed while your application runs. You should use RxJava to help you with that, since you should use to observe the data you need to get from your API.
Important Note: Also updating UI in your response from retrofit might trigger exceptions due to not running in Main Thread.
Some useful links to implement what you need :
https://www.toptal.com/android/functional-reactive-android-rxjava
https://www.journaldev.com/20433/android-rxjava-retrofit
https://medium.com/3xplore/handling-api-calls-using-retrofit-2-and-rxjava-2-1871c891b6ae

Working with access_token android retrofit?

In my application, I connect to a server and have to get a successful response to my further working, after such type of response, in general, I get two tokens ( access+refresh). For my further actions, I have to use my access token because I won't be able to get data. This token, in general, expires in 30 minutes. I can't understand how I can get a new token from the server without fails of my application. I had seen some questions and this Refreshing OAuth token using Retrofit without modifying all calls one was the best I think. But I can't understand the way of using it for me.
Right now I am using such an interface:
public interface APIService {
#Headers("Content-type: application/json")
#POST("/v1/login")
Call<Post> auth(#Body Post body);
#Headers({"Content-type: application/json",
"Authorization: Bearer access_token"})
#GET("/v1/message/list")
Call<Message> getInMess(#Query("type") int type, #Query("offset") int offset);
}
there I have to insert my access token by hands.
And then in my MainActivity Class I initialize my interface:
public void sendPost()
{
final EditText titleEt = findViewById(R.id.login);
final EditText bodyEt = findViewById(R.id.password);
final String a = titleEt.getText().toString().trim();
final String b = bodyEt.getText().toString().trim();
saveData();
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient client = new OkHttpClient.Builder().addInterceptor(interceptor).build();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://server/")
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.build();
APIService mAPIService = retrofit.create(APIService.class);
//retrofit.create(APIService.class);
mAPIService.auth(new Post(a, b)).enqueue(new Callback<Post>() {
#Override
public void onResponse(#NonNull Call<Post> call, #NonNull Response<Post> response) {
if (response.isSuccessful()) {
Toast.makeText(LoginActivity.this, "Post submitted to API.", Toast.LENGTH_LONG).show();
Intent intent = new Intent(LoginActivity.this, SecondScreen.class);
findViewById(R.id.btn_submit).getBackground().setColorFilter(Color.parseColor("#1cd000"), PorterDuff.Mode.MULTIPLY);
startActivity(intent);
} else {
Toast.makeText(LoginActivity.this, "Unable to submit post to API.Error!!!", Toast.LENGTH_LONG).show();
findViewById(R.id.btn_submit).getBackground().setColorFilter(Color.parseColor("#FF0000"), PorterDuff.Mode.MULTIPLY);
}
}
#Override
public void onFailure(#NonNull Call<Post> call, #NonNull Throwable t) {
Toast.makeText(LoginActivity.this, "Unable to submit post to API.", Toast.LENGTH_LONG).show();
}
});
}
Please help me to understand the strategy of my further development because I can't solve my problem.
P.S. Sorry for my bad English))
You need to intercept the request and add the header in your interceptor. I use this in my applications :
public class AuthenticationInterceptor implements Interceptor {
public AuthenticationInterceptor(Context context) {
}
#Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
if(!YOUR_TOKEN.isEmpty()) {
Request authenticatedRequest = request.newBuilder().header("Authorization", "Bearer:" + YOUR_TOKEN).build();
return chain.proceed(authenticatedRequest);
}
return chain.proceed(request);
}
}

Retrofit 2 error in Json parsing

I want to send a post response with no parameters on my url. However the response I am getting is this
[Lcom.example.c4u015.retrotestapp.Questions;#97ddd71
but the actual response is this
{
"MemberDays": "[{"Dt":"02/19/2016","AM":"0","AMS":"1","NS":"0","NSS":"0","PM":"1","PMS":"0","SB":"0","DS":"0","Note":""},{"Dt":"02/25/2016","AM":"0","AMS":"0","NS":"0","NSS":"0","PM":"0","PMS":"0","SB":"0","DS":"0","Note":""}]"
"Message": ""
"Status": "0"
}
This is my code.
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://192.168.1.99:82/MembersWS.svc/")
.addConverterFactory(GsonConverterFactory.create())
.build();
gitAPI stackOverflowAPI = retrofit.create(gitAPI.class);
Call<Questions[]> call = stackOverflowAPI.loadQuestions();
call.enqueue(new Callback<Questions[]>() {
#Override
public void onResponse(Response<Questions[]> response, Retrofit retrofit) {
if (response.isSuccess()) {
Questions[] user = response.body();
Log.e("parsed"," on response");
Log.e("response", response.body().toString()+" "+response.raw().toString());
} else {
int statusCode = response.code();
Log.e("status",""+statusCode);
// handle request errors yourself
ResponseBody errorBody = response.errorBody();
}
pd.dismiss();
}
#Override
public void onFailure(Throwable t) {
pd.dismiss();
Log.e("error", t.getLocalizedMessage());
Toast.makeText(MainActivity.this, t.getLocalizedMessage(), Toast.LENGTH_SHORT).show();
}
});
And this is the interface I have used
public interface gitAPI {
#POST("GetMemberAvailability/MBR0011581")
Call<Questions[]> loadQuestions();
}
you are printing the output of the toString() which is object id in this case, and it's value is waht you see [Lcom.example.c4u015.retrotestapp.Questions;#97ddd71
your response is an Array of type Question, so to print the output you will have to iterate over the array's items and call getters , ex:
user[0].getXXX(); where XXX is any getter method for class Questions
or you can use a for loop or other process you wish

Categories

Resources