2

I'm responsible for maintaining a public ASP.NET Core 8 Web API. One endpoint accepts POST data like this:

{
    "xid": 12345,
    "message": "Hello, world!"
}

On the .NET side, the controller action looks like this:

[HttpPost]
[Route("SendMessage")]
public async Task<ActionResult> SendMessage(MessageDto message)
{
    // ...
}

And finally, MessageDto looks like this on the API side - importantly, the id field is a long:

public class MessageDto
{
    public long XID { get; set; }
    public string Message { get; set; }
}

The problem

Before I describe the problem, let me just emphasize that I did not originally write this code!

I'm being asked to change the API to support XID values that include a leading zero (for example, 06789). Obviously the correct answer here is to use a string for XID instead of a long, but since this is an active public API, I can't change the data contract.

Moreover, in ASP.NET Core Web API, JSON numbers with leading zeroes don't simply drop the zero. Instead, they throw a JSON parser error:

{
    "type": "https://tools.ietf.org/html/rfc9110#section-15.5.1",
    "title": "One or more validation errors occurred.",
    "status": 400,
    "errors": {
        "$.xid": [
            "Invalid leading zero before '6'. Path: $.xid | LineNumber: 1 | BytePositionInLine: 11."
        ]
    }
}

I understand that this is because leading zeroes on whole numbers are simply not permitted in the JSON spec. Again, this field should have been a string from the get-go, but our users are already passing numbers.

What I've tried:

  • Changing the property to a string on the API side: can't accept numbers anymore
  • Writing a custom JsonConverter for type long: the "invalid leading zero" 400 is thrown before the converter is reached
  • Changing the property to a string and writing a custom JsonConverter for type string that turns long into string: the "invalid leading zero" 400 is thrown before the converter is reached

Given that I can't ask our users to change how they're submitting request data, is there any way to add custom handling for this field? Ideally, a solution would handle both number and string in the JSON (since it's possible some users will switch to submitting XID as a string because of the leading zero). But really, I just need some way to intercept this request before .NET decides it's bad JSON and returns a 400.

Alternately, is there a way to handle the query data as something other than JSON and avoid the parser entirely?

Thank you in advance for any and all advice!

5
  • 5
    You are basically being asked (1) to forget about JSON compatibility and standards, or (2) to break the contract by changing a number to a string. The 1st I would never recommend - if it's even possible, given the problems you already encountered. That forces option 2 with very simple reasoning: A change in requirements means a new contract. It is a new API version. You need to tell them that API v1 can only support plain numbers (no leading zeroes) and API v2 will support strings. v1 clients can talk to the v1 API, v2 clients can talk to the v2 API. Normal practice, happens all the time. Commented Sep 30 at 23:09
  • 2
    API versioning would be an ideal solution but probably a costly one if versioning isn't already around. If there are few existing users, I'd definitely consider asking the users if they would be willing to conform to a breaking contract change, esp. if API is in early phases. Most probably, this would get you to a cleanest state w/ minimal effort. If that's not possible, I'd consider adding a new optional string field. New clients could fill the field while old users could go on as-is. Of course, some might argue that this would introduce tech debt and I wouldn't disagree but it has its place. Commented Oct 1 at 3:02
  • 1
    Thank you both for the comments! I'm talking with management today about these considerations. Commented Oct 1 at 13:09
  • If xid happen to need a fixed number of digits, then you have all the info to convert xid numbers to fixed-width strings by prepadding them with zeroes to achieve the desired number of digits. If it is not a fixed width, then maybe a new optional field could specify the number of digits (or the number of zeroes to prepad). The default would be to prepad nothing. Commented Oct 2 at 13:40
  • 1
    We have a fairly high number of integrators, but not many actually active, so we're going to reach out to them individually about changing the contract to use a string. Thanks again for the advice! Commented Oct 3 at 19:04

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.