Reputation: 23551
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.
Upvotes: 5
Views: 1408
Reputation: 31282
You could avoid using FromBody
attribute for each complex action parameter by implementing custom model binding convention. The steps are following:
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
{
}
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, e.g. if you want to check for controller routes instead of attributes or to have special conditions for specific types.
Register the convention in Startup.ConfigureServices
method:
services.AddMvc(options =>
{
options.Conventions.Add(new DefaultFromBodyBindingConvention());
});
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.
Upvotes: 5