web-dev-qa-db-ja.com

error.networkResponseがnullの場合、Android VolleyのHTTPステータスコード

AndroidプラットフォームでGoogle Volleyを使用しています。erroronErrorResponseパラメーターがnull networkResponseを返している問題があります。私が使用しているRESTful API、私はしばしば401(SC_UNAUTHORIZED)または500(SC_INTERNAL_SERVER_ERROR)として到着しているHttpステータスコードを決定する必要があり、私は時々経由で確認できます:

final int httpStatusCode = error.networkResponse.statusCode;
if(networkResponse == HttpStatus.SC_UNAUTHORIZED) {
    // Http status code 401: Unauthorized.
}

NullPointerExceptionがnullであるため、これはnetworkResponseをスローします。

関数onErrorResponseのHttpステータスコードを確認するにはどうすればよいですか?

または、onErrorResponseerror.networkResponseがnullでないことを確認するにはどうすればよいですか?

41
David Manpearl

401 Volleyでサポートされていません

Httpステータスコード401(_HttpStatus.SC_UNAUTHORIZED_)の例外NoConnectionErrorをスローするVolleyのバグにより、Google Volleyコードを変更せずにerror.networkResponseがnull以外であることを保証することは不可能であることが判明しましたnetworkResponseの値を設定する前のBasicNetwork.Java(134)。

回避策

この場合のソリューションは、Volleyコードを修正する代わりに、WebサービスAPIを変更して、問題の特定のケースに対してHttpエラーコード403(_HttpStatus.SC_FORBIDDEN_)を送信することでした。

このHttpステータスコードの場合、Volleyエラーハンドラーの_error.networkResponse_の値はnullではありません:public void onErrorResponse(VolleyError error)。そして、_error.networkResponse.httpStatusCode_は_HttpStatus.SC_FORBIDDEN_を正しく返します。

その他の提案

_Request<T>_クラスを拡張するというRperryngの提案は、解決策を提供した可能性があり、創造的で優れたアイデアです。詳細な例をありがとうございました。私たちの場合の最適な解決策は、WebサービスAPIを制御できる幸運に恵まれているため、回避策を使用することです。

サーバーで簡単な変更を行うことができない場合は、BasicNetwork.Java内の1つの場所でVolleyコードを修正することを選択できます。

25
David Manpearl

または、onErrorResponseでerror.networkResponseがnullでないことをどのように確認できますか?

最初に考えたのは、オブジェクトがnullかどうかを確認することです。

_@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>_クラスを既に拡張しているツールボックスクラスの1つを使用しており、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);
}
_

もちろん、メソッドのインラインをオーバーライドするオプションも利用可能です

_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);
}
_

更新:
HttpStatusは非推奨です。代わりにHttpURLConnectionを使用してください。 リンク を参照してください。

78
rperryng

Volleyは、HTTP 401 Unauthorized応答をサポートしています。ただし、この応答には「WWW-Authenticate」ヘッダーフィールドを含める必要があります。

このヘッダーがない場合、401応答により"com.Android.volley.NoConnectionError: Java.io.IOException: No authentication challenges found"エラー。

詳細: https://stackoverflow.com/a/25556453/860189

サードパーティAPIを使用し、応答ヘッダーを変更する権利がない場合、HurlStackからスローされるこの例外のために、独自のHttpStackを実装することを検討できます。または、OkHttpStackをHttpStackとして使用してください。

5
Tolga Okur

error.networkResponseは、デバイスにネットワーク接続がない場合はnullになります(機内モードを有効にすることでこれを証明できます)。 Volleyライブラリの対応する code fragment を見てください。

エラーがNoConnectionErrorのインスタンスである場合、networkResponseを探す前に確認する必要があります。私は同意できません。401エラーはVolleyでサポートされていないので、テストし、401ステータスコードでnull以外のnetworkResponseオブジェクトを取得しました。対応するコードを見てください here

3
artkoenig

ネットワーク応答は、次の形式で受信できます

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;
                    }
                }
3
Uday Nayak

VolleyライブラリのperformRequest me(toolbox/BasicNetwork.Java)メソッドを変更して、401 Unauthorized応答をキャプチャできます。 (この修正されたコードは、http-> httpsボレーのリダイレクト問題も解決します)

 @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);
            }
        }
    }
}

その後、ボレーエラーハンドラでこのコードを使用します

@Override
        public void onErrorResponse(VolleyError error) {
             if (error instanceof AuthFailureError) {
                //handler error 401 unauthorized from here
            }
        }
    })

ハッピーコーディング:D

2

これは、エラーをチェックして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();
                }
1
aung

この問題を手動で処理します。

  1. githubのVolleyライブラリ をダウンロードし、AndroidStudioプロジェクトに追加します

  2. _com.Android.volley.toolbox.HurlStack_クラスに移動します

  3. performRequestメソッド内のsetConnectionParametersForRequest(connection, request);行を見つける

  4. 最後に、setConnectionParametersForRequest(connection, request);行から次のコードを追加します。

_// for avoiding this exception : No authentication challenges found
        try {
            connection.getResponseCode();
        } catch (IOException e) {
            e.printStackTrace();
        }
_
0
Richi