Code d'état Http dans Android Volley en cas d'erreur.networkResponse est null

J'utilise Google Volley sur la plateforme Android. Je vais avoir un problème dans lequel l' error paramètre onErrorResponse renvoie un nul networkResponse Pour L'API RESTful que j'utilise, j'ai besoin de déterminer le Code de statut Http qui arrive souvent en 401 (SC_UNAUTHORIZED) ou 500 (SC_INTERNAL_SERVER_ERROR), et je peux occasionnellement vérifier via:

final int httpStatusCode = error.networkResponse.statusCode;
if(networkResponse == HttpStatus.SC_UNAUTHORIZED) {
    // Http status code 401: Unauthorized.
}

cela jette un NullPointerException parce que networkResponse est nulle.

Comment puis-je déterminer le code D'état Http dans la fonction onErrorResponse?

Ou, comment puis-je m'assurer error.networkResponse est non-null onErrorResponse?

37
demandé sur Nijraj Gelani 2014-04-09 00:56:58

8 réponses

Ou, comment puis-je m'assurer d'erreur.la réponse au réseau n'est pas nulle en onErrorResponse?

Ma première pensée serait de vérifier si l'objet est null.

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

vous pouvez aussi essayer de saisir le code de statut en étendant le Request classe et en remplaçant parseNetworkResponse.

Par exemple, si l'extension de l'abstrait Request<T> classe

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));
        }
    }
    ...
}

ou, si vous utilisez l'une des classes de la boîte à outils qui étendre l'abstrait Request<T> classe et vous ne voulez pas embrouiller la mise en œuvre pour parseNetworkResponse(NetworkResponse networkResponse), continuer à supplanter la méthode mais retourner l'implémentation du super 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);
    }
}

utilisation:

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);
}

bien sûr, la possibilité de remplacer la méthode inline est aussi disponible

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);
}

mise à Jour:

HttpStatus est déprécié. Utilisez HttpURLConnection à la place. Voir Lien.

70
répondu rperryng 2017-05-23 12:10:26

401 Non pris en charge par le Volley -

Il s'avère qu'il est impossible de garantir que l'erreur.networkResponse est non-null sans modifier le code Google Volley à cause d'un bug dans Volley qui jette L'Exception NoConnectionError pour le code d'état Http 401 (HttpStatus.SC_UNAUTHORIZED) en BasicNetwork.java (134) avant de définir la valeur de networkResponse.

Travail

au lieu de corriger le code Volley, notre solution dans ce cas était de modifier L'API de Service Web pour envoyer Code D'Erreur Http 403 (HttpStatus.SC_FORBIDDEN) pour le cas particulier en question.

pour ce code de statut Http, la valeur de error.networkResponse est non-nulle dans le Volley gestionnaire d'erreur: public void onErrorResponse(VolleyError error). Et,error.networkResponse.httpStatusCode renvoie correctement HttpStatus.SC_FORBIDDEN.

Autres-Suggestions

la suggestion de Rperryng d'étendre le Request<T> classe peut avoir fourni une solution, et est une création et une excellente idée. Merci beaucoup pour l'exemple détaillé. J'ai trouvé la solution optimale pour notre cas est utiliser la solution de rechange parce que nous avons la chance d'avoir le contrôle de l'API services web.

je pourrais opter pour la fixation du Volley le code dans un emplacement à l'intérieur de BasicNetwork.java si je n'avais pas accès à faire un simple changement sur le serveur.

25
répondu David Manpearl 2014-04-18 22:21:09

Volley supporte la réponse non autorisée HTTP 401. Mais cette réponse doit inclure le champ d'en-tête "WWW-Authenticate".

Sans cette en-tête, la réponse 401 causes "com.android.volley.NoConnectionError: java.io.IOException: No authentication challenges found" erreur.

Pour plus de détails : https://stackoverflow.com/a/25556453/860189

si vous consommez des API tierces et que vous n'avez pas le droit de changer l'en-tête de réponse, vous pouvez envisager d'implémenter votre propre HttpStack à cause de cette exception lancée de HurlStack. Ou mieux, utiliser OkHttpStack comme un HttpStack.

5
répondu tolpp 2017-05-23 12:18:21

error.networkResponse sera null, si le périphérique n'a pas de connexion réseau (vous pouvez le prouver en activant le mode avion). Regardez la correspondance fragment de code de la bibliothèque Volley.

vous devez vérifier alors, si l'erreur est une instance du NoConnectionError, avant de chercher sur le networkResponse. Je ne peux pas d'accord, que l'erreur 401 n'est pas pris en charge par le Volley, je l'ai testé et j'ai obtenu un non-null networkResponse objet de retour avec 401 code d'état. Regardez le code correspondant ici.

3
répondu artkoenig 2016-11-13 10:05:09

la réponse du réseau peut être reçue dans le format suivant

NetworkResponse response = error.networkResponse;
                if(response != null && response.data != null){
                    switch(response.statusCode){
                        case 403:
                            json = new String(response.data);
                            json = trimMessage(json, "error");
                            if(json != null) displayMessage(json);
                            break;
                    }
                }
2
répondu Uday Nayak 2016-06-22 12:57:43

vous pouvez modifier le performRequest me(toolbox/BasicNetwork) de la bibliothèque volley.java) pour saisir 401 réponses non autorisées. (Ce code modifié résoudra aussi le problème de redirection http-> https de volley)

 @Override
