Android 6.0上的HttpURLConnnection请求失败(MarshMallow)

时间:2015-10-22 19:07:24

标签: android-volley httpurlconnection http-status-code-401 android-6.0-marshmallow

我正在使用Google的Volley库作为我的应用程序项目,目标是最低api级别14.因此,Volley库使用HttpURLConnection作为NetworkClient。

因此,不应该有任何与删除Apache HTTPClient相关的问题。但是,我已经完成了6.0 Sdk所需的配置,即compileSdkVersion 23,build-tool-version 23.0.1和build:gradle:1.3.1'甚至尝试添加useLibrary' org.apache.http.legacy'。在我的应用程序中为Volley库项目更新了相同内容。

最近,我尝试在Android 6.0(MarshMallow)上运行我的应用程序,我的项目编译并运行。但是那些需要身份验证标头的请求在MarshMallow上失败了:

  

BasicNetwork.performRequest:意外的响应代码401 com.android.volley.AuthFailureError

然而,在23以下的所有Api等级上运行相同。

我已多次检查过标题。但是,那些不需要身份验证的请求会给出200 OK的响应。

现在我完全不知道是什么打破了请求,是否有人知道新版本中HttpURLConnection请求仅在Api级别23失败了什么?是否有其他人使用Volley并面临类似的问题?

这是我的CustomRequest类

public class CustomRequest extends Request<Void> {

int id, cmd;
Map<String, String> params;
BaseModel model;

public CustomRequest(int method, int cmd, String url, Map<String, String> params, int id, BaseModel model) {
    super(method, url, null);
    this.id = id;
    this.cmd = cmd;
    this.params = params;
    this.model = model;

    if (method == Method.GET) {
        setUrl(buildUrlForGetRequest(url));
    }

    Log.v("Volley", "Making request to: " + getUrl());
}

private String buildUrlForGetRequest(String url) {
    if (params == null || params.size() == 0) return url;

    StringBuilder newUrl = new StringBuilder(url);
    Set<Entry<String, String>> paramPairs = params.entrySet();
    Iterator<Entry<String, String>> iter = paramPairs.iterator();

    newUrl.append("?");
    while (iter.hasNext()) {
        Entry<String, String> param = iter.next();
        newUrl
            .append(param.getKey())
            .append("=")
            .append(param.getValue());
        if (iter.hasNext()) newUrl.append("&");
    }

    return newUrl.toString();
}

@Override
public Map<String, String> getHeaders() throws AuthFailureError {
    Map<String, String> headers = new HashMap<String, String>();

    headers.put("X-Api-Version", Contract.API_VERSION);
    headers.put("X-Client", "android");
    String accessToken = APP.getInstance().getToken();
    if (!accessToken.equals("")) {
        headers.put("Authorization", "Bearer " + accessToken);    
    }

    return headers;
}

@Override
protected Response<Void> parseNetworkResponse(NetworkResponse response) {
    Exception ex;
    try {
        String jsonString = new String(
                response.data, HttpHeaderParser.parseCharset(response.headers));
        JsonNode json = new ObjectMapper().readTree(jsonString);
        if (model != null) model.parse(id, json);
        EventBus.getDefault().post(new EventResponse(cmd, true, null));
        return Response.success(null, HttpHeaderParser.parseCacheHeaders(response)); //Doesn't return anything. BaseModel.parse() does all the storage work.
    } catch (NoMoreDataException e) {
        ex = e;
        EventBus.getDefault().post(new NoMoreDataModel(cmd, e));
        EventBus.getDefault().post(new EventResponse(cmd, false, null));
        return Response.success(null, HttpHeaderParser.parseCacheHeaders(response));
    } catch (Exception e) {
        ex = e;
        Log.e("CustomRequest", Log.getStackTraceString(e));

        String message = APP.getInstance().getString(R.string.failedRequest);
        if (!TextUtils.isEmpty(e.getMessage()))
            message = e.getMessage();
        EventBus.getDefault().post(new ErrorEventModel(cmd, message, e));
        return Response.error(new ParseError(ex));
    }
}

@Override
protected Map<String, String> getParams() throws AuthFailureError {
    return params;
}

@Override
protected void deliverResponse(Void response) {
    Log.v("Volley", "Delivering result: " + getUrl());
}

@Override
public void deliverError(VolleyError error) {
    Log.e("CustomRequest", "Delivering error: Request=" + getUrl() + " | Error=" + error.toString());

    String message = APP.getInstance().getString(R.string.failedRequest);
    EventBus.getDefault().post(new ErrorEventModel(cmd, message, error));
}

}

我在Api 23和其他人之间找到的唯一区别是 HostNameVerifier 。 对于Api等级23:com.android.okhttp.internal.tls.OkHostnameVerifier 对于Api等级&lt; 23:javax.net.ssl.DefaultHostnameVerifier。

1 个答案:

答案 0 :(得分:1)

检查我的应用程序的服务器端后,找到问题背后的原因。 对于Android 6.0(MarshMallow),标题变为空白,而其他版本则不然。

所以对我有用的修复:

创建并添加了新的自定义标头 X-Proxy-No-Redirect =&gt; 1 并与其他标题一起传递。

背后的理论:

我们向其发送请求的API服务器,然后API服务器根据oAuth令牌将请求重定向到相应的站点 重定向时 为了实现重定向,有两种方法可以做到这一点

1 - 我们只是向呼叫者发回一条回复,说明要重定向到某个页面。然后调用者(网络库)负责重定向

2 - API服务器本身会发出重定向请求并获得响应,然后传递给调用者

早些时候我们使用方法1。

在Android 6.0上 - 网络库(Volley)在进行重定向请求时似乎没有设置所有标头。 一旦设置了这个新标题,方法2就会生效。

P.S 此修复程序适用于我的应用程序项目,可能不适用于其他人。只提供错误和帮助我的内容。