HttpPost在Java项目中可行,但在Android上不起作用。

5
我已经为我的Android设备编写了一些代码,以通过HTTPS登录到网站并解析出一些数据。 首先进行HttpGet以获取登录所需的一些信息,然后进行HttpPost以进行实际的登录过程。
下面的代码在Eclipse中的Java项目中运行良好,该项目具有以下构建路径上的JAR文件:httpcore-4.1-beta2.jarhttpclient-4.1-alpha2.jarhttpmime-4.1-alpha2.jarcommons-logging-1.1.1.jar
public static MyBean gatherData(String username, String password) {
    MyBean myBean = new MyBean();
    try {
        HttpResponse response = doHttpGet(URL_PAGE_LOGIN, null, null);
        System.out.println("Got login page");
        String content = EntityUtils.toString(response.getEntity());
        String token = ContentParser.getToken(content);
        String cookie = getCookie(response);
        System.out.println("Performing login");
        System.out.println("token = "+token +" || cookie = "+cookie);
        response = doLoginPost(username,password,cookie, token);
        int respCode = response.getStatusLine().getStatusCode();
        if (respCode != 302) {
            System.out.println("ERROR: not a 302 redirect!: code is \""+ respCode+"\"");
            if (respCode == 200) {
                System.out.println(getHeaders(response));
                System.out.println(EntityUtils.toString(response.getEntity()).substring(0, 500));
            }
        } else {
            System.out.println("Logged in OK, loading account home");
            // redirect handler and rest of parse removed
        }
    }catch (Exception e) {
        System.out.println("ERROR in gatherdata: "+e.toString());
        e.printStackTrace();
    }
    return myBean;
}
private static HttpResponse doHttpGet(String url, String cookie, String referrer) {
    try {
        HttpClient client = new DefaultHttpClient();
        client.getParams().setParameter(CoreProtocolPNames.PROTOCOL_VERSION, HttpVersion.HTTP_1_1);
        client.getParams().setParameter(CoreProtocolPNames.HTTP_CONTENT_CHARSET, "UTF-8");
        HttpGet httpGet = new HttpGet(url);
        httpGet.getParams().setParameter(CoreProtocolPNames.PROTOCOL_VERSION, HttpVersion.HTTP_1_1);
        httpGet.setHeader(HEADER_USER_AGENT,HEADER_USER_AGENT_VALUE);
        if (referrer != null && !referrer.equals("")) httpGet.setHeader(HEADER_REFERER,referrer);
        if (cookie != null && !cookie.equals("")) httpGet.setHeader(HEADER_COOKIE,cookie);
        return client.execute(httpGet);
    } catch (Exception e) {
        e.printStackTrace();
        throw new ConnectException("Failed to read content from response");
    }
}
private static HttpResponse doLoginPost(String username, String password, String cookie, String token) throws ClientProtocolException, IOException {
    try {
        HttpClient client = new DefaultHttpClient();
        client.getParams().setParameter(CoreProtocolPNames.PROTOCOL_VERSION, HttpVersion.HTTP_1_1);
        client.getParams().setParameter(CoreProtocolPNames.HTTP_CONTENT_CHARSET, "UTF-8");
        HttpPost post = new HttpPost(URL_LOGIN_SUBMIT);
        post.getParams().setParameter(CoreProtocolPNames.PROTOCOL_VERSION, HttpVersion.HTTP_1_1);
        post.setHeader(HEADER_USER_AGENT,HEADER_USER_AGENT_VALUE);
        post.setHeader(HEADER_REFERER, URL_PAGE_LOGIN);
        post.setHeader(HEADER_COOKIE, cookie);
        post.setHeader("Content-Type","application/x-www-form-urlencoded");
        List<NameValuePair> formParams = new ArrayList<NameValuePair>();
        formParams.add(new BasicNameValuePair("org.apache.struts.taglib.html.TOKEN", token));
        formParams.add(new BasicNameValuePair("showLogin", "true"));
        formParams.add(new BasicNameValuePair("upgrade", ""));
        formParams.add(new BasicNameValuePair("username", username));
        formParams.add(new BasicNameValuePair("password", password));
        formParams.add(new BasicNameValuePair("submit", "Secure+Log+in"));
        UrlEncodedFormEntity entity = new UrlEncodedFormEntity(formParams,HTTP.UTF_8);
        post.setEntity(entity);
        return client.execute(post);
    } catch (Exception e) {
        e.printStackTrace();
        throw new ConnectException("ERROR in doLoginPost(): "+e.getMessage());
    }
}

