F#无法向Web API发布信息?

4
我有以下的代码要发布到Web API。
[<CLIMutable>]
type MyModel = { FileName:string; Period:DateTime; DownloadedTime:DateTimeOffset; Url:string; }

let PostDownload (filepath, date, url) = 
  async {
    try
        use client = new HttpClient()
        let content = { FileName = filepath; Period = date; DownloadedTime = DateTimeOffset.Now; Url = url }
        let! response = Async.AwaitTask(client.PostAsJsonAsync("http://localhost:5000/api/DownloadedFiles", content))
    with 
    | ex -> logger.Error(ex, "Exception: " + ex.Message)
  } |> Async.Start

该服务具有以下代码,调试器显示所有downloadedFile字段的值均为默认值(null或value类型的最小值)。
    [HttpPost]
    public void Post([FromBody]DownloadedFile downloadedFile)
    {
        try
        {
            _context.DownloadedFile.Add(downloadedFile);
            _context.SaveChanges();
        }
        catch (Exception ex) { ...... }
    }

这个问题可能与F#可变记录类型有关,因为fiddler显示的F#代码中在字段名称的末尾添加了@。以下是代码示例:

{"FileName@":"test","Period@":"2100-01-01T00:00:00","DownloadedTime@":"2016-08-18T15:50:37.5004391-04:00","Url@":"test"}


2
http://blog.ploeh.dk/2015/03/19/posting-json-to-an-f-web-api - Mark Seemann
Mark的例子值得一看。这是一个“默认HttpClient JSON序列化器不太好”的问题,而不是F#的问题。 - Joel Mueller
作为HttpClient的替代方案,你可以看看FSharp.Data - Joel Mueller
您可以查看 https://github.com/haf/Http.fs,它是以 F# 为主要开发语言编写的,并且还具有大量关于发布文件的测试用例。 - Henrik
2个回答

2

我不知道你从哪里得到了HttpClient.PostAsJsonAsync,因为在我目前查看的HttpClient版本中并没有它。然而,我通常使用这个扩展,它对我很有效:

type HttpClient with
    member this.PostAsJsonAsync (requestUri : string, value : obj) =
        let json = string value
        let content = new StringContent (json)
        content.Headers.ContentType <-
            Headers.MediaTypeHeaderValue "application/json"
        this.PostAsync (requestUri, content)

“PostAsJasonAsync”在HttpClientExtensions中:https://msdn.microsoft.com/zh-cn/library/hh944521(v=vs.118).aspx - ca9163d9
我尝试了这段代码(使用PostAsync代替PostAsJasonAsync),但仍然失败,Fiddler显示HTTP POST主体现在是FSI_0004+MyFileModel - ca9163d9
1
你看到的类名(FSI_0004+MyFileModel)是因为在调用未重写该方法的类的ToString()方法时会发生这种情况。使用理解F#记录类型的库(例如JSON.NET的当前版本)显式地将对象转换为JSON将允许Mark的示例正常工作。 - Joel Mueller

0

我刚遇到了这个问题,花了几个小时才解决。 似乎 HttpClient.PostAsJsonAsync 中使用的序列化程序无法很好地处理 F# 类型。@ 符号问题也不容易在谷歌上找到。 然而,使用以下代码似乎可以解决:

task {
  let content = JObject.FromObject({ Id = "foo" }).ToString()
  let! response = client.PostAsync("url", new StringContent(content, Encoding.UTF8, "application/json"))
  return "Hello world"
}


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接