jsonobjectrequest - android volley post json
Código de estado de Http en Android Volley cuando error.networkResponse es nulo (7)
Estoy usando Google Volley en la plataforma Android. Tengo un problema en el que el parámetro de error
en onErrorResponse
devuelve una networkResponse
nula networkResponse
Para la API RESTful que estoy usando, necesito determinar el código de estado Http que a menudo llega como 401 (SC_UNAUTHORIZED) o 500 (SC_INTERNAL_SERVER_ERROR), y yo En ocasiones puede verificar a través de:
final int httpStatusCode = error.networkResponse.statusCode;
if(networkResponse == HttpStatus.SC_UNAUTHORIZED) {
// Http status code 401: Unauthorized.
}
Esto arroja una NullPointerException
porque networkResponse
es nulo.
¿Cómo puedo determinar el código de estado Http en la función onErrorResponse
?
O bien, ¿cómo puedo asegurarme de que error.networkResponse
no sea nulo en onErrorResponse
?
O bien, ¿cómo puedo asegurarme de que error.networkResponse no sea nulo en onErrorResponse?
Mi primer pensamiento sería verificar si el objeto es nulo.
@Override
public void onErrorResponse(VolleyError error) {
NetworkResponse networkResponse = error.networkResponse;
if (networkResponse != null && networkResponse.statusCode == HttpStatus.SC_UNAUTHORIZED) {
// HTTP Status Code: 401 Unauthorized
}
}
Alternativamente, también puede intentar obtener el código de estado extendiendo la clase Request
y anulando parseNetworkResponse
.
Por ejemplo, si extiende la clase abstracta de Request<T>
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));
}
}
...
}
O bien, si está utilizando una de las clases de caja de herramientas que ya extienden la clase abstracta de Request<T>
y no desea parseNetworkResponse(NetworkResponse networkResponse)
la implementación de parseNetworkResponse(NetworkResponse networkResponse)
, continúe reemplazando el método pero devuelva la implementación del super.parseNetworkResponse(networkResponse)
través de super.parseNetworkResponse(networkResponse)
por ejemplo, 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);
}
}
uso:
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);
}
Por supuesto, la opción para anular el método en línea también está 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);
}
Actualizar:
HttpStatus
está en HttpStatus
. Use HttpURLConnection
en HttpURLConnection
lugar. Ver enlace .
401 No respaldado por Volley
Resulta que es imposible garantizar que error.networkResponse no sea nulo sin modificar el código de Google Volley debido a un error en Volley que arroja la excepción NoConnectionError
para Http Status Code 401 ( HttpStatus.SC_UNAUTHORIZED
) en BasicNetwork.java (134) antes de establecer el valor de networkResponse
.
Work-Around
En lugar de corregir el código de Volley, nuestra solución en este caso fue modificar la API del servicio web para enviar el código de error Http 403 ( HttpStatus.SC_FORBIDDEN
) para el caso particular en cuestión.
Para este código de estado Http, el valor de error.networkResponse
no es nulo en el controlador de errores Volley: public void onErrorResponse(VolleyError error)
. Y, error.networkResponse.httpStatusCode
retorna correctamente HttpStatus.SC_FORBIDDEN
.
Otras sugerencias
La sugerencia de Rperryng de extender la clase Request<T>
puede haber proporcionado una solución, y es una idea creativa y excelente. Muchas gracias por el ejemplo detallado. Encontré que la solución óptima para nuestro caso es utilizar el margen de error porque tenemos la suerte de tener el control de la API de servicios web.
Podría optar por arreglar el código de Volley en una ubicación dentro de BasicNetwork.java si no tuviera acceso a hacer un cambio simple en el servidor.
Así es como compruebo y 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();
}
El error.networkResponse
será null
, si el dispositivo no tiene conexión de red (puede probar esto habilitando el modo avión). Mira el fragmento de código correspondiente de la biblioteca de Volley.
Entonces, debe verificar si el error es una instancia de NoConnectionError
antes de buscar la networkResponse
. No puedo estar de acuerdo con que Volley no admita el error 401, lo probé y obtuve un objeto networkResponse
no nulo con el código de estado 401. Mira el código correspondiente aquí .
La respuesta de red se puede recibir en el siguiente formato
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;
}
}
Manejo este problema de forma manual:
Descargue la librería Volley de github y agréguela al proyecto AndroidStudio
Ir a la clase
com.android.volley.toolbox.HurlStack
Encuentra
setConnectionParametersForRequest(connection, request);
línea dentro del métodoperformRequest
Y finalmente agregue estos códigos después de
setConnectionParametersForRequest(connection, request);
línea:
// for avoiding this exception : No authentication challenges found try { connection.getResponseCode(); } catch (IOException e) { e.printStackTrace(); }
Volley admite HTTP 401 Respuesta no autorizada. Pero esta respuesta DEBE incluir el campo de encabezado "WWW-Authenticate".
Sin este encabezado, la respuesta 401 provoca el error "com.android.volley.NoConnectionError: java.io.IOException: No authentication challenges found"
.
Para más detalles: https://.com/a/25556453/860189
Si consume API de terceros y no tiene derecho a cambiar el encabezado de respuesta, puede considerar implementar su propia HttpStack debido a esta excepción lanzada desde HurlStack. O mejor, use OkHttpStack como HttpStack.