3

I have a regular Integer (Not nullable) in my model:

    [Required]
    [Range(0, Int32.MaxValue - 1)]
    public int PersonId
    {
        get;
        set;
    }

In my WebApi action, I accept an object that has that propery.

    public IHttpActionResult Create([FromBody] Person person)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest("Some error message.");
        } 
        //Do some stuff with person...
    }

Now, altough there is a Required attribute on PersonId, when a person is posted to this action, the ModelState.IsValid property is true.

I guess this is because Person is created with default value, which is 0, I want to throw an error if there is no PersonId field in the incoming JSON / query string request.

I can set PersonId to be Nullable, but that doesn't make sense.

Is there any easy way to validate the field exists and the integer is larger than 0 ? (without custom validators for that simple requirement)

Ofiris
  • 6,047
  • 6
  • 35
  • 58

4 Answers4

3

Setting the [Required] attribute doesn't do anything on an int, as far as I know. All [Required] does is make sure the value is not null. You can set [Range(1, Int32.MaxValue)] to make sure that a correct value is added.

If you don't already do this, it might be a good idea to make a different model for your view and make the data annotations on this model. I use view models to make sure I don't pollute my "real" models with stuff that is not relevant to the whole domain. This way your PersonId can be nullable in your view model only, where it makes sense.

andreas
  • 505
  • 1
  • 7
  • 16
2

BindRequiredAttribute can be used to

Quoting from this nice blog post about [Required] and [BindRequired]

It works the same way as RequiredAttribute, except it mandates that the value comes from the request – so it not only rejects null values, but also default (or “unbound”) values.

So this would reject unbound integer values:

[BindRequired]
[Range(0, Int32.MaxValue - 1)]
public int PersonId
{
    get;
    set;
}
Ofiris
  • 6,047
  • 6
  • 35
  • 58
  • 1
    I stumbled upon this when I had similar problem where input was coming from [FromBody]. So to add exception above answer, [BindRequired] is not relevant when using input formatters aka [FromBody]. See [Github](https://github.com/aspnet/Mvc/issues/8631#issuecomment-432361684) – HappyTown Apr 17 '19 at 18:56
  • @Ofiris, what namespace is [BindRequired] part of? – David Mays Jun 09 '22 at 17:02
  • This answer (and question) is quite old and I am not sure if it's relevant but I guess - `Microsoft.AspNetCore.Mvc.ModelBinding ` – Ofiris Jun 10 '22 at 18:29
0

I tend to use int? (nullable int) in this case and then mark those as required. I then use myInt.Value throughout the code and assume it's safe to use because it wouldn't have passed validation otherwise.

and like @andreas said, I do make sure to use "view models" in times like this so I'm not polluting my view model as a business or data layer model.

Joe Phillips
  • 49,743
  • 32
  • 103
  • 159
0

Actually for missing not nullable integer parameters model validation doesn't work. There is JSON parsing exception which is thrown by Newtonsoft.Json. You can have a following workaround to parse and include exceptions in model validations. Create the custom validation attribute as following and register in WebApiConfig.cs.

public class ValidateModelAttribute : ActionFilterAttribute {
    public override void OnActionExecuting(HttpActionContext actionContext) {
        // Check if model state is valid
        if (actionContext.ModelState.IsValid == false) {
            // Return model validations object
            actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.BadRequest,
                                                                            new ValidationResultModel(100001, actionContext.ModelState));
        }
    }

    public class ValidationError {
        public string Field { get; }

        public string Message { get; }

        public ValidationError(string field, string message) {
            Field = field != string.Empty ? field : null;
            Message = message;
        }
    }

    public class ValidationResultModel {
        public int Code { get; set; }
        public string Message { get; }
        public IDictionary<string, IEnumerable<string>> ModelState { get; private set; }

        public ValidationResultModel(int messageCode, ModelStateDictionary modelState) {
            Code = messageCode;
            Message = "Validation Failed";

            ModelState = new Dictionary<string, IEnumerable<string>>();

            foreach (var keyModelStatePair in modelState) {
                var key = string.Empty;
                key = keyModelStatePair.Key;
                var errors = keyModelStatePair.Value.Errors;
                var errorsToAdd = new List<string>();

                if (errors != null && errors.Count > 0) {
                    foreach (var error in errors) {
                        string errorMessageToAdd = error.ErrorMessage;

                        if (string.IsNullOrEmpty(error.ErrorMessage)) {
                            if (key == "model") {
                                Match match = Regex.Match(error.Exception.Message, @"'([^']*)");
                                if (match.Success)
                                    key = key + "." + match.Groups[1].Value;

                                errorMessageToAdd = error.Exception.Message;
                            } else {
                                errorMessageToAdd = error.Exception.Message;
                            }
                        }

                        errorsToAdd.Add(errorMessageToAdd);
                    }

                    ModelState.Add(key, errorsToAdd);
                }
            }
        }
    }
}

//Register in WebApiConfig.cs

// Model validation           
   config.Filters.Add(new ValidateModelAttribute());
faisale
  • 1,435
  • 14
  • 14