Подтвердить что ты не робот

Код статуса 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?

4b9b3361

Ответ 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();
        }