Получить асинхронный HttpResponse через Silverlight (F #) - PullRequest
2 голосов
/ 09 июня 2010

Я новичок в F # и SL и играю с получением асинхронного HttpResponse через Silverlight.Ниже приведены фрагменты кода F #, которые протестированы на VS2010 и Window7 и работают хорошо, но необходимо улучшить.Любые советы и обсуждения, особенно часть callback , приветствуются и большое спасибо.

module JSONExample
open System
open System.IO 
open System.Net 
open System.Text 
open System.Web 
open System.Security.Authentication 
open System.Runtime.Serialization 


[<DataContract>] 
type Result<'TResult> = { 
    [<field: DataMember(Name="code") >] 
    Code:string 
    [<field: DataMember(Name="result") >] 
    Result:'TResult array
    [<field: DataMember(Name="message") >] 
    Message:string 
    } 

// The elements in the list
[<DataContract>] 
type ChemicalElement = { 
    [<field: DataMember(Name="name") >] 
    Name:string 
    [<field: DataMember(Name="boiling_point") >] 
    BoilingPoint:string 
    [<field: DataMember(Name="atomic_mass") >] 
    AtomicMass:string 
} 



//http://blogs.msdn.com/b/dsyme/archive/2007/10/11/introducing-f-asynchronous-workflows.aspx
//http://lorgonblog.spaces.live.com/blog/cns!701679AD17B6D310!194.entry
type System.Net.HttpWebRequest with
    member x.GetResponseAsync() =
        Async.FromBeginEnd(x.BeginGetResponse, x.EndGetResponse)


type RequestState () = 
    let mutable request : WebRequest = null
    let mutable response : WebResponse = null
    let mutable responseStream : Stream = null
    member this.Request with get() = request and set v = request <- v
    member this.Response with get() = response and set v = response <- v
    member this.ResponseStream with get() = responseStream and set v = responseStream <- v

let allDone = new System.Threading.ManualResetEvent(false)


let getHttpWebRequest (query:string) = 
    let query = query.Replace("'","\"") 
    let queryUrl = sprintf "http://api.freebase.com/api/service/mqlread?query=%s" "{\"query\":"+query+"}" 

    let request : HttpWebRequest = downcast WebRequest.Create(queryUrl) 
    request.Method <- "GET" 
    request.ContentType <- "application/x-www-form-urlencoded" 
    request


let GetAsynResp (request : HttpWebRequest) (callback: AsyncCallback) = 
    let myRequestState = new RequestState()
    myRequestState.Request <- request
    let asyncResult = request.BeginGetResponse(callback, myRequestState)
    ()


// easy way to get it to run syncrnously w/ the asynch methods
let GetSynResp (request : HttpWebRequest) : HttpWebResponse  =      
    let response = request.GetResponseAsync() |> Async.RunSynchronously  
    downcast response

let RespCallback (finish: Stream -> _) (asynchronousResult : IAsyncResult) =
        try
            let myRequestState : RequestState = downcast asynchronousResult.AsyncState 
            let myWebRequest1 : WebRequest = myRequestState.Request
            myRequestState.Response <- myWebRequest1.EndGetResponse(asynchronousResult)
            let responseStream = myRequestState.Response.GetResponseStream()
            myRequestState.ResponseStream <- responseStream
            finish responseStream
            myRequestState.Response.Close() 
            ()
        with 
        | :? WebException as e
            -> printfn "WebException raised!"
               printfn "\n%s" e.Message
               printfn "\n%s" (e.Status.ToString())
               ()
        | _ as e
            -> printfn "Exception raised!"
               printfn "Source : %s" e.Source
               printfn "Message : %s" e.Message
               ()

let printResults (stream: Stream)= 
    let result = 
        try 
            use reader = new StreamReader(stream) 
            reader.ReadToEnd(); 
        finally 
            ()

    let data = Encoding.Unicode.GetBytes(result); 
    let stream = new MemoryStream() 
    stream.Write(data, 0, data.Length); 
    stream.Position <- 0L 

    let JsonSerializer = Json.DataContractJsonSerializer(typeof<Result<ChemicalElement>>) 
    let result = JsonSerializer.ReadObject(stream) :?> Result<ChemicalElement> 

    if result.Code<>"/api/status/ok" then 
        raise (InvalidOperationException(result.Message)) 
    else 
        result.Result |> Array.iter(fun element->printfn "%A" element) 

