如何在Silverlight中创建异步HttpWebRequest(F#)

J.K*_*K.J 6 silverlight f# asynchronous httpwebrequest

正如我所提到的,因为Silverlight HttpWebRequest.Create在异步块内部挂起,我只是创建了一组回调函数来实现相同的异步块.

登录过程需要两个步骤:

1)获取返回cookie的页面的请求2)表单发布到第二页,通过该cookie并执行身份验证

以下是src.无论是关于异步HttpWebRequest还是关于F#代码样式,欢迎和赞赏任何建议和讨论.

module File1

open System
open System.IO
open System.Net
open System.Text
open System.Security
open System.Runtime.Serialization
open System.Collections.Generic 
open JsonData
open System.Net.Browser
open System.Threading


module rpc = 
    let mutable BASE_DNS = ""

    let mutable requestId : int = 0
    let getId() = 
        requestId <- requestId +  1
        requestId.ToString()

    module internal Helper = 
        ///<Summary>
        ///Transfer data from Security.loginToRpc to Helper.FetchCookieCallback
        ///</Summary>
        type LoginRequestRecord = {
                Request : HttpWebRequest;
                UserName : string;
                Password : string;
                AuthenticationUrl : string;
                CallbackUI  : (bool -> unit)
                }

        ///<Summary>
        ///Transfer data from Helper.FetchCookieCallback to Helper.requestAuthenticationCallback
        ///</Summary>
        type AuthenticationRecord = {
                Request : HttpWebRequest;
                UserName : string;
                Password : string;
                CallbackUI  : (bool -> unit)
                }

        ///<Summary>
        ///Transfer data from Helper.requestAuthenticationCallback to Helper.responseAuthenticationCallback
        ///</Summary>
        type ResponseAuthenticationRecord = {
                Request : HttpWebRequest;
                CallbackUI  : (bool -> unit)
                }

        ///<Summary>
        ///The cookieContainer for all the requests in the session
        ///</Summary>
        let mutable cookieJar = new CookieContainer()

        ///<summary>
        ///Function: Create HttpRequest
        ///Param: string
        ///Return: HttpWebRequest  
        ///</summary>
        let internal createHttpRequest  (queryUrl : string) =
            let uri = new Uri(queryUrl)
            let request : HttpWebRequest = 
                downcast WebRequestCreator.ClientHttp.Create(
                    new Uri(queryUrl, UriKind.Absolute))
            request

        ///<summary>
        ///Function: set request whose method is "GET".
        ///Attention: no contentType for "GET" request~!!!!!!!!!!!!!!!!
        ///Param: HttpWebRequest
        ///Return: unit  
        ///</summary>
        let internal requestGetSet (request : HttpWebRequest) =
            request.Method <- "GET"

        ///<summary>
        ///Function: set request whose method is "POST" and its contentType
        ///Param: HttpWebRequest and contentType string
        ///Return: unit  
        ///</summary>
        let internal requestPostSet (request : HttpWebRequest) contentType = 
            request.Method <- "POST"
            request.ContentType <- contentType 

        ///<summary>
        ///Function: Callback function inluding EndGetResponse method of request
        ///Param: IAsyncResult includes the information of HttpWebRequest
        ///Return: unit
        ///</summary>
        let internal responseAuthenticationCallback (ar : IAsyncResult) =
            let responseAuthentication : ResponseAuthenticationRecord
                    = downcast ar.AsyncState
            try 
                let response = responseAuthentication.Request.EndGetResponse(ar)
                //check whether the authentication is successful,
                //which may be changed later into other methods
                match response.ContentLength with
                    | -1L -> responseAuthentication.CallbackUI true
                    | _ -> responseAuthentication.CallbackUI false
                ()
            with
                | Ex -> responseAuthentication.CallbackUI false

        ///<summary>
        ///Function: Callback function for user to log into the website
        ///Param: IAsyncResult includes the information of
        ///HttpWebRequest and user's identity
        ///Return: unit  
        ///</summary>
        let internal requestAuthenticationCallback (ar : IAsyncResult) = 
            let authentication : AuthenticationRecord = downcast ar.AsyncState
            try
                let requestStream = authentication.Request.EndGetRequestStream(ar)
                let streamWriter = new StreamWriter(requestStream)
                streamWriter.Write(
                    String.Format(
                        "j_username={0}&j_password={1}&login={2}", 
                        authentication.UserName, 
                        authentication.Password, 
                        "Login"))
                streamWriter.Close()
                let responseAuthentication = {
                    ResponseAuthenticationRecord.Request    = authentication.Request
                    ResponseAuthenticationRecord.CallbackUI = authentication.CallbackUI
                    }
                authentication.Request.BeginGetResponse(
                    new AsyncCallback(responseAuthenticationCallback), 
                    responseAuthentication) 
                    |> ignore
            with
                | Ex -> authentication.CallbackUI false
            ()

        ///<summary>
        ///This is a magic number to check 
        ///whether the first request have got the cookie from the server-side,
        ///which should be changed later
        ///</summary>
        let countHeadersAfterGetCookie = 8

        ///<summary>
        ///Function: Callback function to get the cookie and 
        ///Param: IAsyncResult includes the information of
        ///login request, username, password and callbackUI
        ///Return:   
        ///</summary>
        let internal FetchCookieCallback (ar : IAsyncResult) = 
            let loginRequest : LoginRequestRecord = downcast ar.AsyncState
            try
                let response = loginRequest.Request.EndGetResponse(ar)
                let request : HttpWebRequest 
                    = createHttpRequest loginRequest.AuthenticationUrl
                requestPostSet request "application/x-www-form-urlencoded"
                request.CookieContainer <- cookieJar

                //if the cookie is got, call the callback function; or else, return to UI
                match response.Headers.Count with
                | countHeadersAfterGetCookie -> 
                    let authentication = {
                        AuthenticationRecord.Request    = request;
                        AuthenticationRecord.UserName   = loginRequest.UserName;
                        AuthenticationRecord.Password   = loginRequest.Password;
                        AuthenticationRecord.CallbackUI = loginRequest.CallbackUI
                        }
                    request.BeginGetRequestStream(
                            new AsyncCallback(requestAuthenticationCallback), 
                            authentication)
                    |> ignore
                    ()
                | _ -> 
                    loginRequest.CallbackUI false
                    ()
            with
                | Ex -> loginRequest.CallbackUI false

    module Security =
        ///<summary>
        ///Function: Use the async workflow around 2 we calls: 
        ///          1. get the cookie; 2. log into the website
        ///Param: UserName and password
        ///Return: unit  
        ///</summary>
        let loginToRpc (userName : string) 
                       (password : string) 
                       (callbackUI : (bool-> unit)) = 
            let sessionIdUrl = BASE_DNS 
            let authenticationUrl = BASE_DNS + "..................."
            let request : HttpWebRequest = Helper.createHttpRequest sessionIdUrl
            Helper.requestGetSet(request)
            request.CookieContainer <- Helper.cookieJar
            let loginRequest = {
                Helper.LoginRequestRecord.Request           = request
                Helper.LoginRequestRecord.UserName          = userName
                Helper.LoginRequestRecord.Password          = password
                Helper.LoginRequestRecord.AuthenticationUrl = authenticationUrl
                Helper.LoginRequestRecord.CallbackUI        = callbackUI
                }
            request.BeginGetResponse(new 
                    AsyncCallback(Helper.FetchCookieCallback), 
                    loginRequest) 
                    |> ignore
            ()
Run Code Online (Sandbox Code Playgroud)

Joe*_*ler 1

通常,在创建记录实例时,无需像您所做的那样完全限定每个属性。

let authentication = {
    AuthenticationRecord.Request    = request;
    AuthenticationRecord.UserName   = loginRequest.UserName;
    AuthenticationRecord.Password   = loginRequest.Password;
    AuthenticationRecord.CallbackUI = loginRequest.CallbackUI
    }
Run Code Online (Sandbox Code Playgroud)

只要您使用的属性的名称和类型仅匹配一种记录类型,F# 通常就足够聪明,能够理解您的意思。

let authentication = {
    Request    = request;
    UserName   = loginRequest.UserName;
    Password   = loginRequest.Password;
    CallbackUI = loginRequest.CallbackUI
}
Run Code Online (Sandbox Code Playgroud)

另外,我可能倾向于sprintfString.Format这里使用:

String.Format(
    "j_username={0}&j_password={1}&login={2}", 
    authentication.UserName, 
    authentication.Password, 
    "Login"))

sprintf "j_username=%s&j_password=%s&login=%s" 
    authentication.UserName authentication.Password "Login"
Run Code Online (Sandbox Code Playgroud)

但由于生成的字符串被传递给 a StreamWriter,它从TextWriter另一个选项继承,将使用fprintf它直接写入 a TextWriter

fprintf streamWriter "j_username=%s&j_password=%s&login=%s" 
    authentication.UserName authentication.Password "Login"
Run Code Online (Sandbox Code Playgroud)