13

Is it possible to validate query parameters on an action without using a model? A lot of the calls in my API are one-offs and I don't see a point in making models for them if they will only be used one time.

I saw the following article, which seemed like it was exactly what I needed, except I don't want it to return a 404 if the required parm doesn't exist, I want it to return an object of error messages similar to the baked in model validation - really, I just want the parameters to be treated like a model, without actually having to make a model.

https://www.strathweb.com/2016/09/required-query-string-parameters-in-asp-net-core-mvc/

[HttpPost]
public async Task<IActionResult> Post(
    [FromQueryRequired] int? Id,
    [FromQuery] string Company)

EDIT:
The [FromQueryRequired] is a custom ActionConstraint that throws a 404 if the ID parm is missing (this was taken directly from the article). However I don't want the 404, I want an object that has a message that says {MESSAGE: "ID is required"}. I think the issue is that i can't access the Response context from within an Action Constraint.

3
  • Wouldn't FromQuery be the way as it doesn't specify Required? Commented Oct 24, 2018 at 20:27
  • @Ryan - I'm not sure I understand, sorry, could you please elaborate? Also, I updated the post to make things a little more clear. Commented Oct 24, 2018 at 20:50
  • From Asp.Net Core 2.1 there is a buit-in validation. See my response stackoverflow.com/a/54533218/245460 Commented Feb 5, 2019 at 11:17

4 Answers 4

11

From Asp.Net Core 2.1 there is a built-in parameter [BindRequired] which is doing this validation automatically.

public async Task<ActionResult<string>> CleanStatusesAsync([BindRequired, 
    FromQuery]string collection, [BindRequired, FromQuery]string repository,
       [BindRequired, FromQuery]int pullRequestId)
{
    // all parameters are bound and valid
}

If you are calling this method without parameters, a ModelState error is returned:

{
"collection": [
  "A value for the 'collection' parameter or property was not provided."
],
"repository": [
  "A value for the 'repository' parameter or property was not provided."
],
"pullRequestId": [
  "A value for the 'pullRequestId' parameter or property was not provided."
],
}

More details you can find in this excellent article.

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

Comments

2

You can read from request and validate it

string id= HttpContext.Request.Query["Id"].ToString();


    if (id==nll)
    {
    //do any thing here
    }

2 Comments

I dont want to do any manual validation though, I was hoping there something I could do that is similar to how ModelState validation works. At least I know I can access the httpcontext from within the action constraint I'll play with that and see if I can work with it.
@AustinShaw you can create middelware to check request , validate and return your response
2

Here is the solution I ended up using. Add an Attribute to the parms named [RequiredParm]. I loosely based it on someone else's answer for a different question, but I can't seem to find it at the moment, apologies to whoever you are, if I can find it I'll update this answer for credit.

EDIT: Found it, answered by @James Law - Web Api Required Parameter

Usage:

[HttpPost]
public async Task<IActionResult> Post(
    [FromQuery, RequiredParm] int? Id,
    [FromQuery] string Company)

ActionFilterAttribute:

[AttributeUsage(AttributeTargets.Method)]
public sealed class CheckRequiredParmAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext context)
    {
        var requiredParameters = context.ActionDescriptor.Parameters.Where(
            p => ((ControllerParameterDescriptor)p).ParameterInfo.GetCustomAttribute<RequiredParmAttribute>() != null).Select(p => p.Name);

        foreach (var parameter in requiredParameters)
        {
            if (!context.ActionArguments.ContainsKey(parameter))
            {
                context.ModelState.AddModelError(parameter, $"The required argument '{parameter}' was not found.");
            }
            else
            {
                foreach (var argument in context.ActionArguments.Where(a => parameter.Equals(a.Key)))
                {
                    if (argument.Value == null)
                    {
                        context.ModelState.AddModelError(argument.Key, $"The requried argument '{argument.Key}' cannot be null.");
                    }
                }
            }
        }

        if (!context.ModelState.IsValid)
        {
            context.Result = new BadRequestObjectResult(context.ModelState);
            return;
        }

        base.OnActionExecuting(context);
    }
}

/// <summary>
/// Use this attribute to force a [FromQuery] parameter to be required. If it is missing, or has a null value, model state validation will be executed and returned throught the response. 
/// </summary>  
[AttributeUsage(AttributeTargets.Parameter)]
public sealed class RequiredParmAttribute : Attribute
{
}

2 Comments

From Asp.Net Core 2.1 there is a buit-in validation. See my response stackoverflow.com/a/54533218/245460
In this solution, you have to annotate the method with CheckRequiredParm also.
0

i haven't tried yet may be like that;

public class MyActionFilterAttribute: IActionFilter
{
    public override void OnActionExecuting(HttpActionContext actionContext)
    {
        var id = actionContext.ActionArguments["Id"];
        if(string.IsNullOrEmpty(id))
            actionContext.Response = actionContext.Request.CreateResponse(
            HttpStatusCode.OK, 
            new {MESSAGE = "ID is required"}, 
            actionContext.ControllerContext.Configuration.Formatters.JsonFormatter
        );
    }
}

[HttpPost]
[MyActionFilterAttribute]
public ActionResult Post([FromQueryRequired] int? Id,[FromQuery] string Company)

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.