WebAPI2 Json.Net Required property not adding ModelState Error properly - c#

[DataContract(Namespace="")]
public class Value
{
[DataMember(IsRequired=true)]
public string Id { get; set; }
[DataMember(IsRequired=true)]
public int Num { get; set; }
[DataMember]
public string Name { get; set; }
}
public Value Post(Value value)
{
if(!ModelState.IsValid)
{
//Bad request
}
return value;
}
I am trying to enforce that all values are specified in a post request to a web api. In the Value model above, when the Num property is omitted:
{"Id": "abc", "Name":"John"}
it adds an error to the model state indicating its absence. However, when the Id property is omitted:
{"Num" : 3, "Name" : "John"}
unexpectedly, no model state error is added, and the model is considered valid.
When I manually Deserialize the model with JsonConvert.Deserialize it throws a serialization exception in both cases indicating that the property is missing. Why does it appear to add model state errors when a value type (int) is not present correctly, but not when a reference type (string) is missing from the request body? How can I include those in the model state errors?
Note: It is not enough to put a [Required] attribute on the Id property. I want to allow a null or empty string value to be posted, as long as it is included in the request.

Try this?
[DataMember]
[Newtonsoft.Json.JsonProperty(Required = Newtonsoft.Json.Required.AllowNull)]
public string Id { get; set; }

Related

System.ComponentModel.DataAnnotations validation attributes not working as expected

