namespace MyOffice.Web.Infrastructure.Attributes; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.ApplicationModels; using Microsoft.AspNetCore.Mvc.ModelBinding; [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] public sealed class DefaultFromBodyAttribute : Attribute { } public class DefaultFromBodyBindingConvention : IActionModelConvention { public void Apply(ActionModel action) { if (action == null) { throw new ArgumentNullException(nameof(action)); } if (action.Controller.Attributes.Any(x => x is DefaultFromBodyAttribute)) { foreach (var parameter in action.Parameters) { if (parameter.Attributes.Any(x => x is FromQueryAttribute or FromRouteAttribute or FromHeaderAttribute or FromFormAttribute or FromServicesAttribute)) { continue; } // Already bound (e.g. CancellationToken → Special) — do not force Body. if (parameter.BindingInfo?.BindingSource is { } existing && existing != BindingSource.ModelBinding && existing != BindingSource.Custom) { continue; } var paramType = parameter.ParameterInfo.ParameterType; if (paramType == typeof(CancellationToken) || Nullable.GetUnderlyingType(paramType) == typeof(CancellationToken)) { continue; } var isSimpleType = paramType.IsPrimitive || paramType.IsEnum || paramType == typeof(string) || paramType == typeof(decimal) || paramType == typeof(Guid) || paramType == typeof(Guid?) || paramType == typeof(DateTime) || paramType == typeof(DateTime?) || paramType == typeof(DateOnly) || paramType == typeof(DateOnly?) || paramType == typeof(TimeOnly) || paramType == typeof(TimeOnly?); if (!isSimpleType) { parameter.BindingInfo ??= new BindingInfo(); parameter.BindingInfo.BindingSource = BindingSource.Body; } } } } }