web-dev-qa-db-ja.com

volleyの応答文字列で、応答ヘッダーとは異なるエンコードが使用されるのはなぜですか?

ボレーリクエスト(StringRequestまたはJsonObjectRequest)を実行する場合、OkHttpスタックを使用して、応答文字列のエンコーディングは、デフォルトのエンコーディングであるISO-8995-1に設定されます。応答にはヘッダーがあります:content-type=text/html; charset=utf-8、これを検出する必要があります。なぜですか?

18
mjibson

これらの要求タイプは両方とも、ヘッダーから文字セットを判別できるHttpHeaderParser.parseCharsetを呼び出します。ただし、ヘッダーはContent-Typeではなくcontent-typeである必要があります。大文字と小文字が区別されます。 (デフォルトのHurlStackを使用している場合の動作はわかりません。これがOkHttpスタックとの実装の詳細の違いである可能性があります。)

解決策1:元の要求タイプをコピーするが、手動で文字セットをオーバーライドする

解決策2:元のリクエストタイプをコピーするが、予期されるヘッダーを強制的に存在させる

import com.Android.volley.NetworkResponse;
import com.Android.volley.ParseError;
import com.Android.volley.Response;
import com.Android.volley.Response.ErrorListener;
import com.Android.volley.Response.Listener;
import com.Android.volley.toolbox.HttpHeaderParser;
import com.Android.volley.toolbox.JsonRequest;

import org.json.JSONException;
import org.json.JSONObject;

import Java.io.UnsupportedEncodingException;

public class JsonUTF8Request extends JsonRequest<JSONObject> {
    public JsonUTF8Request(int method, String url, JSONObject jsonRequest,
                           Listener<JSONObject> listener, ErrorListener errorListener) {
        super(method, url, (jsonRequest == null) ? null : jsonRequest.toString(), listener,
                errorListener);
    }

    @Override
    protected Response<JSONObject> parseNetworkResponse(NetworkResponse response) {
        try {
            // solution 1:
            String jsonString = new String(response.data, "UTF-8");
            // solution 2:
            response.headers.put(HTTP.CONTENT_TYPE,
                response.headers.get("content-type"));
            String jsonString = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
            //
            return Response.success(new JSONObject(jsonString),
                    HttpHeaderParser.parseCacheHeaders(response));
        } catch (UnsupportedEncodingException e) {
            return Response.error(new ParseError(e));
        } catch (JSONException je) {
            return Response.error(new ParseError(je));
        }
    }
}
34
mjibson

ここに投稿した2つのソリューションについて@mjibsonに感謝します。同様の問題がありました。私の場合、コンテンツタイプが常に見つからないため、次のようにしました。

    protected static final String TYPE_UTF8_CHARSET = "charset=UTF-8";

    @Override
    protected Response<String> parseNetworkResponse(
            NetworkResponse response) {
        try {
            String type = response.headers.get(HTTP.CONTENT_TYPE);
            if (type == null) {
                Log.d(LOG_TAG, "content type was null");
                type = TYPE_UTF8_CHARSET;
                response.headers.put(HTTP.CONTENT_TYPE, type);
            } else if (!type.contains("UTF-8")) {
                Log.d(LOG_TAG, "content type had UTF-8 missing");
                type += ";" + TYPE_UTF8_CHARSET;
                response.headers.put(HTTP.CONTENT_TYPE, type);
            }
        } catch (Exception e) {
            //print stacktrace e.g.
        }
        return super.parseNetworkResponse(response);
    }

他の人が同様の問題に遭遇するためにこれを共有したかっただけです。 https://Android.googlesource.com/platform/frameworks/volley/+/master/src/com/Android/volley/toolbox/HttpHeaderParser.Java のparseCharsetメソッドを読むことも重要ですこれが機能する理由を理解する

12
Simon Heinen

メソッドをGETからPOST UTF-8サポート用に変更します。

