如何让ASP.NET Core 2.0 MVC Model Binder绑定没有FromBody属性的复杂类型

时间:2018-03-21 01:39:39

标签: c# asp.net asp.net-mvc asp.net-core

所以在ASP.NET Core MVC中他们决定我需要在复杂类型的所有动作参数前面输入[FromBody],因为有些传奇的CSRF问题似乎没有人会谈论。我觉得这很荒谬所以有没有办法让ASP.NET Core MVC像旧的WebAPI一样,并且不需要[FromBody]到处都只是将JSON的所有内容绑定到复杂的类型参数?如果我能以某种方式选择它适用的控制器组,例如以/ api开头的控制器或用特定属性装饰的控制器,那将是很棒的。

1 个答案:

答案 0 :(得分:4)

通过实现自定义模型绑定约定,可以避免对每个复杂操作参数使用FromBody属性。步骤如下:

  1. 定义将在控制器级别指示所有操作应使用来自请求正文的默认绑定的属性:

    [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
    public sealed class DefaultFromBodyAttribute : Attribute
    {
    }
    
  2. 添加自定义模型绑定约定的实现:

    public class DefaultFromBodyBindingConvention : IActionModelConvention
    {
        public void Apply(ActionModel action)
        {
            if (action == null)
            {
                throw new ArgumentNullException(nameof(action));
            }
    
            if (action.Controller.Attributes.Any(a => a is DefaultFromBodyAttribute))
            {
                foreach (var parameter in action.Parameters)
                {
                    var paramType = parameter.ParameterInfo.ParameterType;
                    var isSimpleType = paramType.IsPrimitive
                                        || paramType.IsEnum
                                        || paramType == typeof(string)
                                        || paramType == typeof(decimal);
    
                    if (!isSimpleType)
                    {
                        parameter.BindingInfo = parameter.BindingInfo ?? new BindingInfo();
                        parameter.BindingInfo.BindingSource = BindingSource.Body;
                    }
                }
            }
        }
    }
    

    实施非常简单。我们检查控制器是否标有必需属性,检查操作参数是否复杂以及两个条件是否都命中 - 我们将绑定源设置为BindingSource.Body。我们应该检查基础操作参数类型是否复杂,因为我们不希望intstring类型与请求主体绑定。我从this answer借用了复杂类型识别的条件。

    您可以根据需要调整此约定的逻辑,例如如果要检查控制器路由而不是属性或具有特定类型的特殊条件。

  3. Startup.ConfigureServices方法中注册约定:

    services.AddMvc(options =>
    {
        options.Conventions.Add(new DefaultFromBodyBindingConvention());
    });
    
  4. 使用DefaultFromBody属性标记所需的控制器:

    [Route("api/[controller]")]
    [DefaultFromBody]
    public class SomeController : Controller
    
  5. 现在,即使您没有指定FromBody属性,默认情况下也会从主体绑定复杂的操作参数:

    [HttpPost]
    public void Post(SomeData value)
    {
        // ...
    }
    

    模型约定在应用程序启动期间调用一次(对于每个操作),因此在请求执行期间您不应该害怕任何性能损失。