If the body of a POST request is empty (ie Content-Length:0) then ModelState.IsValid returns true even if the object in question has a [Required] attribute. To validate this case I've added to ModelValidationFilterAttribute class and added the following:
(
I'm afraid I am unable to reproduce your problem. Everything is working as it should, that's why I am asking for your code.
Either way, have you missed that putting a required attribute on a non nullable field is pretty much useless? Why? Because it's non nullable and the contructor of that object automatically assigns a value to it. For instance, int becomes 0 and guid becomes
guid.Empty. That is why the model is valid in that case. As Mike said, you should make those object nullable. For instance a trailing question mark (int?, guid?) will make those objects nullable and the constructor will not asign a value to them and model
will not be valid.
This code has a required attribute on an Id field and it pretty much useless. The model will validate even if you don't set the Id because the constructor will (value 0).
public class Premises
{
[Required]
public int Id { get; set; }
public string City { get; set; }
public string Country { get; set; }
}
This code has a nullable int and if you don't set the Id the model will not validate because a contructor will not assign a value to it. It will be null.
public class Premises
{
[Required]
public int? Id { get; set; }
public string City { get; set; }
public string Country { get; set; }
}
I hope this explains the nehaviour you are experiencing. If not, please reply with the relavant source code of your project.
Please "Mark As Answer" if the post helped you.
mitja.gti | www.mitjagti.com
mitja, I see what you're saying about nullable but my class has [Required] on a nullable field so its not about that.
Do you have a controller that takes Premises as an argument? eg
[HttpPost]
public HttpResponseMessage Post(Premises premises)
{
//premises is null here
return new HttpResponseMessage();
}
now assuming you have Mikes validation code try sending thru a POST with no body... the method will still be called allbeit with premises equal to null. Am i making sense now?
Do you know how to contact Mike Wasson? I've trolled the web cant find any links to his email.
I am afraid it has everything to do with a nullable objects and there is also no need to contact Mike. I think he explained the situation very good and I suggest you go trough Mikes article again, slowly.
You should always validate your model (if (ModelState.IsValid)) before doing anything else.
public HttpResponseMessage Post(Premises premises)
{
if (ModelState.IsValid)
{
return new HttpResponseMessage(HttpStatusCode.OK);
}
else
{
return new HttpResponseMessage(HttpStatusCode.BadRequest);
}
}
Try with the above validation ;)
Please "Mark As Answer" if the post helped you.
mitja.gti | www.mitjagti.com
I'm already doing the validation. I have the [ModelValidationFilterAttribute] on my method and it works if you post something like this
{
City ="city"
}
However if you POST nothing (empty, content length=0) then a NULL object is passed to the controller, and ModelState.IsValid=true (which is wrong as the object has a [Required] field)
public HttpResponseMessage Post(Premises premises)
{
if (ModelState.IsValid)
{
return new HttpResponseMessage(HttpStatusCode.OK);
}
else
{
return new HttpResponseMessage(HttpStatusCode.BadRequest);
}
}
Now, send a POST to that controller that doesnt define a body. What happens?
I think you are correct. This is the behaviour of the ModelState.IsValid, it will not check for the the null model. I don't think it's a bug, as this is the behaviour of model binding in MVC.
But we might need to introduce a new set of attributes to "Web API" project to make it easy for us to specify which paramters are required for the specific method or we might need to have a new attribute that is similar to the [Required] attribute but can
be used on the model's class level instead of just on the class' properties level.
wal
Member
4 Points
22 Posts
model validation when Content-Length = 0
Aug 02, 2012 06:04 AM|LINK
this refers to Mike's article: http://www.asp.net/web-api/overview/formats-and-model-binding/model-validation-in-aspnet-web-api
If the body of a POST request is empty (ie Content-Length:0) then ModelState.IsValid returns true even if the object in question has a [Required] attribute. To validate this case I've added to ModelValidationFilterAttribute class and added the following: (
if (actionContext.Request.Method == HttpMethod.Post && actionContext.Request.Content.Headers.ContentLength == 0 && !_allowEmptyPost) { actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.BadRequest, "POST requires non-zero ContentLength"); }please note _allowEmptyPost is passed as an optional ctor argument to the Attribute.
Is this a bug that ModelState.IsValid returns true in this case?
mitja.GTI
Star
11157 Points
2094 Posts
Re: model validation when Content-Length = 0
Aug 06, 2012 01:36 PM|LINK
Can you please provide a relavant sample of your code?
mitja.gti | www.mitjagti.com
wal
Member
4 Points
22 Posts
Re: model validation when Content-Length = 0
Aug 07, 2012 03:00 AM|LINK
mitja.GTI,
you can use Mike's code and use HttpWebRequest to send thru a POST with no body. do you want a Spike or are you unable to reproduce this ?
mitja.GTI
Star
11157 Points
2094 Posts
Re: model validation when Content-Length = 0
Aug 07, 2012 12:38 PM|LINK
I'm afraid I am unable to reproduce your problem. Everything is working as it should, that's why I am asking for your code.
Either way, have you missed that putting a required attribute on a non nullable field is pretty much useless? Why? Because it's non nullable and the contructor of that object automatically assigns a value to it. For instance, int becomes 0 and guid becomes guid.Empty. That is why the model is valid in that case. As Mike said, you should make those object nullable. For instance a trailing question mark (int?, guid?) will make those objects nullable and the constructor will not asign a value to them and model will not be valid.
This code has a required attribute on an Id field and it pretty much useless. The model will validate even if you don't set the Id because the constructor will (value 0).
public class Premises { [Required] public int Id { get; set; } public string City { get; set; } public string Country { get; set; } }This code has a nullable int and if you don't set the Id the model will not validate because a contructor will not assign a value to it. It will be null.
public class Premises { [Required] public int? Id { get; set; } public string City { get; set; } public string Country { get; set; } }I hope this explains the nehaviour you are experiencing. If not, please reply with the relavant source code of your project.
mitja.gti | www.mitjagti.com
wal
Member
4 Points
22 Posts
Re: model validation when Content-Length = 0
Aug 08, 2012 11:43 PM|LINK
mitja, I see what you're saying about nullable but my class has [Required] on a nullable field so its not about that.
Do you have a controller that takes Premises as an argument? eg
[HttpPost] public HttpResponseMessage Post(Premises premises) { //premises is null here return new HttpResponseMessage(); }now assuming you have Mikes validation code try sending thru a POST with no body... the method will still be called allbeit with premises equal to null. Am i making sense now?
Do you know how to contact Mike Wasson? I've trolled the web cant find any links to his email.
mitja.GTI
Star
11157 Points
2094 Posts
Re: model validation when Content-Length = 0
Aug 08, 2012 11:55 PM|LINK
I am afraid it has everything to do with a nullable objects and there is also no need to contact Mike. I think he explained the situation very good and I suggest you go trough Mikes article again, slowly.
You should always validate your model (if (ModelState.IsValid)) before doing anything else.
public HttpResponseMessage Post(Premises premises) { if (ModelState.IsValid) { return new HttpResponseMessage(HttpStatusCode.OK); } else { return new HttpResponseMessage(HttpStatusCode.BadRequest); } }Try with the above validation ;)
mitja.gti | www.mitjagti.com
wal
Member
4 Points
22 Posts
Re: model validation when Content-Length = 0
Aug 09, 2012 02:42 AM|LINK
I'm already doing the validation. I have the [ModelValidationFilterAttribute] on my method and it works if you post something like this
{
}
However if you POST nothing (empty, content length=0) then a NULL object is passed to the controller, and ModelState.IsValid=true (which is wrong as the object has a [Required] field)
OK?
wal
Member
4 Points
22 Posts
Re: model validation when Content-Length = 0
Aug 10, 2012 06:32 AM|LINK
Mitja,
Lets take your code as an exmaple:
public HttpResponseMessage Post(Premises premises) { if (ModelState.IsValid) { return new HttpResponseMessage(HttpStatusCode.OK); } else { return new HttpResponseMessage(HttpStatusCode.BadRequest); } }Now, send a POST to that controller that doesnt define a body. What happens?
anas
All-Star
73649 Points
7914 Posts
Moderator
Re: model validation when Content-Length = 0
Aug 11, 2012 02:26 AM|LINK
wal.
I think you are correct. This is the behaviour of the ModelState.IsValid, it will not check for the the null model. I don't think it's a bug, as this is the behaviour of model binding in MVC.
But we might need to introduce a new set of attributes to "Web API" project to make it easy for us to specify which paramters are required for the specific method or we might need to have a new attribute that is similar to the [Required] attribute but can be used on the model's class level instead of just on the class' properties level.
wal
Member
4 Points
22 Posts
Re: model validation when Content-Length = 0
Aug 11, 2012 07:11 AM|LINK
thanks anas, i was more bringing to the teams attention, appreciate the understanding.
As I said, I updated Mikes attribute class so you can specify whether you need to validate for null
public class ModelValidationFilterAttribute : ActionFilterAttribute { private bool _allowEmptyPost; public ModelValidationFilterAttribute() : this(false) { } public ModelValidationFilterAttribute(bool allowEmptyPost) { _allowEmptyPost = allowEmptyPost; } public override void OnActionExecuting(HttpActionContext actionContext) { if (!actionContext.ModelState.IsValid) { // Return the validation errors in the response body. var errors = new Dictionary<string, IEnumerable<string>>(); foreach (KeyValuePair<string, ModelState> keyValue in actionContext.ModelState) { errors[keyValue.Key] = keyValue.Value.Errors.Select(e => e.ErrorMessage); } actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.BadRequest, errors); } if (actionContext.Request.Method == HttpMethod.Post && actionContext.Request.Content.Headers.ContentLength == 0 && !_allowEmptyPost) { actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.BadRequest, "POST requires non-zero ContentLength"); } } }