Web Api Required Parameter

前端 未结 5 805
闹比i
闹比i 2020-12-14 06:10

Using ASP.NET Web API. Is there a way to automatically return a status code 400 if a parameter is null? I found this question but that is a global solution that is applied

相关标签:
5条回答
  • 2020-12-14 06:29

    we can use the BindRequired, which is from Microsoft.AspNetCore.Mvc.ModelBinding namespace.

    public async Task<ActionResult<IEnumerable<Numbers>>> GetAll([BindRequired, FromQuery]string[] numbers)
            {
                var result = await _service.GetAllDetails(numbers);
                return Ok(result);
            }
    

    after that your swagger will look like below.

    0 讨论(0)
  • 2020-12-14 06:39

    Set [Required] on a property in your model and then check the ModelState to see if it IsValid.

    This will allow all the required properties to be tested at the same time.

    See the "Under-Posting" section @ Model validation in WebAPI

    0 讨论(0)
  • 2020-12-14 06:49

    The approach I ended up using was to create a custom filter that I registered globally. The filter checks all request parameters for the RequiredAttribute. If the attribute is found then it checks if the parameter was passed with the request (not null) and returns status code 400 if it was null. I also added a cache to the filter to store the required parameters for each request to avoid the reflection hit on future calls. I was pleasantly surprised to find that this works for value types as well since the action context stores the parameters as objects.

    EDIT - Updated solution based on tecfield's comment

    public class RequiredParametersFilter : ActionFilterAttribute
    {
        // Cache used to store the required parameters for each request based on the
        // request's http method and local path.
        private readonly ConcurrentDictionary<Tuple<HttpMethod, string>, List<string>> _Cache =
            new ConcurrentDictionary<Tuple<HttpMethod, string>, List<string>>();
    
        public override void OnActionExecuting(HttpActionContext actionContext)
        {
            // Get the request's required parameters.
            List<string> requiredParameters = this.GetRequiredParameters(actionContext);     
    
            // If the required parameters are valid then continue with the request.
            // Otherwise, return status code 400.
            if(this.ValidateParameters(actionContext, requiredParameters))
            {
                base.OnActionExecuting(actionContext);
            }
            else
            {
                throw new HttpResponseException(HttpStatusCode.BadRequest);
            }
        }
    
        private bool ValidateParameters(HttpActionContext actionContext, List<string> requiredParameters)
        {
            // If the list of required parameters is null or containst no parameters 
            // then there is nothing to validate.  
            // Return true.
            if (requiredParameters == null || requiredParameters.Count == 0)
            {
                return true;
            }
    
            // Attempt to find at least one required parameter that is null.
            bool hasNullParameter = 
                actionContext
                .ActionArguments
                .Any(a => requiredParameters.Contains(a.Key) && a.Value == null);
    
            // If a null required paramter was found then return false.  
            // Otherwise, return true.
            return !hasNullParameter;
        }
    
        private List<string> GetRequiredParameters(HttpActionContext actionContext)
        {
            // Instantiate a list of strings to store the required parameters.
            List<string> result = null;
    
            // Instantiate a tuple using the request's http method and the local path.
            // This will be used to add/lookup the required parameters in the cache.
            Tuple<HttpMethod, string> request =
                new Tuple<HttpMethod, string>(
                    actionContext.Request.Method,
                    actionContext.Request.RequestUri.LocalPath);
    
            // Attempt to find the required parameters in the cache.
            if (!this._Cache.TryGetValue(request, out result))
            {
                // If the required parameters were not found in the cache then get all
                // parameters decorated with the 'RequiredAttribute' from the action context.
                result = 
                    actionContext
                    .ActionDescriptor
                    .GetParameters()
                    .Where(p => p.GetCustomAttributes<RequiredAttribute>().Any())
                    .Select(p => p.ParameterName)
                    .ToList();
    
                // Add the required parameters to the cache.
                this._Cache.TryAdd(request, result);
            }
    
            // Return the required parameters.
            return result;
        }
    
    }
    
    0 讨论(0)
  • 2020-12-14 06:52

    A solution for asp.net core...

    [AttributeUsage(AttributeTargets.Method)]
    public sealed class CheckRequiredModelAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext context)
        {
            var requiredParameters = context.ActionDescriptor.Parameters.Where(
                p => ((ControllerParameterDescriptor)p).ParameterInfo.GetCustomAttribute<RequiredModelAttribute>() != null).Select(p => p.Name);
    
            foreach (var argument in context.ActionArguments.Where(a => requiredParameters.Contains(a.Key, StringComparer.Ordinal)))
            {
                if (argument.Value == null)
                {
                    context.ModelState.AddModelError(argument.Key, $"The argument '{argument.Key}' cannot be null.");
                }
            }
    
            if (!context.ModelState.IsValid)
            {
                var errors = context.ModelState.Values.SelectMany(v => v.Errors).Select(e => e.ErrorMessage);
                context.Result = new BadRequestObjectResult(errors);
                return;
            }
    
            base.OnActionExecuting(context);
        }
    }
    
    [AttributeUsage(AttributeTargets.Parameter)]
    public sealed class RequiredModelAttribute : Attribute
    {
    }
    
    services.AddMvc(options =>
    {
        options.Filters.Add(typeof(CheckRequiredModelAttribute));
    });
    
    public async Task<IActionResult> CreateAsync([FromBody][RequiredModel]RequestModel request, CancellationToken cancellationToken)
    {
        //...
    }
    
    0 讨论(0)
  • 2020-12-14 06:52

    The accepted solution takes it upon itself to report back any errors. A more appropriate approach for MVC5 is to let the controller handle (via model validation) the reporting of any errors, aka something like this:

    using System.ComponentModel.DataAnnotations;
    using System.Linq;
    using System.Web.Http.Controllers;
    using System.Web.Http.Filters;
    using System.Web.Http.ModelBinding;
    
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)]
    public sealed class ValidateParametersAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting(HttpActionContext context)
        {
            var descriptor = context.ActionDescriptor;
            if (descriptor != null)
            {
                var modelState = context.ModelState;
                foreach (var parameterDescriptor in descriptor.GetParameters())
                {
                    EvaluateValidationAttributes(
                        suppliedValue: context.ActionArguments[parameterDescriptor.ParameterName],
                        modelState: modelState,
                        parameterDescriptor: parameterDescriptor
                    );
                }
            }
    
            base.OnActionExecuting(context);
        }
    
        static private void EvaluateValidationAttributes(HttpParameterDescriptor parameterDescriptor, object suppliedValue, ModelStateDictionary modelState)
        {
            var parameterName = parameterDescriptor.ParameterName;
    
            parameterDescriptor
                .GetCustomAttributes<object>()
                .OfType<ValidationAttribute>()
                .Where(x => !x.IsValid(suppliedValue))
                .ForEach(x => modelState.AddModelError(parameterName, x.FormatErrorMessage(parameterName)));
        }
    }
    

    You may then plug it in universally via WebApiConfig.cs:

    config.Filters.Add(new ValidateParametersAttribute());
    
    0 讨论(0)
提交回复
热议问题