Home > Net >  ajax call to "asp.net core WebApi" returns a strange/weird value
ajax call to "asp.net core WebApi" returns a strange/weird value

Time:09-21

I have api controller that returns one object that contain two lists of objects.

I expect that I will get this result when I call this api using ajax:
one object that contain a long value and two lists of objects:

{
"userId":26000000000000228,
"paidInvoices":
[{"id":13060000000000039,"total":10},
{"id":13060000000000040,"total":20},
{"id":13060000000000041,"total":30}],
"unPaidInvoices":
[{"id":15060000000000039,"total":10},
{"id":15060000000000040,"total":20},
{"id":15060000000000041,"total":30}]
}

but I get unexpected repeated ids (all end with 40) :

{
"userId":26000000000000228,
"paidInvoices":
[{"id":13060000000000040,"total":10},
{"id":13060000000000040,"total":20},
{"id":13060000000000040,"total":30}],
"unPaidInvoices":
[{"id":15060000000000040,"total":10},
{"id":15060000000000040,"total":20},
{"id":15060000000000040,"total":30}]
}

Code:

public class Invoice
{
    public long Id { get; set; }
    public decimal Total { get; set; }
}

public class Invoices
{
    public long UserId { get; set; }
    public List<Invoice> PaidInvoices { get; set; }
    public List<Invoice> UnPaidInvoices { get; set; }
}

[Route("NetApi/[controller]")]
[ApiController]
public class PurchasePaymentController : ControllerBase
{
    [HttpGet("Search/{id}")]
    public Task<Invoices> Search(long id)
    {
        var paid = new List<Invoice>
        {
            new Invoice {Id = 13060000000000039, Total = 10},
            new Invoice {Id = 13060000000000040, Total = 20},
            new Invoice {Id = 13060000000000041, Total = 30}
        };
        var unPaid = new List<Invoice>
        {
            new Invoice {Id = 15060000000000039, Total = 10},
            new Invoice {Id = 15060000000000040, Total = 20},
            new Invoice {Id = 15060000000000041, Total = 30}
        };
        return Task.FromResult(new Invoices {UserId = id, PaidInvoices = paid, UnPaidInvoices = unPaid});
    }
}

I've tried to return JsonResult,it doesn't make any difference.
and I call it from this razorPage which doesn't use any layout:

@page
@model MyWebSite.Pages.Test.IndexModel
@{
    Layout = null;
}
<html>
<head>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
</head>
<body>
<h1>Welcome</h1>
<div id="result"></div>
<script>

    // if I use $.ajax or $.get , the result is the same (weird/unexpected result) 
    $(function () {
        $.ajax({
            type: "GET",
            url: "@Url.RouteUrl("NetApi", new {controller = "PurchasePayment", action = "Search", id = 26000000000000228})",
            contentType: "application/json; charset=utf-8",
            dataType: "json",
            success: getCallback
        });
    });

    $(function() {
        debugger;
        $.get("@Url.RouteUrl("NetApi", new {controller = "PurchasePayment", action = "Search", id = 26000000000000228})",
            getCallback, "json");
    });

    function getCallback(response) {
        console.log(JSON.stringify(response));
        debugger;
    }


    // but this load method works fine

    $(function() {
        $("#result").load("@Url.RouteUrl("NetApi", new {controller = "PurchasePayment", action = "Search", id = 26000000000000228})");
    });


</script>
</body>
</html>

in my Startup.cs

// ConfigureServices
services.AddMvc().AddNewtonsoftJson(options =>
                options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore
            );

// Configure
app.UseStaticFiles();
        app.UseRouting();
        app.UseAuthentication();
        app.UseAuthorization();
        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Home}/{action=Index}/{id?}");
            endpoints.MapControllerRoute(
             name: "api",
             pattern: "api/{controller}/{action}/{id?}",
             defaults: new { controller = "login", action = "Index" });

            endpoints.MapControllerRoute(
                name: "NetApi",
                pattern: "NetApi/{controller}/{action}/{id?}");
            endpoints.MapRazorPages();
        });

CodePudding user response:

I tested your Api using a Postman and everything works properly. So the problem is a Javascript. If you delete even one zero inside of your Id long numbers everything works fine inside of javascript too.

The value 15060000000000039 is actually not exceeding the maximum numeric value in JavaScript, however, the value is exceeding the range of "integral precision" or sometimes it is called the integer precision magnitude which is 2 pow 53. It is not that the wrong number is sent: rather, it is that the literal 15060000000000039 and 13060000000000041 can only be represented as precisely as 13060000000000040, and thus there was never the correct numeric value in JavaScript. (The range of integrals is about /- 253.) The problem is the way javascript rounds big integers.

So if you are still going to use javascript, the workaround I can offer is to send this long numbers as a string. Or if you need to manipulate with the numbers there are a lot of javascript BigInt libraries. But it seems you don't need it since they are just invoice numbers. So change your class to this

public class Invoice
{
    public string Id { get; set; }
    public decimal Total { get; set; }
}

and data

var paid = new List<Invoice>
        {
            new Invoice {Id = "13060000000000039", Total = 10},
            new Invoice {Id = "13060000000000040", Total = 20},
     .... and so on

or maybe use this, you will not need to change anything then

public class Invoice
{
        [JsonIgnore]
        public long Id { get; set; }
        public string InvoiceId { get { return Id.ToString(); } }
        public decimal Total { get; set; }
}

  • Related