当登录成功时,服务器(不在我的控制之下)返回302重定向,如果登录失败并重新加载登录页面,则返回200。使用上述JAR文件运行时,我得到了302重定向,但是如果我从Android项目中使用相同的代码并将1.6 Android JAR文件添加到构建路径中,我会从服务器得到200响应。在我的2.2设备上运行代码时,我也会得到相同的200响应。
我的Android应用程序具有Internet权限,并且HttpGet运作正常。我认为问题在于HttpPost(或其他类)在Android JAR版本和较新的Apache版本之间存在某些显着差异。
我已尝试将Apache库添加到Android项目的构建路径中,但由于重复的类,我收到像“INFO/dalvikvm(390): DexOpt: not resolving ambiguous class 'Lorg/apache/http/impl/client/DefaultHttpClient;'”这样的消息日志。我还尝试使用MultipartEntity而不是UrlEncodedFormEntity,但我得到了相同的200结果。
因此,我有几个问题:
  • 我能强制在Android下运行的代码使用新版的Apache库而不是Android版本吗?
  • 如果不能,有没有什么办法可以修改我的代码,让它能够与Android JAR文件一起工作?
  • 在Android中进行HttpPost的其他完全不同的方法还有吗?
  • 还有其他的想法吗?

我已经阅读了很多帖子和代码,但我并没有取得任何进展。

4个回答

6

我已经放弃使用HttpClient路线在Android上运行时从服务器获得预期响应的尝试。相反,我重新编写了上面的doPost方法,改用HttpsURLConnection。以下是新版本(可工作)的代码,希望对某些人有用。

private static LoginBean altPost(String username, String password, String cookie, String token){
    LoginBean loginBean = new LoginBean();
    HttpsURLConnection urlc = null;
    OutputStreamWriter out = null;
    DataOutputStream dataout = null;
    BufferedReader in = null;
    try {
        URL url = new URL(URL_LOGIN_SUBMIT);
        urlc = (HttpsURLConnection) url.openConnection();
        urlc.setRequestMethod("POST");
        urlc.setDoOutput(true);
        urlc.setDoInput(true);
        urlc.setUseCaches(false);
        urlc.setAllowUserInteraction(false);
        urlc.setRequestProperty(HEADER_USER_AGENT, HEADER_USER_AGENT_VALUE_FF);
        urlc.setRequestProperty("Cookie", cookie);
        urlc.setRequestProperty("Content-Type","application/x-www-form-urlencoded");
        String output = "org.apache.struts.taglib.html.TOKEN="+ URLEncoder.encode(token, HTTP.UTF_8)
                +"&showLogin=true&upgrade=&username="+ URLEncoder.encode(username, HTTP.UTF_8)
                +"&password="+ URLEncoder.encode(password, HTTP.UTF_8)+"&submit="
                +URLEncoder.encode("Secure+Log+in", HTTP.UTF_8);
        dataout = new DataOutputStream(urlc.getOutputStream());
        // perform POST operation
        dataout.writeBytes(output);
        // get response info
        loginBean.setResponseCode(urlc.getResponseCode());
        // get required headers
        String headerName = null;
        StringBuffer newCookie = new StringBuffer(100);
        String redirectLocation = "";
        for (int i=1; (headerName = urlc.getHeaderField(i)) != null;i++) {
            if (headerName.indexOf(COOKIE_VALUE_SESSION) > -1) {
                if (newCookie.length() > 0) {newCookie.append("; ");}
                newCookie.append(headerName);
            }
            if (headerName.indexOf(COOKIE_VALUE_AUTH) > -1) {
                if (newCookie.length() > 0) {newCookie.append("; ");}
                newCookie.append(headerName);
            }
            if (headerName.indexOf("https://") > -1) {
                redirectLocation = headerName;
            }
        }
        loginBean.setCookie(newCookie.toString());
        loginBean.setRedirectUrl(redirectLocation);

        in = new BufferedReader(new InputStreamReader(urlc.getInputStream()),8096);
        String response;
        // write html to System.out for debug
        while ((response = in.readLine()) != null) {
            System.out.println(response);
        }
        in.close();
    } catch (ProtocolException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (out != null) {
            try {
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if (in != null) {
            try {
                in.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    return loginBean;
}

我仍然不知道为什么HttpClient方式无法正常工作。

1
在Gingerbread及更高版本中,HttpURLConnection是首选。请考虑Apache HttpClient已被弃用。 - Elliott Hughes

1

0

这个网站是否可能进行了用户代理检测,并且因为是Android而实际返回了不同的结果?既然200表示成功,为什么必须返回302而不是200?你是否已经打印出它返回200时得到的结果,并且它是否提供了任何额外的信息?


200结果只是用户登录页面,没有错误消息或其他信息,这通常是登录不正确时发生的情况。我在标头中明确设置了用户代理,并且无论以哪种方式调用代码,它都设置相同。我可以从我的Android手机浏览器成功登录到该网站,这意味着它不会基于其为Android而阻止访问。 - dave.c

0

检查RedirectHandler,覆盖默认的处理程序并在其中进行一些日志记录,在转到Android时我遇到了一些问题...


我对重定向代码(上面没有包括)感到相当满意,问题是我甚至没有达到那一步。由于服务器从Android返回的是200代码而不是Eclipse返回的302代码,因此我无法进入需要处理重定向的部分。 - dave.c
嗯,可能是用户代理问题吗? - dacwe
以上代码中设置了用户代理,无论在Android内部运行还是作为纯Java运行,它都被设置为相同的值。我尝试过Android用户代理、FF/Windows用户代理和一些自定义用户代理,它们都表现出相同的行为。 - dave.c

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接