【问题标题】:Strongly typed AND arbitrary properties in Web API request objectWeb API 请求对象中的强类型和任意属性
【发布时间】:2014-01-03 12:57:12
【问题描述】:

我有一个看起来很像这样的对象:

public class MyNiceRequest 
{
    [Required]
    public string FirstName { get; set; }
    [Required]
    public string LastName { get; set; }

    public string SomeOptionalField { get; set; }
}

我的控制器如下所示,仍然很标准:

public MyNiceResponse Post(MyNiceRequest request) {
...
}

从调用应用程序的前端,我想包含比对象中指定的三个更多的字段。这些字段是运行时生成的(通过管理界面控制),因此我无法将它们应用于我的请求类。但是,我还没有找到在控制器中检索它们的好方法。

我可以让我的请求对象 (MyNiceRequest) 继承自 Dictionary<string,string> - 然后我会得到它们,但它们不会绑定到强类型类上的各自属性(似乎字典是在使用任何模型绑定器中的其余部分之前绑定)。此外,更重要的是,对应用程序至关重要的验证停止工作。

我看到this 的问题,但它没有给我任何东西,因为Request.Content.Read...-方法给我的结果是空的(因为它已经被读取并绑定到一个模型?)。

假设我想要前端的以下字段:

  • FirstName(应该绑定到强类型,别处)
  • LastName(应该绑定到强类型,别处)
  • SomeOptionalField(应该绑定到强类型,别处)
  • RuntimeGenerated1(应该出现在字典中)
  • RuntimeGenerated2(应该出现在字典中)

我想要两种解决方案之一:

  • 既可以从Dictionary<string,string> 继承,也可以在强类型属性之后绑定字典以让验证发挥作用
  • MyNiceRequest 上有一个单独的属性,可能类似于Dictionary<string,string> TheRest { get; set; },并将其绑定到某个地方的其余传入属性。

重写前端以将运行时生成的字段作为单独的集合传递不是一种选择。

..这完全可以通过重用/重新排序现有的东西来实现,还是我必须从头开始编写一个完整的媒体类型格式化程序和/或模型绑定器?

【问题讨论】:

  • 您的请求内容类型是什么?是 JSON 吗?
  • 是的。对于一个真正的通用解决方案,我想它应该处理任何应用的媒体格式化程序,但对于这种仅支持 JSON 的特定情况是非常好的。

标签: c# asp.net-web-api


【解决方案1】:

对于application/json 内容类型,您可以将DynamicObject 与WebAPI 的默认JSON 格式化程序一起使用。

public class MyNiceRequest : DynamicObject
{
    private Dictionary<string, string> _dynamicMembers = new Dictionary<string, string>();

    [Required]
    public string FirstName { get; set; }

    [Required]
    public string LastName { get; set; }

    public string SomeOptionalField { get; set; }

    [JsonIgnore]
    public Dictionary<string, string> DynamicMembers
    {
        get { return _dynamicMembers; }
    }

    public override bool TryGetMember(GetMemberBinder binder, out object value)
    {
        string stringValue;
        var isFound = _dynamicMembers.TryGetValue(binder.Name, out stringValue);
        value = stringValue;
        return isFound;
    }

    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
        if (value is string)
        {
            _dynamicMembers[binder.Name] = (string)value;
            return true;
        }
        return false;
    }
}

编辑

  1. 如果您希望对象以相同的格式序列化,请实现IDictionary&lt;string, string&gt;。这很简单,只需将接口实现委托给_dynamicMembers

  2. 此解决方案不适用于默认 XML 和 x-www-form-urlencoded 格式化程序:(

【讨论】:

  • 太棒了。像魅力一样工作!
【解决方案2】:

你可以拥有这样的 DTO。

public class MyNiceRequest
{
    [Required]
    public string FirstName { get; set; }
    [Required]
    public string LastName { get; set; }

    public string SomeOptionalField { get; set; }

    public Dictionary<string, string> TheRest { get; set; }
}

如果您在请求正文中发布 JSON,例如 {"firstname":"arve", "therest":{"key":"value"}},将填充 FirstName 属性。 TheRest 将有一个带有键“key”和值“value”的项目。当然,ModelState.IsValid 将是 false,因为 JSON 不包含 LastName

【讨论】:

  • 这是一个公平的答案,但我不想重写前端以将所有这些动态字段放入单独的集合中 (TheRest) - 它们需要位于“基础级别”像其他一切一样。澄清了我原来的问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-09-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多