当 error.networkResponse 为空时,Android Volley 中的 Http 状态代码

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/22948006/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-20 06:35:59  来源:igfitidea点击:

Http Status Code in Android Volley when error.networkResponse is null

androidhttp-status-codesandroid-volley

提问by David Manpearl

I am using Google Volley on the Android platform. I am having a problem in which the errorparameter in onErrorResponseis returning a null networkResponseFor the RESTful API I am using, I need to determine the Http Status Code which is often arriving as 401 (SC_UNAUTHORIZED) or 500 (SC_INTERNAL_SERVER_ERROR), and I can occasionally check via:

我在 Android 平台上使用 Google Volley。我有一个问题,其中error参数onErrorResponse返回空值networkResponse对于我正在使用的 RESTful API,我需要确定 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.
}

This throws a NullPointerExceptionbecause networkResponseis null.

这会抛出一个NullPointerException因为networkResponse为空。

How can I determine the Http Status Code in the function onErrorResponse?

如何确定函数中的 Http 状态代码onErrorResponse

Or, how can I ensure error.networkResponseis non-null in onErrorResponse?

或者,我如何确保error.networkResponse在 中不为空onErrorResponse

采纳答案by David Manpearl

401 Not Supported by Volley

401 不支持 Volley

It turns out that it is impossible to guarantee that error.networkResponse is non-null without modifying Google Volley code because of a bug in Volley that throws the Exception NoConnectionErrorfor Http Status Code 401 (HttpStatus.SC_UNAUTHORIZED) in BasicNetwork.java (134) prior to setting the value of networkResponse.

事实证明,这是不可能保证error.networkResponse非空,而无需修改谷歌凌空代码,因为在凌空抛出异常的一个错误NoConnectionError的HTTP状态代码401( HttpStatus.SC_UNAUTHORIZED)在BasicNetwork.java(134)之前设置的值networkResponse

Work-Around

变通

Instead of fixing the Volley code, our solution in this case was to modify the Web Service API to send Http Error Code 403 (HttpStatus.SC_FORBIDDEN) for the particular case in question.

在这种情况下,我们的解决方案不是修复 Volley 代码,而是修改 Web 服务 API 以HttpStatus.SC_FORBIDDEN针对相关特定情况发送 Http 错误代码 403 ( )。

For this Http Status Code, the value of error.networkResponseis non-null in the Volley error handler: public void onErrorResponse(VolleyError error). And, error.networkResponse.httpStatusCodecorrectly returns HttpStatus.SC_FORBIDDEN.

对于此 Http 状态代码,error.networkResponse在 Volley 错误处理程序中的值为非空:public void onErrorResponse(VolleyError error)。并且,error.networkResponse.httpStatusCode正确返回HttpStatus.SC_FORBIDDEN.

Other-Suggestions

其他建议

Rperryng's suggestion of extending the Request<T>class may have provided a solution, and is a creative and excellent idea. Thank you very much for the detailed example. I found the optimal solution for our case is to use the work-around because we are fortunate enough to have control of the web services API.

Rperryng 的扩展Request<T>类的建议可能已经提供了一个解决方案,并且是一个创造性和极好的想法。非常感谢您提供的详细示例。我发现我们案例的最佳解决方案是使用变通方法,因为我们很幸运能够控制 Web 服务 API。

I might opt for fixing the Volley code in one location within BasicNetwork.java if I did not have access to making a simple change at the server.

如果我无法在服务器上进行简单的更改,我可能会选择在 BasicNetwork.java 中的一个位置修复 Volley 代码。

回答by rperryng

Or, how can I ensure error.networkResponse is non-null in onErrorResponse?

或者,如何确保 onErrorResponse 中的 error.networkResponse 不为空?

My first thought would be to check if the object is null.

我的第一个想法是检查对象是否为空。

@Override
public void onErrorResponse(VolleyError error) {
    NetworkResponse networkResponse = error.networkResponse;
    if (networkResponse != null && networkResponse.statusCode == HttpStatus.SC_UNAUTHORIZED) {
        // HTTP Status Code: 401 Unauthorized
    }
}

Alternatively, you could also try grabbing the Status Code by extending the Requestclass and overriding parseNetworkResponse.

