1

In an Asp.Net Web API controller I have, there is a PostAsync method that bind the request JSON body to a model class.

E.g.:

public class EmployeesController : ApiController
{
   ...
   public async Task<HttpResponseMessage> PostAsync([FromBody] Employee employee)
   {
      ...
   }
   ...
}

The Employee model class contains a numeric property, Height:

public class Employee
{
   ...
   public decimal Height { get; set; }
   ...
}

The problem is that when posting to the above controller action with request payload that contains a Height value with a leading zero, the FromBodyAttribute binder automatically converts the value to octal base, e.g.: 010 is translated as the value 8 in decimal base.

Sample request body:

{
   ...
   "Height": 010,
   ...
}

How can I prevent the conversion to an octal base?

What I've tried so far:

  1. Implement a PropertyBindAttribute like described in this blog post, but the attribute receives the value post conversion to Octal base, so it's too late in the binding-life-cycle to use this approach
  2. Convert the property to string type, and use a custom ValidationAttribute to validate the string is format match a numeric decimal base value, then converting the value to decimal and assigning it to another decimal property / backing-field
ADyson
  • 57,178
  • 14
  • 51
  • 63

1 Answers1

1

Why are you sending values with leading zeros? That's not a number as defined in JSON. Strictly according to the standard it's simply invalid JSON, but some parsers will be lenient and interpret it as an octal, which seems to be what is happening here.

So, .NET doesn't think you sent a standard decimal, which is why it interprets it like that. You could do the the validation via a string as a workaround, but really the caller should understand the correct format in which to send data.

P.S. This question is also relevant to the issue: Why is JSON invalid if an integer begins with 0

ADyson
  • 57,178
  • 14
  • 51
  • 63
  • Thanks @ADyson for the reply and the reference to a related question. To give some context, I'm building an ASP.Net Web-API that provides the customers an option to perform payments. I would like to have the amount field typed as decimal instead of string to expose a more strongly-typed API, but I can't predict customers sending amount containing a leading zeros, even though its an invalid JSON by definition. If's important for me to be able to catch such cases and to be able to respond with an error message. The problem is, that such cases goes unnoticed when using the `FromBody` attribute. – Liroy Alima Jan 17 '18 at 06:59
  • @LiroyAlima I would argue that if you make it very clear in your specification that you accept JSON data, including decimals, then it's reasonable to expect that callers of the webservice understand what constitutes valid JSON and adhere to that. Even if the original input comes from a user, it's up to the calling application to validate that the data is in a suitable format for transmission. If they choose to ignore the standard and send you ambiguous data, that is _their_ problem, not yours, although perhaps it would be better if the web API de-serializer didn't allow the ambiguity – ADyson Jan 17 '18 at 08:01
  • @LiroyAlima I would make it very very clear in our documentation what the correct format is, and maybe with a warning that incorrect values could be mis-interpreted, and that responsibility for this lies with the caller. You could put something in the terms and conditions about the responsibility to send correct data being the caller's, so you make it a legal issue, not a software issue! There is not a lot you can do to alter how ASP.NET interprets the values, short of writing your own deserializer and/or model binder, or applying the workaround you've already described. – ADyson Jan 17 '18 at 08:05
  • Thanks @ADyson for the reply. I understand one approach for dealing with the challenge is using EULA. What I'm interested more is on the technical aspect, of - how to implement a model binder that will ignore leading "0" or leading "0x" to enable either capturing the value as decimal (010 -> to translate as decimal 10), or reject the request (0x10 -> to be rejected). Would appropriate assistance implementing the same. – Liroy Alima Jan 21 '18 at 06:47
  • @LiroyAlima It's an interesting but very broad topic. I'm pretty sure there are Microsoft docs or tutorials showing how to replace the default de-serialiser with another one (I'm fairly sure the problem will be in deserialisation, since it's to do with the JSON standard. Model binding is simply matching field names to object properties, regardless of the value of the field), but writing your own is a fairly big job. Web API's default is JSON.NET which is open-source on GitHib, perhaps you could check out how they do it, especially in regards to this specific issue, and try to alter it. – ADyson Jan 21 '18 at 06:54
  • 1
    ADyson, appreciate the clarification between de-serialization and model binding. We've decided to go with string inputs, and writing a customize ValidationAttribute. Thanks for all your comments on this thread. – Liroy Alima Jan 22 '18 at 07:19