volley jsonobjectrequest android http-status-codes android-volley

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:

  1. Descargue la librería Volley de github y agréguela al proyecto AndroidStudio

  2. Ir a la clase com.android.volley.toolbox.HurlStack

  3. Encuentra setConnectionParametersForRequest(connection, request); línea dentro del método performRequest

  4. 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.