I'm using Retrofit library in my Android project. I have a rest server and trying to make request to it's login method. If login failed, server returns me 401 error and detailed message in json. My problem is that on my Nexus 5 with Android 5.0 it works fine, I can get the response body from the RetrofitError object. But on other devices, such as Sony Xperia M with Android 4.2.2 on the same request I'm getting No authentication challenges found exception and the RetrofitError.response object is null. Any ideas?
UPDATE
Here is my code. I also use Robospice library.
So this is an interface:
#POST("/auth")
User logIn(#Body UserSignUp userSignUp);
Here is my Robospice request class
public class UserLoginRequest extends RetrofitSpiceRequest<User, Server> {
private UserSignUp userSignUp;
public UserLoginRequest(UserSignUp userSignUp) {
super(User.class, Server.class);
this.userSignUp = userSignUp;
}
#Override
public User loadDataFromNetwork() throws Exception {
return getService().logIn(userSignUp);
}
}
And here is my request listener:
public final class UserLoginListener implements RequestListener<User> {
#Override
public void onRequestFailure(SpiceException spiceException) {
if (spiceException.getCause() != null && ((RetrofitError) spiceException.getCause()).getBody() != null) {
Toast.makeText(LoginActivity.this, ((ResponseDto) (((RetrofitError) spiceException.getCause()).getBody())).error, Toast.LENGTH_LONG).show(); //On Nexus 5 with Android 5 this line works fine
} else {
Toast.makeText(LoginActivity.this, "Login failed", Toast.LENGTH_SHORT).show(); //On other devices I'm getting to this
}
}
#Override
public void onRequestSuccess(User user) {
//Some code here
}
}
User is a subclass of ResponseDto class. ResponseDto class has only one public string field called error.
Seems that this exception is versions related - it's IOException thrown by HttpURLConnection. Try to change server's behavior or handle the IOException in retrofit.
No authentication challenges found
This error happens beause the server sends a 401 (Unauthorized) but does not give a WWW-Authenticate header which is a hint for the client what to do next.
Take a look here
Related
I am developing an Android app. In my app, I am working with Android to connect to server. I am using Laravel for server side. My server side code is working fine because I already tested it. The problem is with checking error status code in error listener of Volley. The status code is always zero when I check it even it is returning 401 exactly when I checked in browser and using other tools.
This is my Volley request return that throw error from server (401 status):
JsonObjectRequest request = new JsonObjectRequest(Request.Method.GET, url, new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Log.i("STATUS_CODE",CommonHelper.volleyResponseStatusCode(error));
if (CommonHelper.volleyResponseStatusCode(error) == 401) {
startActivity(new Intent(getActivity(), LoginActivity.class));
} else {
showMessageDialog("Server error encountered");
}
}
});
As you can see I log the status code.
This is my CommonHelper class with method that check Volley status code
public final class CommonHelper {
.
.
.
public static int volleyResponseStatusCode(VolleyError error)
{
NetworkResponse networkResponse = error.networkResponse;
if (networkResponse != null) {
return networkResponse.statusCode;
}
else{
return 0;
}
}
}
Whenever I check the status code, it is always returning 0. Why is this, and how can I check it in correct way?
I found the issue with that code. Status code is correctly returned if the server returned stats like 500,400 and so on except 401. But when server returns status code 401, network response is always null. So I just considered like, if the status returned 0, it is equal to 401. That is how I solved the problem with my app.
My request should get either JSON for POJO or JSON described error(can be invalid request fields, server problems and so on).
But retrofit in subscriber gives me only Throwable. How can I find out is that a network error, what is http code, and get JSON with error?
private class ProjectListSubscriber extends Subscriber<ProjectListResponse> {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
//is that a network? http code? convert json to error POJO?
}
#Override
public void onNext(ProjectListResponse projectListResponse) {
updateProjectList(projectListResponse.getProjectList());
}
}
Since you are using RxJava, onError is called in case of network errors and endpoints related error are part of the Response.
In case of error, check if the throwable is an instance of HttpException
public void onError(Throwable e) {
if (e instanceof HttpException) {
if the check is true, the you have an error in your request. Cast the throwable to HttpException, and access is members. E.g.
((HttpException) e).response().errorBody()
if the check is false then you have a network related error.
I have written a Retrofit code which has a Yii2 backend. The problem is: when I call the web-service on backend, it works perfectly. However, when I try to call the web-service from android device; it throws a response code of 404. Here is what I have done:
I am targeting a url which looks like: http://192.168.0.104/root-web/web/index.php?r= and it had an end-point: root/register
public interface RegisterAPIService {
#POST("practice/register")
Call<RegisterModel> registerUser(#Body RegisterDetails registerDetails);
}
The code in my activity looks like this..
RegisterDetails registerDetails = new RegisterDetails(email, mobile, password);
RegisterAPIService registerAPIService = retrofit.create(RegisterAPIService.class);
Call<RegisterModel> call =registerAPIService.registerUser(registerDetails);
call.enqueue(new Callback<RegisterModel>() {
#Override
public void onResponse(Response<RegisterModel> response) {
Log.d("Message", "code..."+response.code() + " message..." + response.message()+" body..."+response.body());
}
#Override
public void onFailure(Throwable t) {
}
});
} else {
// Error
}
}
I am getting 404 for the above code. I am trying to send my parameters in the form of a POST request. Please guide me through it.
You should call your development machine from the device, by its ip address, based on how they are connected. Also you can use Android Reverse Tethering tools for your operating system. for further study and options you can take a look at the answers to this question
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.
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 //
}