提交请求后,无法执行此操作

时间:2014-07-25 19:51:42

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

以下代码使用 FSharp.Data Http 模块,在调用setProxy时在函数Http.RequestString中引发异常。在其他网站上使用query参数而不是body时,类似的代码也有效。什么可能导致问题? BTW,它在使用修改后的(用于分配代理)以前版本的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'类型的异常,但未在用户代码中处理

     

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

2 个答案:

答案 0 :(得分:4)

我认为这是 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. 通知图书馆的贡献者对此进行修复
  4. 注意:这可以在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();
    }
    

答案 1 :(得分:1)

这已在最新版本的F#Data

上修复