.NET Core MVC反序列化

4
在一个.netcore应用程序中,我想提供以下内容(简化):
// Create a new record, assume it returns an ID=1
https://site/MyController/Save?FirstName=John&LastName=Doe&Status=Active

// Update the record without full state
PUT https://site/MyController/1
{
  'DOB': '1/1/1970',
  'Status': null
}

我很乐意为您翻译这个第二个调用的内容:

我想将这个第二个调用翻译为:

UPDATE MyModel SET DOB = '1/1/1970' AND Status=NULL WHERE Id = 1

我当然可以在MyController中编写代码来解析请求(querystring/form/body)中提交的值,并相应地创建 SQL 数据。

然而,我更愿意遵循 MVC 规范并利用 MVC 默认提供的绑定(binding)功能:

public async Task<MyModel> Save(string id, [FromBody]MyModel instance)
{
  await _MyRepository.UpdateAsync(id, message);
  return message;
}

这里的问题在于实例看起来是这样的:
{
  'FirstName': null,
  'LastName': null,
  'DOB': '1/1/1970',
  'Status': null
}

我无法确定在数据库中哪些字段应该设置为空,哪些应该保留不变。

我已经实现了一个包装类,它能够:

  • 在反序列化时设置任何“脏”属性
  • 在序列化时仅写入脏的属性

这将略微改变我的方法签名,但不会对开发人员造成负担:

public async Task<MyModel> Save(string id, [FromBody]MyWrapper<MyModel> wrapper
{
  await _MyRepository.UpdateAsync(id, wrapper.Instance, wrapper.DirtyProperties);
  return wrapper.Instance;
}

我的两个问题是:

  1. 我是否在重新发明已经存在的模式?
  2. 我能否以优雅的方式拦截MVC反序列化过程?

不要将持久化模型用作控制器操作的返回或输入值。这会引发麻烦。 - Tseng
2个回答

1

您可以查看自定义模型绑定。

  • create own model binder: class that implements IModelBinder interface:

    /// <summary>
    /// Defines an interface for model binders.
    /// </summary>
    public interface IModelBinder
    {
       /// <summary>
       /// Attempts to bind a model.
       /// </summary>
       /// <param name="bindingContext">The <see cref="ModelBindingContext"/>.</param>
       /// <returns>
       /// <para>
       /// A <see cref="Task"/> which will complete when the model binding process completes.
       /// </para>
       /// <para>
       /// If model binding was successful, the <see cref="ModelBindingContext.Result"/> should have
       /// <see cref="ModelBindingResult.IsModelSet"/> set to <c>true</c>.
       /// </para>
       /// <para>
       /// A model binder that completes successfully should set <see cref="ModelBindingContext.Result"/> to
       /// a value returned from <see cref="ModelBindingResult.Success"/>. 
       /// </para>
       /// </returns>
       Task BindModelAsync(ModelBindingContext bindingContext);
     }
    
  • register your binder:

    services.AddMvc().Services.Configure<MvcOptions>(options => {
        options.ModelBinders.Insert(0, new YourCustomModelBinder());
    });
    

MVC的github存储库和"自定义模型绑定"文章可能会有所帮助:


这确实实现了我所要求的。然而,它也让我很想只是创建中间件,因为我(到目前为止)利用了 MVC 的非常少的附加值。当然,这与我的问题相矛盾! - Eric Patrick

0

PUT动词需要整个实体,但您可以使用Delta发送HTTP PATCH。关于如何完成此操作的官方文档很少,但我找到了这个link,其中介绍了如何使用JSONPatchDocument来完成与拦截类基本相同的操作。


从服务器端的角度来看很优雅!对于我的用例,我试图让客户端/使用者的生活变得简单,并让他们采用相当冗长的PATCH文档将是一项挑战。[链接](http://jsonpatch.com/) - Eric Patrick

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