2

I'm returning BadRequest(ModelState) from an ASP.NET controller action, like this:

public class Person{
   [MaxLength(40)]
   public string Name{get;set;}
   public double Height{get;set;}
}

public Put([FromBody]Person item){
    if(!ModelState.IsValid) return BadRequest(ModelState);
    //save to db
    return Ok(item);
}

This works, however casing seems inconsistent when the BadRequest is serialized to JSON. First, the object serializes as camelCase, as expected:

GET /api
{ name:'foo',
  height: 11
}

If the error is a failure in model binding, then the casing is camel cased:

PUT /api {name:'bob',height:'foo'}
{
    height:['Could not convert 'foo' to double']
}

If the error is a failure in validation, the casing is PascalCased:

PUT /api {name:'morethan40characters', height:12}
{
    Name:['Name should be between 0 and 40 characters']
}

I think I understand why this is: the model binding is starting from the posted JSON which is camelCased, while the validation is working against the .NET class directly, which is PascalCased.

However, API consumers won't care about that distinction and will expect consistent casing. Especially in the client UI with something like Vue or Angular, it's cleaner to keep everything camelCased, or at least consistent:

http.put('/api',item).then(x=>{...},e=>{ this.error=e.response.data });
...
<ul>
  <li v-for="message in error.height">{{message}}</li>
  <li v-for="message in error.Height">{{message}}</li>
  <!-- line above smells -->
</ul>
<input type="text" v-model="item.height">

Obviously I could massage the error response client-side, but is there a flag or some middleware I can do server-side to just make this consistent. Preferably, consistently camelCase?

Daniel
  • 3,021
  • 5
  • 35
  • 50
  • So regardless you'd prefer a constant camelCase output? [link to stackoverflow post](https://stackoverflow.com/questions/52010816/json-returns-properties-in-pascalcase-instead-of-camelcase) maybe this link helps? – Zephire Aug 27 '19 at 20:47

2 Answers2

0

Try with CamelCasePropertyNamesContractResolver:

services.AddMvc().AddJsonOptions(options => {
    options.SerializerSettings.ContractResolver =
        new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver();
})

So If sending PUT /api {name: "xxxxx",height: 1111} , the result will be :{"errors":{"name":["

Nan Yu
  • 26,101
  • 9
  • 68
  • 148
0

ModelState is a Dictionary, so force it in Program.cs:

   builder.Services.AddControllers().AddJsonOptions(options =>
{
    options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase; //best pratics for SPAs
    options.JsonSerializerOptions.DictionaryKeyPolicy = JsonNamingPolicy.CamelCase;//best pratics for SPAs
});
Rafael Rocha
  • 174
  • 14