代码之家  ›  专栏  ›  技术社区  ›  Alk

F#异步HTTP请求-解析JSON响应

  •  2
  • Alk  · 技术社区  · 7 年前

    我正在尝试使用以下代码片段 F# 向Firebase发送异步POST请求:尤其是以下操作,它允许匿名登录:

    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
        }
    

    我这样称呼它:

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

    我是个新手 F级# 还有一点困惑,这是怎么回事。如何从该请求中获取JSON响应?我怎么知道这是什么时候完成的?这会阻止主线程上的UI吗?我正在翻译以下Swift代码块:

     Auth.auth().signInAnonymously(completion: { (user, error) in
    
            if let err = error {
                print(err.localizedDescription)
                return
            }
    
        })
    

    这里的一切对我来说都是有意义的,因为我们有一个完成块,它在方法执行完成后被调用,我们知道 user 变量将包含从服务器获得的响应,在F#中对我来说意义不大。

    更新:

    根据注释中的建议,我引用了F#Data:HTTP实用程序,并创建了以下代码段:

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

    但是,这会产生错误 The value, namespace, type or module Http is not defined

    更新2: 我已接受以下代码:

    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
    

    以下示例:

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

    但是当我打电话给 openLandlord 此处的功能:

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

    我得到以下错误:

    此表达式应具有uint类型,但此处具有string类型。

    enter image description here

    错误:

    1. 绑定中此点或之前的不完整结构化构造。
    2. 未定义构造函数请求的值。
    3. 成员定义中存在意外的关键字匹配。
    4. 未定义值、命名空间、类型或模块响应。
    5. 成员定义中标识了意外。
    1 回复  |  直到 7 年前
        1
  •  2
  •   Alex Netkachov    7 年前

    检查以下代码:

    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
    

    如果提供的类型不适用于您的设置,这可能会有帮助: https://github.com/Microsoft/visualfsharp/issues/3303

    将示例JSON替换为服务的预期内容。