Как создать асинхронный HttpWebRequest в Silverlight (F#)

Поскольку я упомянул, потому что Silverlight HttpWebRequest. Создайте подвешивает внутренний асинхронный блок, я просто создал пакет функций обратного вызова для реализации того же асинхронного блока.

Процесс входа в систему требует двух шагов:

1) Получите запрос к странице, которая возвращает cookie 2), Сообщение Формы к второй странице, которая передает тот cookie w/это и выполняет аутентификацию

Следующее является 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 = 
        ///
        ///Transfer data from Security.loginToRpc to Helper.FetchCookieCallback
        ///
        type LoginRequestRecord = {
                Request : HttpWebRequest;
                UserName : string;
                Password : string;
                AuthenticationUrl : string;
                CallbackUI  : (bool -> unit)
                }

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

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

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

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

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

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

        ///
        ///Function: Callback function inluding EndGetResponse method of request
        ///Param: IAsyncResult includes the information of HttpWebRequest
        ///Return: unit
        ///
        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

        ///
        ///Function: Callback function for user to log into the website
        ///Param: IAsyncResult includes the information of
        ///HttpWebRequest and user's identity
        ///Return: unit  
        ///
        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
            ()

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

        ///
        ///Function: Callback function to get the cookie and 
        ///Param: IAsyncResult includes the information of
        ///login request, username, password and callbackUI
        ///Return:   
        ///
        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 =
        ///
        ///Function: Use the async workflow around 2 we calls: 
        ///          1. get the cookie; 2. log into the website
        ///Param: UserName and password
        ///Return: unit  
        ///
        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
            ()

6
задан Community 23 May 2017 в 11:47
поделиться

2 ответа

Обычно при создании экземпляров записи нет необходимости полностью определять каждое свойство, как вы делаем.

let authentication = {
    AuthenticationRecord.Request    = request;
    AuthenticationRecord.UserName   = loginRequest.UserName;
    AuthenticationRecord.Password   = loginRequest.Password;
    AuthenticationRecord.CallbackUI = loginRequest.CallbackUI
    }

Пока имена и типы свойств, которые вы используете, соответствуют только одному типу записи, F # обычно достаточно умен, чтобы понять, что вы имели в виду.

let authentication = {
    Request    = request;
    UserName   = loginRequest.UserName;
    Password   = loginRequest.Password;
    CallbackUI = loginRequest.CallbackUI
}

Кроме того, я мог бы быть склонен использовать sprintf вместо String.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"

Но поскольку результирующая строка передается в StreamWriter , который наследуется от TextWriter , другой вариант - использовать fprintf , который записывает непосредственно в TextWriter .

fprintf streamWriter "j_username=%s&j_password=%s&login=%s" 
    authentication.UserName authentication.Password "Login"
1
ответ дан 17 December 2019 в 20:28
поделиться

Я обычно сохраняю местный штат очень локальным, пряча его внутри закрытия. Итак, если я не пропустил ссылку на requestId , я бы переместил его внутрь getId :

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

// changes to:
let getId =
 let mutable requestId : int = 0
 (fun () -> 
   requestId <- requestId + 1
   requestId.ToString())

Во второй версии getId фактически является fun внизу, после строки let mutable ... . fun захватывает requestId , а затем получает имя getId . Поскольку requestId выходит за рамки, никто другой не может его изменить или даже увидеть.

1
ответ дан 17 December 2019 в 20:28
поделиться
Другие вопросы по тегам:

Похожие вопросы: