【问题标题】:How to post a dynamic JSON property to a C# ASP.Net Core Web API using MongoDB?如何使用 MongoDB 将动态 JSON 属性发布到 C# ASP.Net Core Web API?
【发布时间】:2017-12-09 16:44:45
【问题描述】:

如何使用 MongoDB 将动态 JSON 属性发布到 C# ASP.Net Core Web API?

似乎 MongoDB 的优势之一是能够将您需要的任何内容存储在 Object 类型属性中,但似乎不清楚如何通过 C# Web 从客户端 ajax 帖子获取动态属性信息MongoDB 的 API。

我们希望允许管理员创建一个带有标题和开始日期/时间的事件,但我们也希望允许用户使用响应式表单添加自定义表单字段,以满足他们想要的任何内容,例如 T 恤尺寸或膳食偏好。 .. 无论用户将来想出什么。然后,当有人注册该事件时,他们会将 EventID 和自定义字段发布到 Web API。

我们可以拥有一个包含 _id、event_id、reg_time 和 form_fields 的 Event MongoDB 集合,其中 form_fields 是存储动态数据的对象类型。

所以我们想用自定义的 FormsFields 发布这个 JSON 的变体:

变体 1:

{
    "EventId": "595106234fccfc5fc88c40c2",
    "RegTime":"2017-07-21T22:00:00Z",
    "FormFields": {
        "FirstName": "John",
        "LastName": "Public",
        "TShirtSize": "XL"
    }
}

变体 2:

{
    "EventId": "d34f46234fccfc5fc88c40c2",
    "RegTime":"2017-07-21T22:00:00Z",
    "FormFields": {
        "Email": "John.Public@email.com",
        "MealPref": "Vegan"
    }
}

我想要一个带有 Post 操作的 EventController,它采用映射到上述 JSON 的自定义 C# EventReg 对象。

事件控制器:

[HttpPost]
public void Post([FromBody]EventReg value)
{
    eventService.AddEventRegistration(value);
}

EventReg 类:

public class EventReg
{
    public EventReg()
    {
        FormFields = new BsonDocument();
    }
    [BsonId]
    [BsonRepresentation(BsonType.ObjectId)]
    public string EventRegId { get; set; }

    [BsonElement("EventId")]
    [BsonRepresentation(BsonType.ObjectId)]
    public string EventId { get; set; }

    [BsonElement("reg_time")]
    public DateTime RegTime
    {
        set; get;
    }

    [BsonElement("form_fields")]
    public MongoDB.Bson.BsonDocument FormFields { get; set; }
}

事件服务

public string AddEventRegistration(EventReg eventReg)
{
    this.database.GetCollection<EventReg>("event_regs").InsertOne(eventReg);
    return eventReg.EventRegId;
} 

现在,如果我发布到控制器,我的 EventReg 为 null,因为它一定不知道如何将我的 JSON FormFields 属性映射到 BsonDocument。

  • FormFields 可以使用什么类型?
  • 我可以将 FormFields 属性设置为 BsonDocument,是否有一种简单的方法可以将 Web API 参数映射到该属性?
  • 是否有示例说明某些自定义序列化程序在这种情况下如何工作?

我们也许可以使用动态类型并循环通过发布的属性,但这看起来很难看。我还从这里的帖子中看到了 JToken 解决方案,但这看起来也很丑陋。

如果 MongoDB 打算像这样动态使用,难道不应该有一个干净的解决方案将动态数据传递给 MongoDB 吗?有什么想法吗?

