6 Stimmen

F# async Web-Anfrage, Behandlung von Ausnahmen

Ich versuche, asynchrone Workflows in F# zu verwenden, um mehrere Webanforderungen abzurufen.

Einige meiner Anfragen geben jedoch gelegentlich Fehler zurück (z. B. http 500), und ich weiß nicht, wie ich damit umgehen soll. Es scheint, wie mein F#-Programm in einer Endlosschleife stecken bleibt, wenn im Debugger ausgeführt.

Wahrscheinlich übersehe ich etwas, denn die Beispiele, die ich gesehen habe, ließen sich nicht sofort kompilieren. Das erste, was ich gefunden, die geholfen hat, war dieses Bit von Code:

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

und dann habe ich mein Stückchen Code, um die Anfragen zu holen, das ist ziemlich Standard von Beispielen, die ich gesehen habe:

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

und dann versuche ich, das Ergebnis zu erhalten:

let status = Async.RunSynchronously(async_value)

Aber wenn ich mein Programm im Debugger ausführe, bricht es bei req.EndGetResponse weil der Server einen internen Serverfehler 500 zurückgegeben hat. Wenn ich die Ausführung fortsetze, gerät sie in eine komische Schleife und bricht bei req.EndGetResponse (manchmal mehrere hintereinander), und bei let status = Async.RunSynchronously (async_value).

Wie kann ich das Ausnahmeproblem umgehen, damit ich meinen Statuscode bekomme? Brauche ich außerdem die oben beschriebene Typisierung? Oder vermisse ich einige Bibliothek/Dll für F#/VS 2010 Beta 1, von denen dies bereits ein Teil ist?

Ich lasse mehrere Anfragen parallel laufen, indem ich Async.RunSynchronously ( Async.Parallel (my_array_of_async_values)), obwohl ich nicht denke, dass ist im Zusammenhang mit der Ausnahme Problem ich habe.

Die Beispiele, die ich kenne, verwenden nur Async.Run statt Async.RunSynchronously ist wahrscheinlich ein Hinweis darauf, dass ich etwas übersehe... =/

3voto

Brian Punkte 115257

Sie heißt jetzt "AsyncGetResponse" (nicht mehr "GetResponseAsync"). Und "Run" wurde umbenannt in "RunSynchronously". Ich glaube also nicht, dass Sie hier etwas Wesentliches verpassen, sondern nur Namensänderungen in der neuesten Version.

Wie lauten Ihre Debugger-Einstellungen in Bezug auf "Tools \Options\Debugging\General\Enable Nur mein Code" und "Debuggen \Exceptions "(z. B. so eingestellt, dass es abbricht, wenn eine CLR-Ausnahme der ersten Chance ausgelöst wird oder nicht)? Mir ist nicht klar, ob sich Ihre Frage auf das Programmverhalten oder auf das Verhalten der VS-Tools bezieht (es klingt nach Letzterem). Dies ist weiter verwirrt durch die Tatsache, dass Haltepunkt/Debugging "Standorte" in F# Beta1 haben einige Bugs, vor allem in Bezug auf async-Workflows, was bedeutet, dass das Verhalten, das Sie im Debugger sehen ein wenig seltsam aussehen kann, auch wenn das Programm ordnungsgemäß ausgeführt wird...

Verwenden Sie VS2008 CTP oder VS2010 Beta1?

In jedem Fall scheint die Ausnahme aufgrund einer 500er-Antwort erwartet zu werden, so funktioniert WebRequest. Hier ist ein kurzes Demo-Programm:

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())

0 Stimmen

Ja, ich sehe ein, was ich falsch gemacht habe. Es war auch Teil Tooling Verhalten (vs2010b1) in Bezug auf die Ausnahme Debugging Seltsamkeit. Ich hatte versucht, nur den StatusCode, der einzige Teil der Antwort, die ich überhaupt interessiert, ohne Behandlung der Ausnahme zu erhalten.

1voto

Dmitry Lomov Punkte 1406

Sie können try...with innerhalb der async verwenden, um Ausnahmen abzufangen:

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

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X