Alexey Strakh Alexey Strakh - 1 month ago 28
ASP.NET (C#) Question

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.


  • 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.


Answer

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.