F#异步Web请求,处理异常

时间:2009-08-25 02:20:28

标签: f# async-workflow

我正在尝试在F#中使用异步工作流来获取多个Web请求。

但是,我的一些请求偶尔会返回错误(例如http 500),我不知道如何处理这个错误。看起来我的F#程序在调试器中运行时会陷入无限循环。

我可能错过了一些东西,因为我看到的例子没有开箱即用。我找到的第一件事就是这段代码:

type System.Net.WebRequest with
  member req.GetResponseAsync() =
    Async.BuildPrimitive(req.BeginGetResponse, req.EndGetResponse)

然后我有了一些代码来获取请求,这与我见过的例子相当标准:

let async_value = async {
  let req = WebRequest.Create(url)
  let! rsp = req.GetResponseAsync()
  return (rsp :?> HttpWebResponse).StatusCode
}

然后我尝试得到结果:

let status = Async.RunSynchronously(async_value)

但是当我在调试器中运行我的程序时,它在req.EndGetResponse处中断,因为服务器返回内部服务器错误500.如果我继续执行,它会进入一个时髦的循环,在req.EndGetResponse处断开(有时几个连续),并在status = Async.RunSynchronously(async_value)。

如何解决异常问题,以便获取状态代码?另外,我需要上面做的类型吗?或者我错过了F#/ VS 2010 Beta 1的一些库/ dll,这已经是其中的一部分了?

我实际上使用Async.RunSynchronouslyAsync.Parallel(my_array_of_async_values))并行运行多个请求,但我认为这与我遇到的异常问题无关。

我遇到的例子只使用Async.Run而不是Async.RunSynchronously,这可能是我错过的一个指标... = /

2 个答案:

答案 0 :(得分:3)

它现在被称为'AsyncGetResponse'(不再是'GetResponseAsync')。并且'Run'被重命名为'RunSynchronously'。所以我认为你在这里没有遗漏任何重大内容,只是在最新版本中命名更改。

关于“Tools \ Options \ Debugging \ General \ Enable Just My Code”和“Debug \ Exceptions”的调试器设置是什么(例如,在抛出任何第一次机会CLR异常时设置为中断)?我不清楚你的问题是否涉及程序行为或VS工具行为(听起来像后者)。 F#Beta1中的断点/调试“位置”存在一些错误,特别是对于异步工作流,这进一步使这一点更加困惑,这意味着即使程序正常执行,您在调试器中看到的行为可能看起来有点奇怪。

您使用的是VS2008 CTP还是VS2010 Beta1?

在任何情况下,由于预期会产生500响应,因此出现异常,这就是WebRequest的工作原理。这是一个简短的演示程序:

open System
open System.ServiceModel 
open System.ServiceModel.Web 

[<ServiceContract>]
type IMyContract =
    [<OperationContract>]
    [<WebGet(UriTemplate="/Returns500")>]
    abstract Returns500 : unit -> unit
    [<OperationContract>]
    [<WebGet(UriTemplate="/Returns201")>]
    abstract Returns201 : unit -> unit

type MyService() =
    interface IMyContract with
        member this.Returns500() =
            WebOperationContext.Current.OutgoingResponse.StatusCode <- 
                System.Net.HttpStatusCode.InternalServerError 
        member this.Returns201() =
            WebOperationContext.Current.OutgoingResponse.StatusCode <- 
                System.Net.HttpStatusCode.Created 

let addr = "http://localhost/MyService"
let host = new WebServiceHost(typeof<MyService>, new Uri(addr))
host.AddServiceEndpoint(typeof<IMyContract>, new WebHttpBinding(), "") |> ignore
host.Open()

open System.Net

let url500 = "http://localhost/MyService/Returns500"
let url201 = "http://localhost/MyService/Returns201"
let async_value (url:string) = 
    async {  
        let req = WebRequest.Create(url)  
        let! rsp = req.AsyncGetResponse()  
        return (rsp :?> HttpWebResponse).StatusCode
    }
let status = Async.RunSynchronously(async_value url201)
printfn "%A" status
try
    let status = Async.RunSynchronously(async_value url500)
    printfn "%A" status
with e ->
    printfn "%s" (e.ToString())

答案 1 :(得分:1)

您可以在异步内部使用try ...来捕获异常:

let async_value =
    async {
        let req = WebRequest.Create("http://unknown")
        try
            let! resp = req.AsyncGetResponse()
            return "success"
        with
        |   :? WebException as e -> return "failure"
    }