当 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
Http Status Code in Android Volley when error.networkResponse is null
提问by David Manpearl
I am using Google Volley on the Android platform.
I am having a problem in which the error
parameter in onErrorResponse
is returning a null networkResponse
For 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 NullPointerException
because networkResponse
is null.
这会抛出一个NullPointerException
因为networkResponse
为空。
How can I determine the Http Status Code in the function onErrorResponse
?
如何确定函数中的 Http 状态代码onErrorResponse
?
Or, how can I ensure error.networkResponse
is 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 NoConnectionError
for 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.networkResponse
is non-null in the Volley error handler: public void onErrorResponse(VolleyError error)
. And, error.networkResponse.httpStatusCode
correctly 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 Request
class 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:HttpStatus
is Deprecated. Use HttpURLConnection
instead. 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.networkResponse
will 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 networkResponse
object 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:
我手动处理这个问题:
Download Volley library from githuband add into AndroidStudio project
Go to
com.android.volley.toolbox.HurlStack
classFind
setConnectionParametersForRequest(connection, request);
line inside ofperformRequest
methodAnd finally add this codes belew of
setConnectionParametersForRequest(connection, request);
line :
去
com.android.volley.toolbox.HurlStack
上课setConnectionParametersForRequest(connection, request);
在performRequest
方法中查找行最后添加以下代码
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(); }