Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Big Integer in JSON getting corrupted by Angular but not by CURL?

I have created a Django REST API using Django Rest Framework.

I use 3 different clients to hit this app:

  • Curl (for debugging purposes)
  • Swagger Interface (for debugging purposes)
  • My Angular application

I have a perplexing problem that data returned by the API is being corrupted by Swagger and Angular but not by Curl.

My Django model looks like this:

class MyModel1(CachingMixin, models.Model):
    id = models.BigIntegerField(default=make_id, primary_key=True)
    name = models.CharField(max_length=50, null=False, blank=False,)

The make_id() method referenced above is described here. I recently implemented this change from the standard Django assigned and auto-incremented primary key. As part of that change, I converted id from an IntegerField to a BigIntegerField.

I have some other Django view code (which I have not shown here) that creates an endpoint called GetMyModel1ByName. That endpoint returns a serialized instance of MyModel1. Here is the curl showing what happens when I hit that endpoint. It works perfectly:

$ curl http://localhost:4212/api/getMyModel1ByName/?name=my-name
{"id": 10150133855458395, "name": "my-name"}

Now here is what happens when I hit the same endpoint from Chrome's Developer console:

> $http = angular.element(document.body).injector().get('$http');
> $http.get("http://localhost:4212/api/getMyModel1ByName/?name=my-name").then(function(response) { console.log(JSON.stringify(response.data)) }).catch(function (error) { console.log(error.data) });
{"id":10150133855458396, "name":"my-name"}

As you can see curl reports the ID as 10150133855458395. That's correct. That's what is in the Database. However Angular reports it as 10150133855458396. The final digit is wrong. The difference is 1. It's very surprising!

This is a truly perplexing error. My Django code is so simple that I'm very confident that there is no mistake in it. Instead I feel that change the id field from IntegerField to BigIntegerField might have caused this problem. Why is it happening and what is the solution?? It's ruining the functionality of my application. I'm seeing the same corruption when I hit this endpoint through Swagger.

EDIT: The other questioner is experiencing the same problem I am. It's great to know that I'm not the only one!! However, the answers on that question are not really answers. They don't explain the cause. And they don't tell me how I should solve the issue in my Angular JS code.

like image 853
Saqib Ali Avatar asked Jun 17 '16 21:06

Saqib Ali


2 Answers

You are trying to store a number which is greater than the safe maximum javascript integer value(+/- 9007199254740991).

Your make_id generator should return values with respect to javascript limitations.

For more information read: http://blog.vjeux.com/2010/javascript/javascript-max_int-number-limits.html

like image 108
Yevgeniy Shchemelev Avatar answered Nov 12 '22 06:11

Yevgeniy Shchemelev


TLDR

To cut a long story short. Have your view return the response as String.

{"id": "10150133855458395", "name": "my-name"}

The Long Story

Let's narrow this down. In fact you can and should test this without the involvement of django at all. Please save the json you get from curl http://localhost:4212/api/getMyModel1ByName/?name=my-name into the /static/ folder in your django project. Now when you fetch it django is no longer involved in the picture. You can even host this file on some other kind of server. Let's call it hello.json

Now if you type in http://localhost:4212/static/hello.json you should get.

{"id": 10150133855458395, "name": "my-name"}

Update:

If you don't see the JSON data correctly, that means you have the problem described in the Q&A in the first comment. You have a dodgy extension that messes up the JSON. Disable all extensions and try again. Enable them back one by one.

Now let's create a small angular script.

<!DOCTYPE html>
<html>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<body>

  <div ng-app="myApp" ng-controller="myCtrl">

    <h1>{{myjson}}</h1>

  </div>


<script>
   var app = angular.module('myApp', []);
   app.controller('myCtrl', function($scope, $http) {
   $http.get("/static/hello.json")
     .then(function(response) {
        console.log(response.data)
        $scope.myjson = response.data;
      });
   });
</script>

</body>
</html>

Then saving this as angular.html in your static folder, you can load it in your browser as http://localhost:4212/static/angular.html which shows.

{"id":10150133855458396,"name":"my-name"}

This is definitely not the correct result. However there is no BigIntegerField and there is no django here. The issue is entirely with in the browser and angular. We have managed to narrow the problem. it is entirely within angular as you can see from the response that's retrieved by the browser.

Chrome dev tools showing that the correct JSON is retrieved

Angular is choking on large numbers. Send them as strings instead.

like image 22
e4c5 Avatar answered Nov 12 '22 05:11

e4c5