简体   繁体   中英

How to get the ASP.NET Core 2.0 MVC Model Binder to bind complex types without FromBody attributes

So in ASP.NET Core MVC they decided that I need to type [FromBody] in front of all my action parameters of complex types because of some legendary CSRF issue nobody seems to talk about. I find this quite absurd so is there a way to make ASP.NET Core MVC behave like the old WebAPI and not require [FromBody] everywhere and just bind everything that is JSON to the complex type parameters? It will be great if I can somehow choose the set of controllers it applies to like for example controllers which start with /api or controllers which are decorated with a specific attribute.

You could avoid using FromBody attribute for each complex action parameter by implementing custom model binding convention. The steps are following:

  1. Define the attribute which will indicate on controller level that all actions should use default binding from request body:

     [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] public sealed class DefaultFromBodyAttribute : Attribute { } 
  2. Add implementation of custom model binding convention:

     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; } } } } } 

    Implementation is pretty straightforward. We check whether controller is marked with required attribute, check whether action parameter is complex and if both conditions hit - we set binding source to BindingSource.Body . We should check whether the underlying action parameter type is complex, as we don't want int or string type to be bound from request body. I've borrowed condition for complex type recognition from this answer .

    You could adjust the logic of this convention for your needs, eg if you want to check for controller routes instead of attributes or to have special conditions for specific types.

  3. Register the convention in Startup.ConfigureServices method:

     services.AddMvc(options => { options.Conventions.Add(new DefaultFromBodyBindingConvention()); }); 
  4. Mark required controllers with the DefaultFromBody attribute:

     [Route("api/[controller]")] [DefaultFromBody] public class SomeController : Controller 

Now complex action parameters will be bound from body by default even if you don't specify FromBody attribute:

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

Model conventions are invoked once during application startup (for every action), so you shouldn't be afraid of any performance penalties during request execution.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM