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?
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.
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.
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.
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 .
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.
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.
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;
}
}
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
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();
}
Ich gehe dieses Problem manuell an:
Laden Sie die Volley-Bibliothek von github herunter und fügen Sie sie in das AndroidStudio-Projekt ein
Gehen Sie zur Klasse com.Android.volley.toolbox.HurlStack
Suche setConnectionParametersForRequest(connection, request);
Zeile innerhalb der performRequest
Methode
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(); }