Gvs Akhil
Gvs Akhil

Reputation: 2601

asp.net core 2.2 - Required attribute is not working for decimal and int

I am using net core 2.2 web api and I want to validate my body for every request. Validations are working fine for datatype string but for int and decimal they are not working.

My Model class is as follows:

public class IcuTherapyO2Request
    {
        [Required(ErrorMessage = "Mr Number is required")]
        public int mrNo { get; set; }
        [Required(ErrorMessage = "Type is required")]
        public string type { get; set; }
    }

My Controller

// create
        #region
        [HttpPost("create")]
        public async Task<IActionResult> create([FromBody] IcuTherapyO2Request icuTherapyO2Request)
        {
            try
            {
                var claimsIdentity = this.User.Identity as ClaimsIdentity;
                var userId = claimsIdentity.FindFirst("id")?.Value;
                icuTherapyO2Request.createdBy = userId;
                var response = await _icuIORepository.create(icuTherapyO2Request);
                return Ok(response);
            }
            catch (Exception ex)
            {
                return StatusCode(500, new Error { });
            }
        }
        #endregion

Case 1: I forgot to pass an object of type string in post body

{
   mrNo: 2
}

Now I get the following error: { "errors": { "type": [ "Type is required" ] }, "title": "One or more validation errors occurred.", "status": 400, "traceId": "0HM66ODDT3V8O:00000002" }

Case 2: I forgot to pass an object of type number in post body

    {
       type: "string"
    }

Here everything is working fine even if mrNo is missing

Upvotes: 1

Views: 2007

Answers (2)

Gvs Akhil
Gvs Akhil

Reputation: 2601

At last I found a solution to my question. Here's whats happening, for int and decimal types the asp.net core model is assigning 0 as default value even if object is not passed, so I added a bit more validation to accept only values starting from 1 using RANGE.

My updated model is as follows:

public class IcuTherapyO2Request
    {
        [Range(1, int.MaxValue, ErrorMessage = "Mr Number is required")] // this makes it to work
        [Required(ErrorMessage = "Mr Number is required")]
        public int mrNo { get; set; }
        [Required(ErrorMessage = "Type is required")]
        public string type { get; set; }
    }

Upvotes: 3

Eatos
Eatos

Reputation: 464

Now as the question is updated with a controller we can talk.

Although I do not know why Your validation for some types are working (You did not answer the question about side of the validation) but here is what we can tell based on the code You have provided:

  1. DataAnnotations attributes which You are using is for ASP.NET * to be able to build a ModelState while You are posting the data back to the server (well, not only posting, but nevermind that). This is so called model binding process which is happening behind the scenes and allow You to specify parameters for the actions as objects with a given types.

In this particular example by sending a POST with the form e.g.

@model WebApplication_NumberValidation.Models.IcuTherapyO2Request

<form asp-action="create" method="post">
    <input asp-for="mrNo" />
    <span asp-validation-for="mrNo" class="text-danger"></span>
    ...
    <button typr="submit">FIRE</button>
</form>

Your browser is calling the server with POST <server>/<controller>/create with a body of

{
 "mrNo": something
 ...
}

Request was sent, <controller>/create is called by the server ([HttpPost("create")]). Thanks to model binding, data that server received FromBody is used to create a IcuTherapyO2Request icuTherapyO2Request and the DataAnnotations are used to update the ModelState.

  1. Now server needs to send You something in return and here's when You are doing it wrong. You are returning Ok(response) which is sending to You the serialised status of response along with the 200 HTTP code. This is not what You want to do.

  2. You want to send back to the user the same model, same view on data validation so the server would be able to use ModelState to fill the HTML. ModelState contains all the data about model beeing validated. You should do this:

{
    Console.WriteLine(ModelState.IsValid);
    if (ModelState.IsValid)
    {
        // Do whatever You do BUT
        return View();
    }

    return View(icuTherapyO2Request); // icuTherapyO2Request from action's parameter
}

ModelState can also be used to add model error manually by the AddModelError if You would need it.

The key points here are:

  • returning same view with same model as the response to the user, which will display a new page but with the old model + updated model state
  • not returning with OK which should be used only for API (or if You are handling that response with JS on the client's browser). Otherwise the user will stay on the same page with no new HTML from the server. So unless You are doing some client side code, You would probably need to return some View (something with "Your form has been something").

Upvotes: 0

Related Questions