wake-up-neo.com

HTTP-Statuscode in Android Volley, wenn error.networkResponse null ist

Ich verwende Google Volley auf der Android-Plattform .. __ Ich habe ein Problem, bei dem der error-Parameter in onErrorResponse eine Null networkResponse.__ zurückgibt. Für die RESTful-API, die ich verwende, muss ich den HTTP-Statuscode ermitteln kommt oft als 401 (SC_UNAUTHORIZED) oder 500 (SC_INTERNAL_SERVER_ERROR) an, und ich kann gelegentlich überprüfen über:

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

Dies wirft eine NullPointerException, da networkResponse null ist.

Wie kann ich den HTTP-Statuscode in der Funktion onErrorResponse ermitteln?

Oder wie kann ich sicherstellen, dass error.networkResponse in onErrorResponse nicht null ist?

40
David Manpearl

401 Wird nicht von Volley unterstützt

Es stellt sich heraus, dass es unmöglich ist, zu garantieren, dass error.networkResponse nicht null ist, ohne den Google Volley-Code zu ändern, da in Volley ein Fehler aufgetreten ist, der die Exception NoConnectionError für den HTTP-Statuscode 401 (HttpStatus.SC_UNAUTHORIZED) in BasicNetwork.Java (134) auslöst den Wert von networkResponse einstellen.

Umgehen

Anstatt den Volley-Code zu korrigieren, bestand unsere Lösung in diesem Fall darin, die Web-Service-API so zu ändern, dass der HTTP-Fehlercode 403 (HttpStatus.SC_FORBIDDEN) für den jeweiligen Fall gesendet wird.

Für diesen HTTP-Statuscode ist der Wert von error.networkResponse im Volley-Fehlerhandler nicht null: public void onErrorResponse(VolleyError error). Und error.networkResponse.httpStatusCode gibt HttpStatus.SC_FORBIDDEN korrekt zurück.

Andere-Vorschläge

Der Vorschlag von Rperryng, die Klasse Request<T> zu erweitern, könnte eine Lösung gefunden haben und ist eine kreative und hervorragende Idee. Vielen Dank für das ausführliche Beispiel. Die optimale Lösung für unseren Fall ist die Verwendung der Problemumgehung, da wir das Glück haben, die Webservices-API zu steuern. 

Ich könnte mich dazu entscheiden, den Volley-Code an einem Ort innerhalb von BasicNetwork.Java zu reparieren, wenn ich keine einfache Änderung am Server vornehmen konnte. 

25
David Manpearl

Oder wie kann ich sicherstellen, dass error.networkResponse in .__ nicht null ist. onErrorResponse?

Mein erster Gedanke wäre zu prüfen, ob das Objekt null ist.

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

Alternativ können Sie auch versuchen, den Statuscode zu übernehmen, indem Sie die Request-Klasse erweitern und parseNetworkResponse überschreiben. 

Zum Beispiel, wenn Sie die abstrakte Request<T>-Klasse erweitern

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

Wenn Sie eine der Toolbox-Klassen verwenden, die bereits die abstrakte Request<T>-Klasse erweitern, und die Implementierung für parseNetworkResponse(NetworkResponse networkResponse) nicht durcheinander bringen möchten, setzen Sie das Überschreiben der Methode fort, geben Sie die Implementierung des Supers jedoch über super.parseNetworkResponse(networkResponse) zurück.

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

verwendungszweck:

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

Natürlich ist auch die Option zum Überschreiben der Methode inline verfügbar

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 ist veraltet. Verwenden Sie stattdessen HttpURLConnection. Siehe Link .

73
rperryng

Volley unterstützt HTTP 401 Unerlaubte Antwort. Diese Antwort MUSS jedoch das Header-Feld "WWW-Authenticate" enthalten. 

Ohne diesen Header verursacht die Antwort 401 einen "com.Android.volley.NoConnectionError: Java.io.IOException: No authentication challenges found"-Fehler.

Für weitere Informationen: https://stackoverflow.com/a/25556453/860189

Wenn Sie APIs von Drittanbietern verwenden und kein Recht haben, den Antwortheader zu ändern, können Sie erwägen, Ihren eigenen HttpStack zu implementieren, da diese Ausnahme von HurlStack ausgelöst wurde. Oder verwenden Sie OkHttpStack besser als HttpStack.

5
Tolga Okur

Der error.networkResponse ist null, wenn das Gerät keine Netzwerkverbindung hat (Sie können dies durch Aktivieren des Flugzeugmodus überprüfen). Schauen Sie sich das entsprechende codefragment aus der Volley-Bibliothek an.

Sie müssen dann prüfen, ob der Fehler eine Instanz von NoConnectionError ist, bevor Sie nach networkResponse suchen. Ich kann nicht zustimmen, dass der 401-Fehler nicht von Volley unterstützt wird. Ich habe ihn getestet und ein Nicht-Nullobjekt networkResponse mit 401-Statuscode erhalten. Schauen Sie sich den entsprechenden Code hier an.

3
artkoenig

Die Netzwerkantwort kann im folgenden Format empfangen werden

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;
                    }
                }
3
Uday Nayak

Sie können die Methode performRequest me (toolbox/BasicNetwork.Java) der Volleybibliothek so ändern, dass nicht autorisierte Antworten erfasst werden. (Dieser modifizierte Code löst auch das http-> https-Umleitungsproblem von Volleyball.)

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

dann verwenden Sie im Volley-Fehlerhandler diesen Code 

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

Glückliche Kodierung: D

2

So überprüfe ich und grep Fehler.

                // 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
aung

Ich gehe dieses Problem manuell an:

  1. Laden Sie die Volley-Bibliothek von github herunter und fügen Sie sie in das AndroidStudio-Projekt ein

  2. Gehen Sie zur Klasse com.Android.volley.toolbox.HurlStack

  3. Suche setConnectionParametersForRequest(connection, request); Zeile innerhalb der performRequest Methode 

  4. Fügen Sie schließlich diese Codes unterhalb der Zeile setConnectionParametersForRequest(connection, request); hinzu:

// for avoiding this exception : No authentication challenges found
        try {
            connection.getResponseCode();
        } catch (IOException e) {
            e.printStackTrace();
        }
0
Richi