简体   繁体   中英

F# asynchronous HTTP request - parse JSON response

I'm trying to use the following snippet of F# to send an asynchronous POST request to Firebase: in particular the following operation, which allows for anonymous sign in:

https://firebase.google.com/docs/reference/rest/auth/#section-sign-in-anonymously

    let makePostRequest (url : string) (requestBody : string) = 
    let req = WebRequest.CreateHttp url
    req.CookieContainer <- new CookieContainer()
    req.Method <- "POST"
    req.ProtocolVersion <- HttpVersion.Version10
    let postBytes = requestBody |> System.Text.Encoding.ASCII.GetBytes
    req.ContentLength <- postBytes.LongLength
    req.ContentType <- "application/xml; charset=utf-8"
    async{
        use! reqStream = req.GetRequestStreamAsync() |> Async.AwaitTask
        do! reqStream.WriteAsync(postBytes, 0, postBytes.Length) |> Async.AwaitIAsyncResult |> Async.Ignore
        reqStream.Close()
        use! res = req.AsyncGetResponse() 
        use stream = res.GetResponseStream()
        use reader = new StreamReader(stream)
        let! rdata = reader.ReadToEndAsync() |> Async.AwaitTask        
        return rdata
    }

I am calling it as follows:

    let url = "https://www.googleapis.com/identitytoolkit/v3/relyingparty/signupNewUser?key=XXXXXXXXXXXXXXXXX"
    let data = "returnSecureToken=true"
    makePostRequest url data

I'm very new to F# and a bit confused as to how this works. How can I obtain the JSON response from this request? How will I know when this is complete? Will this block the UI on the main thread? What I am doing is translating the following block of Swift code:

 Auth.auth().signInAnonymously(completion: { (user, error) in

        if let err = error {
            print(err.localizedDescription)
            return
        }

    })

Here everything makes sense to me as we have a completion block which gets called after the method is done executing, and we know that the user variable will contain the response obtained from the server, it makes much less sense to me in F#.

UPDATE:

Following the suggestions in the comments, I have referenced F# Data: HTTP Utilities and created the following code snippet:

let url = "https://www.googleapis.com/identitytoolkit/v3/relyingparty/signupNewUser?key=XXXXXXXXXXXX"
Http.RequestString
    ( url, 
      headers = [ ContentType HttpContentTypes.Json ],
      body = TextRequest """ {"returnSecureToken": true} """)

However this produces the error The value, namespace, type or module Http is not defined

UPDATE 2: I have settled for the following code:

let fetchUrl callback url (requestBody : string) =        
    let req = WebRequest.Create(Uri(url)) 
    req.Method <- "POST"
    let postBytes = requestBody |> System.Text.Encoding.ASCII.GetBytes
    req.ContentLength <- postBytes.LongLength
    req.ContentType <- "application/json; charset=utf-8"
    let reqStream = req.GetRequestStream() 
    reqStream.Write(postBytes, 0, postBytes.Length);
    reqStream.Close()

    use resp = req.GetResponse() 
    use stream = resp.GetResponseStream() 
    use reader = new IO.StreamReader(stream) 
    callback reader url

let myCallback (reader:IO.StreamReader) url = 
    let html = reader.ReadToEnd()
    let html1000 = html.Substring(0,1000)
    printfn "Downloaded %s. First 1000 is %s" url html1000
    html      // return all the html


let openLandlord ()  =
    let url = "https://www.googleapis.com/identitytoolkit/v3/relyingparty/signupNewUser?key=XXXXXXXXXXXXXXXXXXX"
    let requestBody = """ {"returnSecureToken": true} """
    fetchUrl myCallback url requestBody

following the example here:

https://fsharpforfunandprofit.com/posts/fvsc-download/

however when I call the openLandlord function here:

 override x.ViewDidLoad () =
    base.ViewDidLoad ()
    openLandlord ()

I get the following error:

This expression was expected to have type uint but here has type string.

在此处输入图片说明

Errors:

  1. Incomplete structured construct at or before this point in binding.
  2. The value of constructor request is not defined.
  3. Unexpected keyword match in member definition.
  4. The value, namespace, type or module Response is not defined.
  5. Unexpected identified in member definition.

Check the following code:

open System
open System.Net
open FSharp.Data

type Response = JsonProvider<""" { "name":"John", "age":94 } """>

[<EntryPoint>]
let main argv =
  let request () =
    async {
      let url = "https://www.googleapis.com/identitytoolkit/v3/relyingparty/signupNewUser?key=XXXXXXXXXXXX"
      return! Http.AsyncRequestString
        ( url,
          headers = [ "Content-Type", HttpContentTypes.Json ],
          body = TextRequest """ {"returnSecureToken": true} """ )
    } |>  Async.Catch
  let result = request ()
               |> Async.RunSynchronously
  match result with
  | Choice1Of2 text -> let json = Response.Parse text
                       printfn "name: %A" json.Name   
  | Choice2Of2 e -> printfn "request failed: %A" e
  0 // return an integer exit code

If type provides are not working with your setup, this may help: https://github.com/Microsoft/visualfsharp/issues/3303

Replace sample JSON with what is expected from the service.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM