【问题标题】:Downloading a form-generated file programmatically以编程方式下载表单生成的文件
【发布时间】:2011-11-27 01:23:23
【问题描述】:

我正在尝试以编程方式下载从网站上的用户输入(HTML 表单)生成的文件。该网站要求用户登录。

我研究了网站如何使用 Tamper Data Firefox 插件工作,这基本上就是在 Firefox 中手动下载时发生的情况:

  • 我转到该网站的主页
  • 我输入我的凭据并点击“登录”
  • 这会触发对 URL A 的 POST 请求并设置“IdSes”(我猜是会话 ID)cookie(以及其他)
  • 我转到让我下载文件的页面
  • 我输入了一些东西(文件格式等)
  • 我点击“下载”
  • 这会触发对 URL B(一个 ASP 页面)的 POST 请求。响应代码为 200 并包含文件。

通过篡改对 URL B 的 POST 请求,我确保了 IdSes cookie 是请求成功所需的唯一一个。如果 IdSes 值不正确或不存在,则响应代码为 302,并且我被重定向到 URL A,并且我似乎仍在登录(显示我的姓名)。如果我在 POST 数据(最初来自表单)中放入垃圾,我会进入正确的 ASP 页面,但会显示错误。

现在我尝试使用 C# 进行下载,而不做我认为不必要的事情(即所有 GET):

// URL A is loginUrl, URL B is retrieveUrl

public void RetrieveFile(
        string loginUrl, IDictionary<string, string> loginData, 
        string retrieveUrl, IDictionary<string, string> retrieveData) {
    var cookies = new CookieContainer();
    var loginRequest = CreatePostRequest(loginUrl, loginData);
    loginRequest.CookieContainer = cookies;
    var loginResponse = loginRequest.GetResponse();
    loginResponse.Close();
    var retrieveRequest = CreatePostRequest(retrieveUrl, retrieveData);
    retrieveRequest.CookieContainer = cookies;
    var response = retrieveRequest.GetResponse();
    using (
            Stream responseStream = response.GetResponseStream(),
            outputFile = new FileStream("response.html", FileMode.Create)) {
        responseStream.CopyTo(outputFile);
    }
}

private HttpWebRequest CreatePostRequest(string url, IDictionary<string, string> data) {
    var request = (HttpWebRequest)WebRequest.Create(url);
    request.KeepAlive = true;
    request.Method = "POST";
    request.ContentType = "application/x-www-form-urlencoded";
    var postData = EncodePostData(data);
    request.ContentLength = postData.Length;
    return request;
}

private byte[] EncodePostData(IDictionary<string, string> data) {
    var dataAsStrings =
        from entry in data
        select String.Format("{0}={1}", entry.Key, entry.Value);
    var dataAsString = String.Join("&", dataAsStrings);
    // Encode in Latin-1 / ISO 8859-1
    var dataAsBytes = Encoding.GetEncoding(1252).GetBytes(dataAsString);
    return dataAsBytes;
}

发生的情况是“response.html”包含位于 URL A 的页面,就像 IdSes cookie 的值错误或不存在一样。但是,如果我在loginRequest.GetResponse() 之后打印cookies 中存在的所有cookie,则存在IdSes。我做错了吗?

【问题讨论】:

  • 可能不正确,但为什么不尝试在第一次调用 GetResponse 后关闭响应?如果我不这样做,我已经看到不守规矩的事情发生了,出于某种原因,这可能是相关的。不过我可能错了。
  • 哦,我不知道如果我不使用它的流,我必须关闭它。然而它并没有解决问题。我将编辑我的代码以反映更改。感谢您指出这一点。
  • 因为是 POST 请求,我想知道网站是否验证 REFERER 标头,如果不在同一个域(或为空!),则基本上不会将您的请求视为有效,导致您看到的行为。现在,只要它是一个 GET 请求,你就会很成功。

标签: c# httprequest


【解决方案1】:

因为它是一个 POST 请求,所以我想知道网站是否验证了 HTTP_REFERER 标头,如果不在同一个域上(或为空!),则基本上不会将您的请求视为有效,从而导致你看到的行为。

您可以尝试在您的检索请求中欺骗 HTTP_REFERER 标头,看看会发生什么。

retrieveRequest.Referer = "http://www.originatingdomain.com";

【讨论】:

  • 感谢您的回复。虽然已经很久了,但我已经没有代码了,甚至没有那个特定网站上的帐户。此后,该网站可能已完全重做。所以我无法测试你的建议。还是谢谢你。 :)
  • 不用担心,我在回复后才注意到问题的年龄。它在未回答的列表中排名很高! :-)
【解决方案2】:

这看起来不像是将最初从 loginRequest 检索到的 cookie 传递给 retrieveRequest 处理程序。

改变

retrieveRequest.CookieContainer = cookies;

retrieveRequest.CookieContainer = loginRequest.CookieContainer;

看看会发生什么。

【讨论】:

  • CookieContainer 是一个类,所以只有引用被传递。都是同一个对象。无论如何我都试过了,只是为了确定,它不会改变结果。
猜你喜欢
  • 1970-01-01
  • 2012-01-10
  • 2018-01-29
  • 2014-08-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-24
  • 1970-01-01
相关资源
最近更新 更多