Mando
Mando

Reputation: 11712

Optional attribute in asp.net web API routing

I'm using web api filter to validate all incoming view models and return view state error if it's null:

public class ValidateViewModelAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(HttpActionContext actionContext)
    {
        if(actionContext.ActionArguments != null)
        {
            foreach (var argument in actionContext.ActionArguments)
            {
                if (argument.Value != null)
                    continue;

                var argumentBinding = actionContext.ActionDescriptor?.ActionBinding.ParameterBindings
                    .FirstOrDefault(pb => pb.Descriptor.ParameterName == argument.Key);

                if(argumentBinding?.Descriptor?.IsOptional ?? true)
                    continue;

                actionContext.Response = actionContext.Request.CreateErrorResponse(HttpStatusCode.BadRequest, string.Format("Arguments value for {0} cannot be null", argument.Key));
                return;
            }
        }

        if (actionContext.ModelState.IsValid == false)
        {
            actionContext.Response = actionContext.Request.CreateErrorResponse(HttpStatusCode.BadRequest, actionContext.ModelState);
        }
    }
}

I have web api working in production and now I got new request to add one optional parameter to one action. Optional.... to keep api compatibility

    [Route("applyorder/{orderId}")]
    [HttpPost]
    public async Task<IHttpActionResult> ApplyOrder(int orderId, [FromBody] ApplyOrderViewModel input = null)

and if I don't specify input = null it isn't considered to be an optional parameters and couldn't pass my validation. With = null I'm getting the following error:

"Message": "An error has occurred.", "ExceptionMessage": "Optional parameter 'input' is not supported by 'FormatterParameterBinding'.",
"ExceptionType": "System.InvalidOperationException", "StackTrace": " at System.Web.Http.Controllers.HttpActionBinding.ExecuteBindingAsync(

How can I keep my global viewmodel validation in place and still mark this only method parameter to be optional.

Upvotes: 5

Views: 2647

Answers (1)

Ilya Chernomordik
Ilya Chernomordik

Reputation: 30205

Since it is your own validation that it cannot pass without = null you can add a custom [OptionalParameter] attribute and check for it existence e.g., though you need to do some caching by type to avoid excessive use of Reflection.

Second option is to have some Base class for all of your optional parameters like below and just check with is operator.

public abstract class OptionalParameter
{
}

Third option is to do the same with an interface.

Though the attribute is the cleanest in my opinion it's a bit harder to implement.

Upvotes: 3

Related Questions