I have created a very simple OData v4 controller. The controller basically contains Entity Framework-backed CRUD methods for the following Pet
entity:
public class Pet
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
public int Age { get; set; }
}
An important thing here is that Pet.Age
is the non-nullable required property.
Here is the controller itself (only Post
method is shown):
public class PetController : ODataController
{
private DatabaseContext db = new DatabaseContext();
// POST: odata/Pet
public IHttpActionResult Post(Pet pet)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
db.Pet.Add(pet);
db.SaveChanges();
return Created(pet);
}
// Other controller methods go here...
}
And this is my WebApiConfig
controller configuration:
ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<Pet>("Pet");
config.MapODataServiceRoute("odata", "odata", builder.GetEdmModel());
Now if I want to create a new Pet
in my database, I issue a POST
request like this:
POST http://localhost:8080/odata/Pet
Content-type: application/json
{ Name: "Cat", Age: 5 }
However, I can simply omit the Age
property in JSON request payload, so JSON deserializer will use a default value of 0
, while I want a 400 Bad Request
status to be returned instead. This problem is called under-posting.
It can be easily solved when using regular WebApi controllers (the solution is described here). You just create a PetViewModel
and make your controller to accept a PetViewModel
instead of an actual Pet
entity:
public class PetViewModel
{
// Make the property nullable and set the Required attribute
// to distinguish between "zero" and "not set"
[Required]
public int? Age { get; set; }
// Other properties go here...
}
Then in your controller you just convert PetViewModel
to Pet
entity and save it to the database as usual.
Unfortunately, this approach does not work with OData controllers: if I change my Post
method to accept PetViewModel
instead of Pet
, I receive the following error:
System.Net.Http.UnsupportedMediaTypeException: No MediaTypeFormatter is available to read an object of type 'PetViewModel' from content with media type 'application/json'.
at System.Net.Http.HttpContentExtensions.ReadAsAsync[T](HttpContent content, Type type, IEnumerable'1 formatters, IFormatterLogger formatterLogger, CancellationToken cancellationToken)
at System.Net.Http.HttpContentExtensions.ReadAsAsync(HttpContent content, Type type, IEnumerable'1 formatters, IFormatterLogger formatterLogger, CancellationToken cancellationToken)
at System.Web.Http.ModelBinding.FormatterParameterBinding.ReadContentAsync(HttpRequestMessage request, Type type, IEnumerable`1 formatters, IFormatterLogger formatterLogger, CancellationToken cancellationToken)
So, is there any way to prevent under-posting when using OData controllers?
RangeAttribute
and specify it at 1 to 999. TheModelState.IsValid
should then catch that value 0 is not within the range and return aBadRequest
status. The other option is to create a custom filter and manually parse the incoming JSON before it is mapped to the model but that seems like overkill. – Igornull
values was required. Have a look at the answer if you are interested. Thank you for your help! – Sergey Kolodiy