AndroidプラットフォームでGoogle Volleyを使用しています。error
のonErrorResponse
パラメーターが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ステータスコードを確認するにはどうすればよいですか?
または、onErrorResponse
でerror.networkResponse
がnullでないことを確認するにはどうすればよいですか?
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コードを修正することを選択できます。
または、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
を使用してください。 リンク を参照してください。
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として使用してください。
error.networkResponse
は、デバイスにネットワーク接続がない場合はnull
になります(機内モードを有効にすることでこれを証明できます)。 Volleyライブラリの対応する code fragment を見てください。
エラーがNoConnectionError
のインスタンスである場合、networkResponse
を探す前に確認する必要があります。私は同意できません。401エラーはVolleyでサポートされていないので、テストし、401ステータスコードでnull以外のnetworkResponse
オブジェクトを取得しました。対応するコードを見てください here 。
ネットワーク応答は、次の形式で受信できます
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;
}
}
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
これは、エラーをチェックして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();
}
この問題を手動で処理します。
githubのVolleyライブラリ をダウンロードし、AndroidStudioプロジェクトに追加します
_com.Android.volley.toolbox.HurlStack
_クラスに移動します
performRequest
メソッド内のsetConnectionParametersForRequest(connection, request);
行を見つける
最後に、setConnectionParametersForRequest(connection, request);
行から次のコードを追加します。
_// for avoiding this exception : No authentication challenges found try { connection.getResponseCode(); } catch (IOException e) { e.printStackTrace(); }
_