Reputation: 75
Given the following Web API controller action:
// GET api/values
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
Executing the following request is not failing, even when the parameter in the query string does not exist:
http://localhost:22297/api/values?someinvalidparameter=10
Is there a way to ensure that all parameters in the query string are valid parameters for the action that's being invoked?
Upvotes: 7
Views: 5695
Reputation: 81
For .net core web-api it will be a little different:
public class ValidateQueryParametersAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext actionContext)
{
var parameters = actionContext.ActionDescriptor.Parameters.ToList();
var queryParameters = actionContext.HttpContext.Request.Query.Keys.ToList();
if (queryParameters.Any(queryParameter => !parameters.Any(p => p.Name == queryParameter)))
{
actionContext.Result = new JsonResult(new { HttpStatusCode.BadRequest });
}
}
}
Upvotes: 0
Reputation: 6793
You can write an action filter that validates that all the query parameters are there in the action parameters and throws if not.
using System;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http.Controllers;
using System.Web.Http.Filters;
namespace My.Namespace.Filters
{
/// <summary>
/// Action filter that checks that parameters passed in the query string
/// are only those that we specified in methods signatures.
/// Otherwise returns 404 Bad Request.
/// </summary>
public class ValidateQueryParametersAttribute : ActionFilterAttribute
{
/// <summary>
/// This method runs before every WS invocation
/// </summary>
/// <param name="actionContext"></param>
public override void OnActionExecuting(HttpActionContext actionContext)
{
//check that client does not use any invalid parameter
//but just those that are required by WS methods
var parameters = actionContext.ActionDescriptor.GetParameters();
var queryParameters = actionContext.Request.GetQueryNameValuePairs();
if (queryParameters.Select(kvp => kvp.Key).Any(queryParameter => !parameters.Any(p => p.ParameterName == queryParameter)))
{
actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.BadRequest);
}
}
}
}
Upvotes: 9
Reputation: 58434
In order for that to work with out of the box validation support nicely, I created my own action selector which makes it possible to bind URI parameters to complex type objects without duplication.
So, you can do the following with this action selector:
public class CarsByCategoryRequestCommand {
public int CategoryId { get; set; }
public int Page { get; set; }
[Range(1, 50)]
public int Take { get; set; }
}
public class CarsByColorRequestCommand {
public int ColorId { get; set; }
public int Page { get; set; }
[Range(1, 50)]
public int Take { get; set; }
}
[InvalidModelStateFilter]
public class CarsController : ApiController {
public string[] GetCarsByCategoryId(
[FromUri]CarsByCategoryRequestCommand cmd) {
return new[] {
"Car 1",
"Car 2",
"Car 3"
};
}
public string[] GetCarsByColorId(
[FromUri]CarsByColorRequestCommand cmd) {
return new[] {
"Car 1",
"Car 2"
};
}
}
Then, you can register an action filter to validate the user inputs to terminate request and return back a "400 Bad Request" response along with the validation error messages:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class InvalidModelStateFilterAttribute : ActionFilterAttribute {
public override void OnActionExecuting(HttpActionContext actionContext) {
if (!actionContext.ModelState.IsValid) {
actionContext.Response = actionContext.Request.CreateErrorResponse(
HttpStatusCode.BadRequest, actionContext.ModelState);
}
}
}
Check out the below posts for more information about this action selector and how you can get it:
Upvotes: 2