7

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?

3 Answers 3

9

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);
            }
        }
    }
}
Sign up to request clarification or add additional context in comments.

3 Comments

you don't need this as the default action selector won't select the action if the necessary simple type parameters is not there and basically returns 404.
If all the parameters are optional, i.e. Get(int? p1 = null, int? p2 = null), the action is executed even if the query string contains invalid parameters, so I believe this approach should work. I'll give it a shot, thanks!
ah, yes. Default action selector doesn't take optional parameters into consideration.
2

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:

1 Comment

Looks great, I'll first try with a simpler approach as the one RaghuRam suggested, anyway, this is very interesting, thanks for sharing :)
0

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 });
        }
    }
}

Comments

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.