【问题标题】:This operation cannot be performed after the request has been submitted请求提交后无法执行此操作
【发布时间】:2014-07-25 19:51:41
【问题描述】:

以下代码使用 FSharp.DataHttp 模块,在调用 Http.RequestString 时在函数 setProxy 中引发异常。当在不同的网站上使用 query 参数而不是 body 时,类似的代码有效。什么会导致问题? 顺便说一句,它在使用经过修改(用于分配代理)以前版本的 Http 模块时有效。

let headers = [ UserAgent ConfigurationManager.AppSettings.["UserAgent"] ]
let setProxy (req: HttpWebRequest) = 
    req.Proxy <- WebProxy(ConfigurationManager.AppSettings.["Proxy"], true)
    req // Error here
let cc = CookieContainer()

let body = HttpRequestBody.FormValues [
             "username", user; 
             "password", password; 
             "nothing.x", "21"; "nothing.y", "34"]
let html =
    Http.RequestString("https://..../CheckLoginServlet", 
        httpMethod = "POST", 
        body = body, cookieContainer = cc, headers = headers, 
        customizeHttpRequest = setProxy )

例外:

System.dll 中出现“System.InvalidOperationException”类型的异常,但未在用户代码中处理

补充信息:请求提交后无法执行此操作。

【问题讨论】:

  • 为什么这个标签是C#

标签: .net http f# f#-data


【解决方案1】:

我相信这是 FSharp.Data 库中的一个错误。查看它的source code on GitHub,我们可以提取我们感兴趣的代码,例如:

let values = [ "id", "2"; ]
let req = WebRequest.Create("https://..../CheckLoginServlet") :?> HttpWebRequest
req.Method <- HttpMethod.Post
req.UserAgent <- "Magic"
req.AutomaticDecompression <- DecompressionMethods.GZip ||| DecompressionMethods.Deflate
let cookieContainer = new CookieContainer()
req.CookieContainer <- cookieContainer
req.ContentType <- "application/x-www-form-urlencoded"
let bytes = [ for k, v in values -> Uri.EscapeDataString k + "=" + Uri.EscapeDataString v ]
                    |> String.concat "&"
                    |> HttpEncodings.PostDefaultEncoding.GetBytes
let f = fun () -> async {
    do! writeBody req bytes
    let req = customizeHttpRequest req
    let! resp = Async.FromBeginEnd(req.BeginGetResponse , req.EndGetResponse)
    let stream = resp.GetResponseStream()
    return! toHttpResponse
}
f()

问题在于writeBody调用之前调用customizeHttpRequest,而它的代码是:

let writeBody (req:HttpWebRequest) (postBytes:byte[]) = async { 
    req.ContentLength <- int64 postBytes.Length
    use! output = Async.FromBeginEnd(req.BeginGetRequestStream, req.EndGetRequestStream)
    do! output.AsyncWrite(postBytes, 0, postBytes.Length)
    output.Flush()
}

其中Async.FromBeginEnd(req.BeginGetRequestStream, req.EndGetRequestStream) 更改HttpWebRequest.m_RequestSubmitted 标志,然后由HttpWebRequest.Proxy 的属性设置器(如许多其他)检查。我的建议:

  1. 直接使用HttpWebRequest 编写您自己的方法,
  2. 编译您自己的 FSharp.Data 版本,更改 do! writeBody req byteslet req = customizeHttpRequest req 调用的顺序
  3. 通知库的贡献者对此进行修复

注意:这可以很容易地在 C# 示例程序中重现:

private static async void GetRequestStreamCallback(IAsyncResult ar)
{
    HttpWebRequest req = (HttpWebRequest)ar.AsyncState; 
    Stream postStream = req.EndGetRequestStream(ar); // Here flag req.m_RequestSubmitted is true already
    byte[] byteArray = Encoding.UTF8.GetBytes("id=1");
    await postStream.WriteAsync(byteArray, 0, byteArray.Length);
    postStream.Close();
    req.Proxy = new WebProxy("localhost:8888", true);
    allDone.WaitOne();
}
private static ManualResetEvent allDone = new ManualResetEvent(false);
static void Main(string[] args)
{
    HttpWebRequest req = (HttpWebRequest)WebRequest.Create("http://localhost:64343/api/products");
    req.Method = "POST";
    req.UserAgent = "Magic";
    req.AutomaticDecompression = DecompressionMethods.GZip;
    req.CookieContainer = new CookieContainer();
    req.ContentType = "application/x-www-form-urlencoded";
    var bytes = Encoding.UTF8.GetBytes("id=2");
    req.ContentLength = bytes.Length;
    req.BeginGetRequestStream(new AsyncCallback(GetRequestStreamCallback), req);
    allDone.WaitOne();
}

【讨论】:

    【解决方案2】:

    此问题已在最新版本的 F# Data 中得到修复

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-12-23
      • 2016-02-08
      • 2020-08-16
      • 2023-03-03
      • 2019-10-01
      • 2021-03-28
      相关资源
      最近更新 更多