或者,您也可以尝试通过扩展Request类并覆盖parseNetworkResponse.

For example, if extending the abstract Request<T>class

例如,如果扩展抽象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));
        }
    }
    ...
}

Or, if you are using one of the toolbox classes that already extend the abstract Request<T>class and you don't want to muddle up the implementation for parseNetworkResponse(NetworkResponse networkResponse), continue overriding the method but return the super's implementation via super.parseNetworkResponse(networkResponse)

或者,如果您正在使用已经扩展抽象Request<T>类的工具箱类之一,并且您不想混淆 的实现parseNetworkResponse(NetworkResponse networkResponse),请继续覆盖该方法,但通过返回超级的实现super.parseNetworkResponse(networkResponse)

e.g. StringResponse

例如 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);
    }
}

usage:

用法:

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

Of course, the option to override the method inline is available too

当然,也可以选择重写方法内联

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:
HttpStatusis Deprecated. Use HttpURLConnectioninstead. See Link.

更新:
HttpStatus已弃用。使用HttpURLConnection来代替。请参阅链接

回答by Tolga Okur

Volley supports HTTP 401 Unauthorized response. But this response MUST include "WWW-Authenticate" header field.

Volley 支持 HTTP 401 未经授权的响应。但是这个响应必须包含“WWW-Authenticate”头域。

Without this header, 401 response causes "com.android.volley.NoConnectionError: java.io.IOException: No authentication challenges found"error.

如果没有这个标头,401 响应会导致"com.android.volley.NoConnectionError: java.io.IOException: No authentication challenges found"错误。

For more detail : https://stackoverflow.com/a/25556453/860189

更多详情:https: //stackoverflow.com/a/25556453/860189

If you consume 3rd party API's and have no right to change response header, you may consider to implement your own HttpStack because of this exception thrown from HurlStack. Or better, use OkHttpStack as a HttpStack.

如果您使用第 3 方 API 并且无权更改响应标头,您可以考虑实现自己的 HttpStack,因为 HurlStack 抛出了这个异常。或者更好的是,将 OkHttpStack 用作 HttpStack。

回答by Afjalur Rahman Rana

You may modify the volley library's performRequest me(toolbox/BasicNetwork.java) method to capture 401 Unauthorized response. (This modified code will also solve http-> https redirect problem of volley)

您可以修改 volley 库的 performRequest me(toolbox/BasicNetwork.java) 方法来捕获 401 Unauthorized 响应。(修改后的代码也将解决 volley 的 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);
            }
        }
    }
}

then in volley error handler use this code

然后在凌空错误处理程序中使用此代码

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

Happy coding :D

快乐编码:D

回答by artkoenig

The error.networkResponsewill be null, if the device has no network connection (you can proof this by enabling the airplane mode). Look at the corresponding code fragmentfrom the Volley library.

error.networkResponse会是null,如果设备没有网络连接(你可以证明这种通过启用飞行模式)。查看来自 Volley 库的相应代码片段

You have to check then, if the error is an instance of the NoConnectionError, before you look for the networkResponse. I cannot agree, that 401 error is not supported by Volley, I tested it and got a non-null networkResponseobject back with 401 status code. Look at the corresponding code here.

然后,您必须NoConnectionError在查找networkResponse. 我不能同意,Volley 不支持 401 错误,我对其进行了测试,并得到了一个networkResponse带有 401 状态代码的非空对象。在这里查看相应的代码。

回答by Uday Nayak

Network response can be received in the following format

可以按以下格式接收网络响应

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

回答by aung

This is how I check and grep error.

这就是我检查和 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();
                }

回答by Richi

I handle this problem manually:

我手动处理这个问题:

  1. Download Volley library from githuband add into AndroidStudio project

  2. Go to com.android.volley.toolbox.HurlStackclass

  3. Find setConnectionParametersForRequest(connection, request);line inside of performRequestmethod

  4. And finally add this codes belew of setConnectionParametersForRequest(connection, request);line :

  1. 从github下载Volley库并添加到AndroidStudio项目中

  2. com.android.volley.toolbox.HurlStack上课

  3. setConnectionParametersForRequest(connection, request);performRequest方法中查找行

  4. 最后添加以下代码setConnectionParametersForRequest(connection, request);行:

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