let test =
    // Call Query (w/ generics telling it you wand an array of ChemicalElement back, the query string is wackyJSON too –I didn’t build it don’t ask me!
    let request = getHttpWebRequest "[{'type':'/chemistry/chemical_element','name':null,'boiling_point':null,'atomic_mass':null}]"
    //let response = GetSynResp request 
    let response = GetAsynResp request (AsyncCallback (RespCallback printResults))
    () 

ignore(test)
System.Console.ReadLine() |> ignore

1 Ответ

2 голосов
/ 09 июня 2010

Весь смысл асинхронности в том, что вам не нужно иметь дело с состоянием и IAsyncResult, а также с обратными вызовами и так далее.Ниже приведена несколько исправленная версия вашего кода ...

open System 
open System.IO  
open System.Net  
open System.Text  
open System.Web  
open System.Security.Authentication  
open System.Runtime.Serialization  

[<DataContract>]  
type Result<'TResult> = {  
    [<field: DataMember(Name="code") >]  
    Code:string  
    [<field: DataMember(Name="result") >]  
    Result:'TResult array 
    [<field: DataMember(Name="message") >]  
    Message:string  
    }  

// The elements in the list 
[<DataContract>]  
type ChemicalElement = {  
    [<field: DataMember(Name="name") >]  
    Name:string  
    [<field: DataMember(Name="boiling_point") >]  
    BoilingPoint:string  
    [<field: DataMember(Name="atomic_mass") >]  
    AtomicMass:string  
}  

//http://blogs.msdn.com/b/dsyme/archive/2007/10/11/introducing-f-asynchronous-workflows.aspx 
//http://lorgonblog.spaces.live.com/blog/cns!701679AD17B6D310!194.entry 
type System.Net.HttpWebRequest with 
    member x.GetResponseAsync() = 
        Async.FromBeginEnd(x.BeginGetResponse, x.EndGetResponse) 

let getHttpWebRequest (query:string) =  
    let query = query.Replace("'","\"")  
    let queryUrl = sprintf "http://api.freebase.com/api/service/mqlread?query=%s" "{\"query\":"+query+"}"  

    let request : HttpWebRequest = downcast WebRequest.Create(queryUrl)  
    request.Method <- "GET"  
    request.ContentType <- "application/x-www-form-urlencoded"  
    request 

let printResults (stream: Stream)=  
    let result =  
        try  
            use reader = new StreamReader(stream)  
            reader.ReadToEnd();  
        finally  
            () 

    let data = Encoding.Unicode.GetBytes(result);  
    let stream = new MemoryStream()  
    stream.Write(data, 0, data.Length);  
    stream.Position <- 0L  

    let JsonSerializer = Json.DataContractJsonSerializer(typeof<Result<ChemicalElement>>)  
    let result = JsonSerializer.ReadObject(stream) :?> Result<ChemicalElement>  

    if result.Code<>"/api/status/ok" then  
        raise (InvalidOperationException(result.Message))  
    else  
        result.Result |> Array.iter(fun element->printfn "%A" element)  

let test = async {
    // Call Query (w/ generics telling it you wand an array of ChemicalElement back, the query string is wackyJSON too –I didn’t build it don’t ask me! 
    let request = getHttpWebRequest "[{'type':'/chemistry/chemical_element','name':null,'boiling_point':null,'atomic_mass':null}]" 
    try 
        use! response = request.AsyncGetResponse()
        use responseStream = response.GetResponseStream() 
        printResults responseStream 
    with  
    | :? WebException as e 
        ->  printfn "WebException raised!" 
            printfn "\n%s" e.Message 
            printfn "\n%s" (e.Status.ToString()) 
    | _ as e 
        ->  printfn "Exception raised!" 
            printfn "Source : %s" e.Source 
            printfn "Message : %s" e.Message 
}

test |> Async.RunSynchronously 
System.Console.ReadLine() |> ignore 
...