【问题讨论】:

    标签: c# asp.net json mongodb asp.net-core-webapi


    【解决方案1】:

    JToken 示例用于获取数据,但在检索时会导致浏览器和 Postman 抛出错误并显示警告,指示内容被作为文档读取,但它是 application/json 格式。我看到 FormFields 属性被返回为 {{"TShirtSize":"XL"}} 所以在序列化过程中双括号可能是一个问题。

    我最终在 System.Dynamic 命名空间中使用了 .NET ExpandoObject。 ExpandoObject 与 MongoDB BsonDocument 兼容,因此序列化会像您期望的那样自动完成。所以不需要奇怪的代码来手动处理问题中的 JToken 示例等属性。

    我仍然认为,如果可能的话,应该使用更强类型的 C# 表示,但是如果您必须允许任何 JSON 内容通过 Web API 以自定义 C# 类作为输入发送到 MongoDB,那么 ExpandoObject 应该这样做诀窍。

    看看下面 EventReg 类的 FormFields 属性是如何变成 ExpandoObject 的,并且没有代码可以手动处理保存到 MongoDB 的对象的属性。

    这是用标准类型属性和动态 FormFields 属性手动填充对象的原始有问题且过于复杂的 JToken 代码:

    [HttpPost]
    public void Post([FromBody]JToken token)
    {
        if (token != null)
        {
            EventReg eventReg = new EventReg();
            if (token.Type == Newtonsoft.Json.Linq.JTokenType.Object)
            {
                eventReg.RegTime = DateTime.Now;
                foreach (var pair in token as Newtonsoft.Json.Linq.JObject)
                {
                    if (pair.Key == "EventID")
                    {
                        eventReg.EventId = pair.Value.ToString();
                    }
                    else if (pair.Key == "UserEmail")
                    {
                        eventReg.UserEmail = pair.Value.ToString();
                    }
                    else
                    {
                        eventReg.FormFields.Add(new BsonElement(pair.Key.ToString(), pair.Value.ToString()));
                     }
                }
            }
            //Add Registration:
            eventService.AddEventRegistration(eventReg);
        }
    }
    

    使用 ExpandoObject 消除了对所有这些代码的需要。请参阅下面的最终代码。 Web API 控制器现在是 1 行而不是 30 行代码。此代码现在可以毫无问题地从上述问题中插入和返回 JSON。

    事件控制器:

    [HttpPost]
    public void Post([FromBody]EventReg value)
    {
        eventService.AddEventRegistration(value);
    }
    

    EventReg 类:

    public class EventReg
    {
        public EventReg()
        {
           FormFields = new ExpandoObject();
        }
        [BsonId]
        [BsonRepresentation(BsonType.ObjectId)]
        public string EventRegId { get; set; }
    
        [BsonElement("event_id")]
        [BsonRepresentation(BsonType.ObjectId)]
        public string EventId { get; set; }
    
        [BsonElement("user_email")]
        public string UserEmail { get; set; }
    
        [BsonElement("reg_time")]
        public DateTime RegTime{ get; set; }
    
        [BsonElement("form_fields")]
        public ExpandoObject FormFields { get; set; }
    }
    

    事件服务:

    public string AddEventRegistration(EventReg eventReg)
    {
        this.database.GetCollection<EventReg>("event_regs").InsertOne(eventReg);
        return eventReg.EventRegId;
    }
    

    【讨论】:

      【解决方案2】:

      在 ASP.NET Core 3.0+ 中,Newtonsoft.Json 不再是默认的 JSON 序列化程序。因此我会使用 JsonElement:

      [HttpPost("general")]
      public IActionResult Post([FromBody] JsonElement elem)
      {        
          var title = elem.GetProperty("title").GetString();
          ...
      

      【讨论】:

        【解决方案3】:

        如果您不确定要发送的 Json 的类型,即如果您正在处理动态 json。那么下面的方法将起作用。 接口:

            [HttpPost]
            [Route("demoPath")]
            public void DemoReportData([FromBody] JObject Jsondata)
        

        来自 .net 核心应用程序的 Http 客户端调用:

        using (var httpClient = new HttpClient())
        {
        
            return await httpClient.PostAsJsonAsync(serviceUrl,
            new  demoClass()
                         {
                           id= "demoid",
                           name= "demo name",
                           place= "demo place"
                          };).ConfigureAwait(false);
        }
        

        【讨论】:

          【解决方案4】:

          您可以将FormFields定义为字符串,将JSON转成字符串后以字符串格式发送数据到API:

          "{\"FirstName\":"John\",\"LastName\":\"Public\",\"TShirtSize\":\"XL\"}"

          然后在您的控制器中将字符串解析为 BsonDocument

          BsonDocument.Parse(FormFields);
          

          我会使用 AutoMapper 来自动化 dto 和文档之间的转换

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2020-12-03
            • 2018-02-28
            • 2017-01-16
            • 1970-01-01
            • 2021-01-19
            • 2020-04-16
            • 2020-11-19
            相关资源
            最近更新 更多