I have checked for possible solutions to this and haven't been able to get any meaningful solution. I am working on an ASP.NET Web API project and I have a controller route with the below method signature:
[HttpPost("cimgAirtimeVending/{msisdn}")]
public async Task<ActionResult<ResponseObject>> CIMGAirtimeVending([FromBody] CIMGBillPaymentRequest _cimgAirtimeBillPaymentRequest, [Required, RegularExpression(#"\d{13}")] string msisdn)
CIMGBillPaymentRequest is a DTO with validations defined on each property as follows:
public class CIMGBillPaymentRequest
{
[Required, RegularExpression(#"\w+"), StringLength(30)]
public string RequestId { get; set; }
[Required, StringLength(10, MinimumLength = 10), RegularExpression(#"\d+")]
public string DebitAccount { get; set; }
/* [Required, RegularExpression(#"\w+")]
public string Narration { get; set; } */
[Required]
public bool IsFees { get; set; }
public List<Charge> Charges { get; set; }
[Required, RegularExpression(#"[\w-]+")]
public string ProductId { get; set; }
[Required, Range(1, 100, ErrorMessage = "Enter valid ChannelId")]
public int ChannelId { get; set; }
// [Required, Range(100, 100000, ErrorMessage = "Enter valid Amount (>=100)")]
public decimal Amount { get; set; }
[Required, RegularExpression(#"\w+")]
public string CustomerReference { get; set; }
}
public class Charge
{
// [StringLength(10, MinimumLength = 10), RegularExpression(#"\d{10,}")]
[RegularExpression(#"\w+")]
public string Account { get; set; }
public decimal Fee { get; set; }
}
The strange thing is, it appears not all the validation works. If isFees is removed from the DTO, it should return a Required validation error, but that does not happen. The validation is completely ignored. This does not happen with the ProductId property. A Required validation error is returned if it is not included in the DTO
Another issue I had was with the Account property in the Charge class. If you look at the line commented above it, you will see I am using a Regex validation RegularExpression(#"\d{10,}")] i.e. to ensure that it is a string of at least 10 digits. However, this validation is completely ignored and I had to use [RegularExpression(#"\w+")].
Any idea what the issue is and possible solution?
Take a look at the documentation here.
"The RequiredAttribute attribute specifies that when a field on a form is validated, the field must contain a value. A validation exception is raised if the property is null, contains an empty string (""), or contains only white-space characters."
"If the MVC data model or entity partial class contains a field that is annotated with the RequiredAttribute attribute, but the page does not contain the property, an error is not raised. Validation occurs only for fields that are submitted to the server."
If you leave out the property entirely then the required validation will not trigger. It will only trigger when explicitly called with null, empty string or string with only whitespace. Therefore I'm assuming the required check on some other fields namely the struct fields also don't work, however when you then check for a range, like with the ChannelId property it will validate the range. The issue here is that a struct can never be null. This means the RequiredAttribute won't work on bool, int, decimal, etc. fields.
In this case when not providing anything for IsFees will set the property with the value default(bool) the default value for a bool is false.
As for the Account field, look at the documentation here
"You apply the RegularExpressionAttribute attribute to a property when you need to validate values for the property against a regular expression. The regular expression enables you to specify very precisely the format of valid values. The Pattern property contains the regular expression. If the value of the property is null or an empty string (""), the value automatically passes validation for the RegularExpressionAttribute attribute. To validate that the value is not null or an empty string, use the RequiredAttribute attribute."
When providing null, or an empty string to a field with RegularExpressionAttribute it will pass the check. This means, for your Account field you need to use [Required, RegularExpression(#"\d{10,}")].

How to prevent initialization of properties which are not present in JSON string during Deserialization in C#?

My given class is :
public class Myclass
{
public int id { get; set; }
public string name{ get; set; }
}
I am passing jsonString like this:
var jsonString = #"{ 'name': 'John'}".Replace("'", "\"");
When i try to deserialize above json string using following code :
var visitData = JsonConvert.DeserializeObject<Myclass>(jsonString, jsonSerialize);
I am getting following values in visitData :
id : 0
name : "john"
But i want to ignore the id property as it is not present in jsonString.
How should i implement this functionality in Console Application of .Net Core 3.1 in C#.
You can try to declare id as a nullable property
public class Myclass
{
public int? id { get; set; } // a nullable property
public string name{ get; set; }
}
Usually deserializer will look for the matching property from the json string, if not exist then the default value is assigned. In your case the default value of int is 0. Again if you make the int as nullable int then again the default value will be assigned i.e., null.
For Newtonsoft.Josn create a contract resolver and manage your serialization/deserialization. Please find the detail information here Should not deserialize a property

MVC Model, inherit Validation Attributes from another Model

As a follow on from my previous question (MVC abstract ViewModel, retain Validation Attributes (dynamically)) I thought I'd ask the question in an alternate version.
So, let's consider the same situation, where I have a core ViewModel:
public class SampleViewModel {
[Required]
public string Property1 { get; set; }
[Required]
[EmailAddress]
public string Property2 { get; set; }
public IList<AnotherModel> Items { get; set; }
}
And another model:
public AnotherModel {
public string Value { get; set; }
}
And then within a controller, I perform the following:
var model = new SampleViewModel();
var fields = new List<AnotherModel>() {
new AnotherModel() { Value = model.Property1 },
new AnotherModel() { Value = model.Property2 },
};
So, my question is, how can I get the AnotherModel models to respond to the properties that are passed to their respective Value property.
In the sample above, the first AnotherModel will be Required, and the second will be Required, and an EmailAddress.
How is this possible?
Thank you
Update
For the purpose of this, lets say that each of those AnotherModel objects is represented by a form field. When the form is posted back, I use a custom model binder to obtain the Value from the AnotherModel and place it back into the source property (so Property1). My model is reconstructed correctly, and ModelState.IsValid is working. So, I have server-side validation of my SampleViewModel on post-back. Can this be somehow passed to the client to validate for me, based on the model's validation attributes?
Thanks

How to force JSON request body to contain some specific parameter in ASP.NET Web API 2

In our project, there is a POST method which take a JSON request body like this:
{
"UserId": string,
"Username": string,
"Email": string
}
It's ok if "Email" is null but we want it to always present in the request body.
So this is OK:
{
"UserId": "u12324",
"Username": "tmpUser",
"Email": null
}
but this is not:
{
"UserId": "u12324",
"Username": "tmpUser"
}
Do you have any ideas? Is it even possible?
You are using asp.net-web-api, which uses json.net as its underlying JSON serializer, according to JSON and XML Serialization in ASP.NET Web API. This serializer allows you to specify that a given property must be present (and optionally non-null) with the JsonPropertyAttribute.Required attribute setting, which has 4 values:
Default The property is not required. The default state.
AllowNull The property must be defined in JSON but can be a null value.
Always The property must be defined in JSON and cannot be a null value.
DisallowNull The property is not required but it cannot be a null value.
The following class makes use of these attributes:
public class EmailData
{
[JsonProperty(Required = Required.Always)] // Must be present and non-null
public string UserId { get; set; }
[JsonProperty(Required = Required.Always)] // Must be present and non-null
public string Username { get; set; }
[JsonProperty(Required = Required.AllowNull)] // Must be present but can be null
public string Email { get; set; }
}
Note that setting [JsonProperty(Required = Required.Always)] will cause an exception to be thrown during serialization if the property value is null.
Try to pass all parameters in a object
Example
[HttpPost]
public bool Create(DoSomething model)
{
return true
}
public class DoSomething
{
public int UserId{ get; set; }
public string UserName{ get; set; }
public string Email{ get; set; }
}

Model validation - Why ModelState.IsValid always returns true even when no values are supplied?

This is regarding WEBAPI and
the following is my Model class.
public class Request
{
public int Id { get; set; }
public string Name { get; set; }
[Required]
public Gender Gender { get; set; }
}
And my controller function (POST)
public class Values1Controller : ApiController
{
public IHttpActionResult Post([FromBody] Models.Request request)
{
if (!ModelState.IsValid)
{
return BadRequest();
}
var gender = request.Gender;
var id = request.Id;
var name = request.Name;
// do some operations!
return Ok();
}
}
And the xml that I submit along with each request.
<Request xmlns:i="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://schemas.datacontract.org/2004/07/webapi1.Models">
<id>1</id>
<name>testName</name>
</Request>
In the above XML post data, I do not supply a value for Gender at all, which are marked as [required].
But the ModelState.IsValid returns true, even when in the above XML there is no value
supplied for Gender.
How to prevent WebAPI from assigning default values to a enum in the model ?
Any ideas Why ?
I don't know why your model is valid if you don't supply gender, but you can make this value not have a default value by defining the Gender value as nullable, as follows:
public class Request
{
public int id { get; set; }
public string Name { get; set; }
[Required]
public Gender? Gender { get; set; }
}
Alternatively you can specify a default value for gender, as follows:
public enum Gender
{
Unknown = 0,
Male,
Female
}
Update
I now can see the difference between our results, again using Postman, if I submit a raw request as xml:
Header: Content-Type text/xml
<Request xmlns:i="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://schemas.datacontract.org/2004/07/webapi1.Models">
<id>1</id>
<Name>testName</Name>
</Request>
My model is valid, however if I submit x-www-form-urlencoded data:
Header: Content-Type application/x-www-form-urlencoded
id=1,Name=testname
Then my model is invalid, even though the value type has a value, my modelstate tells me that the Gender property is required.
As x-www-form-urlencoded is part of the query string then I guess that MVC is able to determine that the value was missing, but when the data is submitted as plain xml it can't determine this.
I suggest that if you want the required attribute to work in all cases, you make your value types nullable as follows:
[Required]
public Gender? Gender { get; set; }
You just put the Required validate, but you didn't check the value like this:
[RegularExpression("^[0-9]*$", ErrorMessage = "Your fill the wrong content!")]
and if you want to set the default value before save, you can do something like this in your class:
public Request()
{
Gender = "male";
Name = "default name"
}
Hope to help!

Categories