Http Status Code in Android Volley when error.networkResponse is null

前端 未结 8 1110
忘了有多久
忘了有多久 2020-11-29 00:44

I am using Google Volley on the Android platform. I am having a problem in which the error parameter in onErrorResponse is returning a null n

相关标签:
8条回答
  • 2020-11-29 00:44

    I handle this problem manually:

    1. Download Volley library from github and add into AndroidStudio project

    2. Go to com.android.volley.toolbox.HurlStack class

    3. Find setConnectionParametersForRequest(connection, request); line inside of performRequest method

    4. And finally add this codes belew of setConnectionParametersForRequest(connection, request); line :

    // for avoiding this exception : No authentication challenges found
            try {
                connection.getResponseCode();
            } catch (IOException e) {
                e.printStackTrace();
            }
    
    0 讨论(0)
  • 2020-11-29 00:47

    The error.networkResponse will be null, if the device has no network connection (you can proof this by enabling the airplane mode). Look at the corresponding code fragment from the Volley library.

    You have to check then, if the error is an instance of the NoConnectionError, before you look for the networkResponse. I cannot agree, that 401 error is not supported by Volley, I tested it and got a non-null networkResponse object back with 401 status code. Look at the corresponding code here.

    0 讨论(0)
  • 2020-11-29 00:48

    401 Not Supported by Volley

    It turns out that it is impossible to guarantee that error.networkResponse is non-null without modifying Google Volley code because of a bug in Volley that throws the Exception NoConnectionError for Http Status Code 401 (HttpStatus.SC_UNAUTHORIZED) in BasicNetwork.java (134) prior to setting the value of networkResponse.

    Work-Around

    Instead of fixing the Volley code, our solution in this case was to modify the Web Service API to send Http Error Code 403 (HttpStatus.SC_FORBIDDEN) for the particular case in question.

    For this Http Status Code, the value of error.networkResponse is non-null in the Volley error handler: public void onErrorResponse(VolleyError error). And, error.networkResponse.httpStatusCode correctly returns HttpStatus.SC_FORBIDDEN.

    Other-Suggestions

    Rperryng's suggestion of extending the Request<T> class may have provided a solution, and is a creative and excellent idea. Thank you very much for the detailed example. I found the optimal solution for our case is to use the work-around because we are fortunate enough to have control of the web services API.

    I might opt for fixing the Volley code in one location within BasicNetwork.java if I did not have access to making a simple change at the server.

    0 讨论(0)
  • 2020-11-29 00:49

    Or, how can I ensure error.networkResponse is non-null in onErrorResponse?

    My first thought would be to check if the object is null.

    @Override
    public void onErrorResponse(VolleyError error) {
        NetworkResponse networkResponse = error.networkResponse;
        if (networkResponse != null && networkResponse.statusCode == HttpStatus.SC_UNAUTHORIZED) {
            // HTTP Status Code: 401 Unauthorized
        }
    }
    

    Alternatively, you could also try grabbing the Status Code by extending the Request class and overriding parseNetworkResponse.

    For example, if extending the abstract Request<T> class

    public class GsonRequest<T> extends Request<T> {
    
        ...
        private int mStatusCode;
    
        public int getStatusCode() {
            return mStatusCode;
        }
        ...
    
        @Override
        protected Response<T> parseNetworkResponse(NetworkResponse response) {
    
            mStatusCode = response.statusCode;
            try {
                Log.d(TAG, "[raw json]: " + (new String(response.data)));
                Gson gson = new Gson();
                String json = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
                return Response.success(gson.fromJson(json, mClazz),
                    HttpHeaderParser.parseCacheHeaders(response));
    
            } catch (UnsupportedEncodingException e) {
                return Response.error(new ParseError(e));
            } catch (JsonSyntaxException e) {
                return Response.error(new ParseError(e));
            }
        }
        ...
    }
    

    Or, if you are using one of the toolbox classes that already extend the abstract Request<T> class and you don't want to muddle up the implementation for parseNetworkResponse(NetworkResponse networkResponse), continue overriding the method but return the super's implementation via super.parseNetworkResponse(networkResponse)

    e.g. StringResponse

    public class MyStringRequest extends StringRequest {
    
        private int mStatusCode;
    
        public MyStringRequest(int method, String url, Listener<String> listener,
                ErrorListener errorListener) {
            super(method, url, listener, errorListener);
        }
    
        public int getStatusCode() {
            return mStatusCode;
        }
    
        @Override
        protected Response<String> parseNetworkResponse(NetworkResponse response) {
            mStatusCode = response.statusCode;
            return super.parseNetworkResponse(response);
        }
    }
    

    usage:

    public class myClazz extends FragmentActivity {
    
    
        private Request mMyRequest;
        ...
    
        public void makeNetworkCall() {
        mMyRequest = new MyNetworkRequest(
                Method.GET, 
                BASE_URL + Endpoint.USER,
                new Listener<String>() {
    
                    @Override
                    public void onResponse(String response) {
                        // Success
    
                    }
                }, 
                new ErrorListener() {
    
                    @Override
                    public void onErrorResponse(VolleyError error) {
                        if (mMyRequest.getStatusCode() == 401) {
                            // HTTP Status Code: 401 Unauthorized
                        }
                    }
                });
    
        MyVolley.getRequestQueue().add(request);
    }
    

    Of course, the option to override the method inline is available too

    public class MyClazz extends FragmentActivity {
    
        private int mStatusCode;
    
        ...
    
        public void makeNetworkCall() {
    
            StringRequest request = new StringRequest(
                    Method.GET, 
                    BASE_URL + Endpoint.USER,
                    new Listener<String>() {
    
                        @Override
                        public void onResponse(String response) {
                            // Success
    
                        }
                    }, 
                    new ErrorListener() {
    
                        @Override
                        public void onErrorResponse(VolleyError error) {
                            if (mStatusCode == 401) {
                                // HTTP Status Code: 401 Unauthorized
                            }
                        }
                    }) {
    
                        @Override
                        protected Response<String> parseNetworkResponse(NetworkResponse response) {
                            mStatusCode = response.statusCode;
                            return super.parseNetworkResponse(response);
                        }
                    };
        MyVolley.getRequestQueue.add(request);
    }
    

    Update:
    HttpStatus is Deprecated. Use HttpURLConnection instead. See Link.

    0 讨论(0)
  • 2020-11-29 00:52

    This is how I check and grep error.

                    // TimeoutError => most likely server is down or network is down.
                    Log.e(TAG, "TimeoutError: " + (e instanceof TimeoutError));
    
                    Log.e(TAG, "NoConnectionError: " + (e instanceof NoConnectionError));
                    /*if(error.getCause() instanceof UnknownHostException ||
                        error.getCause() instanceof EOFException ) {
                        errorMsg = resources.getString(R.string.net_error_connect_network);
                    } else {
                        if(error.getCause().toString().contains("Network is unreachable")) {
                            errorMsg = resources.getString(R.string.net_error_no_network);
                        } else {
                            errorMsg = resources.getString(R.string.net_error_connect_network);
                        }
                    }*/
    
                    Log.e(TAG, "NetworkError: " + (e instanceof NetworkError));
                    Log.e(TAG, "AuthFailureError: " + (e instanceof AuthFailureError));
                    Log.e(TAG, "ServerError: " + (e instanceof ServerError));
                    //error.networkResponse.statusCode
    
                    // inform dev
                    Log.e(TAG, "ParseError: " + (e instanceof ParseError));
                    //error.getCause() instanceof JsonSyntaxException
    
                    Log.e(TAG, "NullPointerException: " + (e.getCause() instanceof NullPointerException));
    
    
                    if (e.networkResponse != null) {
                        // 401 => login again
                        Log.e(TAG, String.valueOf(e.networkResponse.statusCode));
    
                        if (e.networkResponse.data != null) {
                            // most likely JSONString
                            Log.e(TAG, new String(e.networkResponse.data, StandardCharsets.UTF_8));
    
                            Toast.makeText(getApplicationContext(),
                                    new String(e.networkResponse.data, StandardCharsets.UTF_8),
                                    Toast.LENGTH_LONG).show();
                        }
                    }
                    else if (e.getMessage() == null) {
                        Log.e(TAG, "e.getMessage");
                        Log.e(TAG, "" + e.getMessage());
    
                        if (e.getMessage() != null && e.getMessage() != "")
                            Toast.makeText(getApplicationContext(),
                                    e.getMessage(), Toast.LENGTH_LONG).show();
                        else
                            Toast.makeText(getApplicationContext(),
                                    "could not reach server", Toast.LENGTH_LONG).show();
                    }
                    else if (e.getCause() != null) {
                        Log.e(TAG, "e.getCause");
                        Log.e(TAG, "" + e.getCause().getMessage());
    
                        if (e.getCause().getMessage() != null && e.getCause().getMessage() != "")
                            Toast.makeText(getApplicationContext(),
                                    e.getCause().getMessage(), Toast.LENGTH_LONG).show();
                        else
                            Toast.makeText(getApplicationContext(),
                                    "could not reach server", Toast.LENGTH_LONG).show();
                    }
    
    0 讨论(0)
  • 2020-11-29 00:53

    Volley supports HTTP 401 Unauthorized response. But this response MUST include "WWW-Authenticate" header field.

    Without this header, 401 response causes "com.android.volley.NoConnectionError: java.io.IOException: No authentication challenges found" error.

    For more detail : https://stackoverflow.com/a/25556453/860189

    If you consume 3rd party API's and have no right to change response header, you may consider to implement your own HttpStack because of this exception thrown from HurlStack. Or better, use OkHttpStack as a HttpStack.

    0 讨论(0)
提交回复
热议问题