Код статуса Http в Android Volley, когда error.networkResponse имеет значение null
Я использую Google Volley на платформе Android.
У меня возникла проблема, в которой параметр error
в onErrorResponse
возвращает null networkResponse
Для API RESTful, который я использую, мне нужно определить код состояния Http, который часто прибывает как 401 (SC_UNAUTHORIZED) или 500 (SC_INTERNAL_SERVER_ERROR), и я могу иногда проверять через:
final int httpStatusCode = error.networkResponse.statusCode;
if(networkResponse == HttpStatus.SC_UNAUTHORIZED) {
// Http status code 401: Unauthorized.
}
Это вызывает a NullPointerException
, потому что networkResponse
имеет значение null.
Как определить код состояния Http в функции onErrorResponse
?
Или, как я могу гарантировать, что error.networkResponse
не является нулевым в onErrorResponse
?
Ответы
Ответ 1
401 Не поддерживается волейболом
Оказывается, невозможно гарантировать, что error.networkResponse не имеет значения null, не изменяя код Google Volley из-за ошибки во Volley, которая выбрасывает Exception NoConnectionError
для Http Status Code 401 (HttpStatus.SC_UNAUTHORIZED
) в BasicNetwork.java(134) перед установкой значения networkResponse
.
обходным
Вместо того, чтобы фиксировать код Volley, нашим решением в этом случае было изменение API веб-сервиса для отправки Http Error Code 403 (HttpStatus.SC_FORBIDDEN
) для конкретного рассматриваемого случая.
Для этого кода состояния Http значение error.networkResponse
не равно null в обработчике ошибок Volley: public void onErrorResponse(VolleyError error)
. И error.networkResponse.httpStatusCode
корректно возвращает HttpStatus.SC_FORBIDDEN
.
Другие-предложения
Предложение Rperryng о расширении класса Request<T>
, возможно, обеспечило решение и является творческой и отличной идеей. Большое спасибо за подробный пример. Я нашел оптимальное решение для нашего случая - использовать обход, потому что нам посчастливилось управлять API веб-сервисов.
Я могу выбрать фиксацию кода Volley в одном месте в BasicNetwork.java, если у меня не было доступа к простому изменению на сервере.
Ответ 2
Или, как я могу гарантировать, что error.networkResponse не имеет значения null onErrorResponse?
Моя первая мысль состояла в том, чтобы проверить, является ли объект нулевым.
@Override
public void onErrorResponse(VolleyError error) {
NetworkResponse networkResponse = error.networkResponse;
if (networkResponse != null && networkResponse.statusCode == HttpStatus.SC_UNAUTHORIZED) {
// HTTP Status Code: 401 Unauthorized
}
}
Кроме того, вы также можете попробовать захватить код состояния, расширив класс Request
и переопределив parseNetworkResponse
.
Например, если расширение абстрактного класса 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));
}
}
...
}
Или, если вы используете один из классов набора инструментов, которые уже расширяют абстрактный класс Request<T>
, и вы не хотите путать реализацию для parseNetworkResponse(NetworkResponse networkResponse)
, продолжайте переопределять метод, но верните супер реализацию через super.parseNetworkResponse(networkResponse)
например. 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);
}
}
использование:
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);
}
Конечно, опция переопределения метода inline также доступна
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
устарел. Вместо этого используйте HttpURLConnection
. См. Ссылка.
Ответ 3
Волейбол поддерживает HTTP 401 Несанкционированный отклик. Но этот ответ ДОЛЖЕН включать поле заголовка "WWW-Authenticate".
Без этого заголовка ответ 401 вызывает ошибку "com.android.volley.NoConnectionError: java.io.IOException: No authentication challenges found"
.
Подробнее: fooobar.com/questions/156391/...
Если вы потребляете сторонний API и не имеете права изменять заголовок ответа, вы можете подумать о том, чтобы реализовать свой собственный HttpStack из-за этого исключения, выброшенного из HurlStack. Или лучше, используйте OkHttpStack как HttpStack.
Ответ 4
error.networkResponse
будет null
, если устройство не имеет сетевого подключения (это можно доказать, включив режим самолета). Посмотрите на соответствующий фрагмент кода из библиотеки Volley.
Затем вы должны проверить, если ошибка является экземпляром NoConnectionError
, прежде чем искать networkResponse
. Я не могу согласиться, что ошибка 401 не поддерживается Volley, я ее протестировал и получил ненулевой объект networkResponse
с кодом состояния 401. Посмотрите на соответствующий код здесь.
Ответ 5
Ответ сети может быть получен в следующем формате
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;
}
}
Ответ 6
Вот как я проверяю ошибку и grep.
// 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();
}
Ответ 7
Я обрабатываю эту проблему вручную:
-
Загрузите библиотеку волейбола из github и добавьте в проект AndroidStudio
-
Перейдите в com.android.volley.toolbox.HurlStack
class
-
Найдите setConnectionParametersForRequest(connection, request);
строку внутри метода performRequest
-
И, наконец, добавьте эти коды в строку setConnectionParametersForRequest(connection, request);
:
// for avoiding this exception : No authentication challenges found
try {
connection.getResponseCode();
} catch (IOException e) {
e.printStackTrace();
}