Say you have a simple method in a MVC controller...
[Route("{id}", Name = "GetById")]
[HttpGet]
[ProducesResponseType(typeof(SomeType), (int)HttpStatusCode.OK)]
[ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)]
[ProducesResponseType(typeof(string), (int)HttpStatusCode.Forbidden)]
[ProducesResponseType(typeof(string), (int)HttpStatusCode.BadRequest)]
[ProducesResponseType(typeof(string), (int)HttpStatusCode.ServiceUnavailable)]
public async Task<IActionResult> Get(string id, bool? includeNonActive = false)
{
// some stuff
}
Is there any way to get MVC to automagically return a HTTP 400 if invalid value (non-boolean) are passed to the includeNonActive
query string parameter?
HTTP GET http://my-server/api/12321312?includeNonActive=thisIsNotABooleanValue
Sure, I can accept a string type for 'includeNonActive', use Boolean.TryParse()
and return BadRequest("wtf")
, but that looks ugly in Swagger.
Is there any way to get MVC to automagically return a HTTP 400 if invalid value (non-boolean) are passed to the 'includeNonActive' query string parameter?
Sure, you could build an action filter for this.
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class EnsureBooleanQueryParameterAttribute : ActionFilterAttribute
{
public EnsureBooleanQueryParameterAttribute(string parameterName)
{
if (string.IsNullOrEmpty(parameterName))
throw new ArgumentException($"'{nameof(parameterName)}' is required.");
this.ParameterName = parameterName;
}
public string ParameterName { get; set; }
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var attribute = (EnsureBooleanQueryParameterAttribute)filterContext.ActionDescriptor
.GetCustomAttributes(typeof(EnsureBooleanQueryParameterAttribute), true)
.FirstOrDefault();
// The attribute exists on the current action method
if (attribute != null)
{
string param = filterContext.HttpContext.Request.QueryString[attribute.ParameterName];
bool result;
// If the query string value is present and not boolean
if (!string.IsNullOrEmpty(param) && !bool.TryParse(param, out result))
{
filterContext.Result = new HttpStatusCodeResult(400,
"Invalid boolean query string value for '{attribute.ParameterName}'.");
}
}
}
}
And then use the filter like:
[Route("{id}", Name = "GetById")]
[HttpGet]
[ProducesResponseType(typeof(SomeType), (int)HttpStatusCode.OK)]
[ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)]
[ProducesResponseType(typeof(string), (int)HttpStatusCode.Forbidden)]
[ProducesResponseType(typeof(string), (int)HttpStatusCode.BadRequest)]
[ProducesResponseType(typeof(string), (int)HttpStatusCode.ServiceUnavailable)]
[EnsureBooleanQueryParameter("includeNonActive")]
public async Task<IActionResult> Get(string id, bool? includeNonActive = false)
{
// some stuff
}
You could use this as a starting point and add an Enum
parameter that you can use to specify the data type and a bool
parameter to indicate whether or not the value is required to exist in the URL.