5

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.

  • ps: I cannot use syntax route with ? sign because it's [FromBody]
  • pss: I wouldn't like to introduce v2 api because it isn't a v2 api, I'm adding new optional parameter
  • psss: I need some kind of attribute to update binding descriptor and specify that my parameter is optional, then it'll pass my validation.

1 Answer 1

3

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.

Sign up to request clarification or add additional context in comments.

3 Comments

an attribute with a method argument? So it'll go as [Optional][FromBody] ApplyOrderViewModel input ? I was thinking about tweaking [FromBody] to add an ability to define it as optional but this class is sealed and I cannot do much about that. The basic idea is to keep validation the same but tweak my method definition to make parameter binding understand that it's optional argumentBinding?.Descriptor?.IsOptional
Yes, additional attribute as you described [Optional]. You will have to use reflection to check if an attribute exists. Though it seems you already use reflection there, so you just need to add a simple check for the attribute.
I guess this suggestion is the best to implement and use. Clear and elegant :) спасибо

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.