Skip to the content.

Teams Bot库的JSON

如果你和我一样,一直使用最新的 asp.net core 来开发teams bot的应用,那么你就会发现当你使用最新的 LTS 3.1版本或者 5.0 版本或者最新的 6.0 版本,asp.net core 默认使用System.Text.Json库,所以当你在处理 Teams 发送来的请求的时候,如果还是使用类似于下面的代码,那很多时候就是发现activity解析不出来,返回的response也不对。

[HttpPost("api/messages")]
public Activity GetMessage([FromBody]Activity activity)
{
    ....

    return new Activity()
    {
        ....
    };
}

为什么呢?从 Microsoft.Bot.Schema 的源代码里的 Activity 定义 里可以发现原因:

using System.Collections.Generic;
using Newtonsoft.Json;

namespace Microsoft.Bot.Schema
{
    public partial class Activity
    {
        [JsonProperty(PropertyName = "type")]
        public string Type { get; set; }

        [JsonProperty(PropertyName = "id")]
        public string Id { get; set; }

        [JsonProperty(PropertyName = "timestamp")]
        public System.DateTimeOffset? Timestamp { get; set; }

        [JsonProperty(PropertyName = "localTimestamp")]
        public System.DateTimeOffset? LocalTimestamp { get; set; }

        [JsonProperty(PropertyName = "localTimezone")]
        public string LocalTimezone { get; set; }

        [JsonProperty(PropertyName = "serviceUrl")]
        public string ServiceUrl { get; set; }

        [JsonProperty(PropertyName = "channelId")]
        public string ChannelId { get; set; }

        [JsonProperty(PropertyName = "from")]
        public ChannelAccount From { get; set; }

        ...
    }
}

从上面的代码可以看到,Activity 使用了 Newtonsoft.JSON 的 JsonProperty attribute 来说明如何 serialize 和 deserialize。既然是 Newtonsoft.JSON 的 attribute,那肯定对 System.Text.Json 不起作用了,就是说默认的 asp.net core 的 json 不会去读取 JsonProperty attribute。除了 JsonProperty,Microsoft.Bot.Schema和其他bot的库也使用了很多其他 Newtonsoft.JSON 的东西。

现在相信大家知道了原因,那如何解决?有这么几种方法:

  1. 改变默认的 json,使用 Newtonsoft.JSON 先给项目安装一个库
    Install-Package Microsoft.AspNetCore.Mvc.NewtonsoftJson
    

    然后在Startup文件中:

    services.AddMvc().AddNewtonsoftJson();
    

    或者

    services.AddControllers().AddNewtonsoftJson();
    services.AddControllersWithViews().AddNewtonsoftJson();
    services.AddRazorPages().AddNewtonsoftJson();
    
  2. 手动的使用 Newtonsoft.JSON 来做序列化和反序列化
[HttpPost]
[Route("api/extension")]
public async Task<IActionResult> Post()
{
    using var reader = new StreamReader(Request.Body);
    var activity = JsonConvert.DeserializeObject<Activity>(await reader.ReadToEndAsync());

    ...

    var responseJson = JsonConvert.SerializeObject(responseActivity);
    return Content(responseJson, "application/json");
}
  1. 自己定义一些需要用到的结构 (只适用于简单的应用,比如:outgoing webhook)
public record Activity
{
    [JsonPropertyName("text")]
    public string Text { get; init; }
}

上面的 JsonPropertyName 是 System.Text.Json 里的一种 attribute。

不过,最终的解决方案,肯定是微软官方的这些 Bot,Teams 的library可以支持最新的 System.Text.Json,希望这天可以快点到来。

Written on October 10, 2021