JsonObjectRequest jsonReq = new JsonObjectRequest(Method.POST,
            URL_FEED, null, new Response.Listener<JSONObject>() {
                @Override
                public void onResponse(JSONObject response) {
                    VolleyLog.d(TAG, "Response: " + response.toString());
                    Log.d("SHY", "Response: " + response.toString());
                    if (response != null) {
                        parseJsonFeed(response);
                    }
                }
            }, new Response.ErrorListener() {
                @Override
                public void onErrorResponse(VolleyError error) {
                    VolleyLog.d(TAG, "Error: " + error.getMessage());
                }
            });

。 。 。 。

3
Suyog Gunjal

Request<T>クラスのメソッドparseNetworkResponseをオーバーライドします。
あなたはこのようにすることができます:

/**
 * A canned request for retrieving the response body at a given URL as a String.
 */
public class StringRequest extends Request<String> {
    private final Listener<String> mListener;


    /**
     * the parse charset.
     */
    private String charset = null;

    /**
     * Creates a new request with the given method.
     *
     * @param method the request {@link Method} to use
     * @param url URL to fetch the string at
     * @param listener Listener to receive the String response
     * @param errorListener Error listener, or null to ignore errors
     */
    public StringRequest(int method, String url, Listener<String> listener,
            ErrorListener errorListener) {
        super(method, url, errorListener);
        mListener = listener;
    }

    /**
     * Creates a new GET request.
     *
     * @param url URL to fetch the string at
     * @param listener Listener to receive the String response
     * @param errorListener Error listener, or null to ignore errors
     */
    public StringRequest(String url, Listener<String> listener, ErrorListener errorListener) {
        this(Method.GET, url, listener, errorListener);
    }

    /**
     * Creates a new GET request with the given Charset.
     *
     * @param url URL to fetch the string at
     * @param listener Listener to receive the String response
     * @param errorListener Error listener, or null to ignore errors
     */
    public StringRequest(String url, String charset, Listener<String> listener, ErrorListener errorListener) {
        this(Method.GET, url, listener, errorListener);
        this.charset = charset;
    }

    @Override
    protected void deliverResponse(String response) {
        mListener.onResponse(response);
    }

    @Override
    protected Response<String> parseNetworkResponse(NetworkResponse response) {
        String parsed;
        try {
            if(charset != null) {
                parsed = new String(response.data, charset);
            } else {
                parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
            }
        } catch (UnsupportedEncodingException e) {
            parsed = new String(response.data);
        }
        return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response));
    }

    /**
     * @return the Parse Charset Encoding
     */
    public String getCharset() {
        return charset;
    }

    /**
     * set the Parse Charset Encoding
     * @param charset
     */
    public void setCharset(String charset) {
        this.charset = charset;
    }

}
3
Ligboy

@Simon Heinen、ありがとうございます。あなたの返事に基づいて、私は関数を書きました。

private void addEncodeing2Request(NetworkResponse response) {
    try {
        String type = response.headers.get(HTTP.CONTENT_TYPE);
        if (type == null) {
            //Content-Type:
            Log.d("RVA", "content type was null");
            type = TYPE_UTF8_CHARSET;
            response.headers.put(HTTP.CONTENT_TYPE, type);
        } else if (!type.contains("charset")) {
            //Content-Type: text/plain;
            Log.d("RVA", "charset was null, added encode utf-8");
            type += ";" + TYPE_UTF8_CHARSET;
            response.headers.put(HTTP.CONTENT_TYPE, type);
        } else {
            //Nice! Content-Type: text/plain; charset=utf-8'
            Log.d("RVA", "charset is " + type);
        }

    } catch (Exception e) {
        e.printStackTrace();
    }
}

使用法:

protected Response<String> parseNetworkResponse(NetworkResponse response) {
          addEncodeing2Request(response);
          return super.parseNetworkResponse(response);
      }

さらに、オーバーライドgetParamsEncoding()も機能する場合があります。

protected String getParamsEncoding() {
            return "utf-8";
        }
1
CoderYel