public NetworkResponse performRequest(Request<?> request) throws VolleyError {
    long requestStart = SystemClock.elapsedRealtime();
    while (true) {
        HttpResponse httpResponse = null;
        byte[] responseContents = null;
        Map<String, String> responseHeaders = Collections.emptyMap();
        try {
            // Gather headers.
            Map<String, String> headers = new HashMap<String, String>();
            addCacheHeaders(headers, request.getCacheEntry());
            httpResponse = mHttpStack.performRequest(request, headers);
            StatusLine statusLine = httpResponse.getStatusLine();
            int statusCode = statusLine.getStatusCode();

            responseHeaders = convertHeaders(httpResponse.getAllHeaders());
            // Handle cache validation.
            if (statusCode == HttpStatus.SC_NOT_MODIFIED) {

                Entry entry = request.getCacheEntry();
                if (entry == null) {
                    return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, null,
                            responseHeaders, true,
                            SystemClock.elapsedRealtime() - requestStart);
                }

                // A HTTP 304 response does not have all header fields. We
                // have to use the header fields from the cache entry plus
                // the new ones from the response.
                // http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.5
                entry.responseHeaders.putAll(responseHeaders);
                return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, entry.data,
                        entry.responseHeaders, true,
                        SystemClock.elapsedRealtime() - requestStart);
            }

            // Handle moved resources
            if (statusCode == HttpStatus.SC_MOVED_PERMANENTLY || statusCode == HttpStatus.SC_MOVED_TEMPORARILY) {
                String newUrl = responseHeaders.get("Location");
                request.setUrl(newUrl);
            }



            // Some responses such as 204s do not have content.  We must check.
            if (httpResponse.getEntity() != null) {
                responseContents = entityToBytes(httpResponse.getEntity());
            } else {
                // Add 0 byte response as a way of honestly representing a
                // no-content request.
                responseContents = new byte[0];
            }

            // if the request is slow, log it.
            long requestLifetime = SystemClock.elapsedRealtime() - requestStart;
            logSlowRequests(requestLifetime, request, responseContents, statusLine);

            if (statusCode < 200 || statusCode > 299) {
                throw new IOException();
            }
            return new NetworkResponse(statusCode, responseContents, responseHeaders, false,
                    SystemClock.elapsedRealtime() - requestStart);
        } catch (SocketTimeoutException e) {
            attemptRetryOnException("socket", request, new TimeoutError());
        } catch (ConnectTimeoutException e) {
            attemptRetryOnException("connection", request, new TimeoutError());
        } catch (MalformedURLException e) {
            throw new RuntimeException("Bad URL " + request.getUrl(), e);
        } catch (IOException e) {
            int statusCode = 0;
            NetworkResponse networkResponse = null;
            if (httpResponse != null) {
                statusCode = httpResponse.getStatusLine().getStatusCode();
            } else {
                throw new NoConnectionError(e);
            }
            if (statusCode == HttpStatus.SC_MOVED_PERMANENTLY ||
                    statusCode == HttpStatus.SC_MOVED_TEMPORARILY) {
                VolleyLog.e("Request at %s has been redirected to %s", request.getUrl(), request.getUrl());
            } else {
                VolleyLog.e("Unexpected response code %d for %s", statusCode, request.getUrl());
                if (statusCode==HttpStatus.SC_FORBIDDEN) {
                    throw new VolleyError("403");
                }else if (statusCode == HttpStatus.SC_UNAUTHORIZED) {
                    attemptRetryOnException("auth",
                            request, new AuthFailureError(""));
                }
            }
            if (responseContents != null) {
                networkResponse = new NetworkResponse(statusCode, responseContents,
                        responseHeaders, false, SystemClock.elapsedRealtime() - requestStart);
                if (statusCode == HttpStatus.SC_UNAUTHORIZED) {
                    attemptRetryOnException("auth",
                            request, new AuthFailureError(networkResponse));
                } else if (statusCode == HttpStatus.SC_MOVED_PERMANENTLY ||
                        statusCode == HttpStatus.SC_MOVED_TEMPORARILY) {
                    attemptRetryOnException("redirect",
                            request, new AuthFailureError(networkResponse));
                } else {
                    // TODO: Only throw ServerError for 5xx status codes.
                    throw new ServerError(networkResponse);
                }
            } else {
                throw new NetworkError(e);
            }
        }
    }
}

puis dans volley erreur handler utilisez ce code

@Override
        public void onErrorResponse(VolleyError error) {
             if (error instanceof AuthFailureError) {
                //handler error 401 unauthorized from here
            }
        }
    })

codage heureux: D

2
répondu Afjalur Rahman Rana 2018-06-02 20:03:33

Voici comment je vérifie et grep erreur.

                // 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();
                }
1
répondu aung 2016-10-14 23:48:57

je gère ce problème manuellement:

  1. Télécharger Bibliothèque Volley de github et ajouter au projet AndroidStudio

  2. com.android.volley.toolbox.HurlStack classe

  3. Trouver setConnectionParametersForRequest(connection, request); ligne à l'intérieur de performRequest méthode

  4. et enfin ajouter ces codes soussetConnectionParametersForRequest(connection, request); ligne :

// for avoiding this exception : No authentication challenges found
        try {
            connection.getResponseCode();
        } catch (IOException e) {
            e.printStackTrace();
        }
0
répondu Richi 2017-03-06 16:28:35