WebAPI:JSON ReferenceHandler.Preserve

Nei*_*l W 6 json asp.net-web-api asp.net-core asp.net-core-webapi blazor-webassembly

我正在使用 Blazor(托管)并希望在将结果发送回客户端时保留引用。下面的示例实际上并不需要引用保存,但它是我针对更复杂的结构的测试场景。

  • 类“Staff”是在共享项目中定义的。
  • WebAPI 方法返回 IEnumerable

该有效负载如下所示:

[
  {
    "id":"a583baf9-8990-484f-9dc6-e8ea822f49c6",
    "name":"Neil",
    "themeName":"Blue Gray"
  },
  {
    "id":"a7a8e753-c7af-4b29-9242-7b2f5bdac830",
    "name":"Caroline",
    "themeName":"Yellow"
  }
]
Run Code Online (Sandbox Code Playgroud)

使用

var result = await response.Content.ReadFromJsonAsync<IEnumerable<Staff>>();
Run Code Online (Sandbox Code Playgroud)

我能够在客户端中获取我的 Staff 对象。

继续参考保存:

我更新了服务器上的 StartUp.cs 以包括:

services.AddControllersWithViews()
    .AddJsonOptions(o => 
        o.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.Preserve
    );
Run Code Online (Sandbox Code Playgroud)

结果是返回负载现在看起来像这样:

{
  "$id":"1",
  "$values":
     [
       {
         "$id":"2",
         "id":"a583baf9-8990-484f-9dc6-e8ea822f49c6",
         "name":"Neil",
         "themeName":"Blue Gray"
       },
       {
         "$id":"3",
         "id":"a7a8e753-c7af-4b29-9242-7b2f5bdac830",
         "name":"Caroline",
         "themeName":"Yellow"
       }
     ]
}
Run Code Online (Sandbox Code Playgroud)

看来是正确的。

这导致 JSON 反序列化异常:

var result = await response.Content.ReadFromJsonAsync<IEnumerable<Staff>>();
Run Code Online (Sandbox Code Playgroud)

因此,我认为在客户端反序列化时可能也需要包含引用处理选项。于是,改为:

JsonSerializerOptions options = new JsonSerializerOptions();
options.ReferenceHandler = ReferenceHandler.Preserve;
var result = await response.Content.ReadFromJsonAsync<IEnumerable<Staff>>(options);
Run Code Online (Sandbox Code Playgroud)

我没有收到任何错误,但我的枚举包括:

2 个 Staff 对象(但会将所有属性清空)。Enumerable 中的第三个空对象。

谁能指导我做错了什么?

Nei*_*l W 8

我找到了解决办法。这似乎正在发生:

WebAPI 上 Json 序列化的默认配置似乎是驼峰式大小写。然而,即使是这种情况,我在序列化共享类(使用大写)和在客户端反序列化时也没有遇到任何问题,即使 JSON 本身使用驼峰式大小写。

当我将 ReferenceHandler.Preserve 添加到 JsonSerializerOptions 时,这开始失败。

按如下方式更新我的 Json 选项,解决了问题:

services.AddControlersWithViews()
    .AddJsonOptions(options =>
    {
        options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.Preserve;
        options.JsonSerializerOptions.PropertyNamingPolicy = null // prevent camel case
    }
Run Code Online (Sandbox Code Playgroud)

替代方法是使用 MvcOptions。我并不声称知道哪个更好,但上面和下面似乎给出了相同的结果。

services.AddControllersWithViews(options =>
{
    options.OutputFormatters.RemoveType<SystemTextJsonOutputFormatter>();
    options.OutputFormatters.Add(new SystemTextJsonOutputFormatter(
        new JsonSerializerOptions(JsonSerializerDefaults.Web)
        {
            ReferenceHandler = ReferenaceHandler.Preserve,
            PropertyNamingPolicy = null    // prevent camel casing of Json
        }));
});
Run Code Online (Sandbox Code Playgroud)

然后在客户端,当收到来自 WebAPI 的响应时:

HttpResponseMessage response = await Http.GetAsync(myapiroute);
IEnumerable<Staff> staff = response.Content.ReadFromJsonAsync<IEnumerable<Staff>>(
    new JsonSerializerOptions() { ReferenceHandler = ReferenceHandler.Preserve });
Run Code Online (Sandbox Code Playgroud)

现在,引用处理似乎跨越了从 WebAPI 到 Blazor 客户端的界限。