F# асинхронный веб-запрос, обработка исключений

Я пытаюсь использовать асинхронные рабочие процессы в F# для получения нескольких веб-запросов.

Однако, некоторые из моих запросов иногда возвращают ошибки (например, 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).

Как мне обойти проблему исключения, чтобы я мог получить свой код состояния? Кроме того, мне нужно то, что я сделал выше? Или мне не хватает библиотеки /dll для F#/VS 2010 Beta 1, частью которой она уже является?

Я фактически запускаю несколько запросов параллельно, используя Async.RunSynchronously(Async.Parallel(my_array_of_async_values)), хотя я не думаю, что это связано с проблемой исключения, которая у меня возникла.

Тот факт, что примеры, с которыми я сталкивался, используется Async.Run скорее, чем Async.RunSynchronously это, наверное, показатель, что я что-то упустил... =/

2 ответа

Решение

Теперь он называется "AsyncGetResponse" (больше не "GetResponseAsync"). И "Выполнить" было переименовано в "Выполнить синхронно". Так что я не думаю, что вы упускаете здесь что-то существенное, просто изменения имени в последней версии.

Каковы ваши настройки отладчика в отношении "Инструменты \ Параметры \ Отладка \ Общие \ Включить только мой код" и "Отладка \ Исключения" (например, установлен ли на разрыв, когда выбрасывается исключение 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())

Вы можете использовать try...with внутри async, чтобы перехватить исключения:

let async_value =
    async {
        let req = WebRequest.Create("http://unknown")
        try
            let! resp = req.AsyncGetResponse()
            return "success"
        with
        |   :? WebException as e -> return "failure"
    }
Другие вопросы по тегам