JSONP is a classic solution for cross-domain problems, and I know how it works. However, as a person who has been spoiled by the separated development of modern times and silently enjoys the convenience brought by CORS, it is time to study the wisdom of predecessors.

References: Huli — CORS Complete Manual part II: How to Solve CORS Problems?

JSON with Padding. The principle is very simple, using the script tag will not be targeted by the browser’s same origin policy, can obtain the full content of the requested resource. For example, using CDN to import jquery.js, we can use $to refer to jquery on the page. Similarly, the data obtained using JSONP is also in the script tag waiting to be called; We define a function in another script tag, JOSNP gets the data to call the function, and when the script is executed, it automatically executes the function we defined.

Having said all these definitions and principles, you probably don’t know how to write it, so let’s look at the code.

Level 1 Usage

Generally we can use data through

<script>
    var user = {
        name: 'eleven'.age: 25
    }
</script>
<script>
    console.log(JSON.stringify(user))
</script>
Copy the code

This time to demand, user is the login user information, can not be static data, so rewrite

<script src="http://localhost:8888/getUserInfo/1">
// Obtain the information about user 1
Var = user = {name: 'eleven', age: 25}
</script>
<script>
    console.log(JSON.stringify(user))
</script>
Copy the code

This enables a simple JSONP cross-domain. Cross-domain is a problem shared by both ends and more likely to be solved at the back end. Cross-domain can never be understood by looking only at one end, so take a quick look at the back end code:

// Use express.js as the back-end framework
var express = require('express');
var app = express();

// Need to return the front-end data
const users = {
    '1': {
        name: 'eleven'.age: 25
    },
    '2': {
        name: 'twelve'.age: 35}}// Simply use the table query to return the correct data
app.get('/getUserInfo/:userId'.function (req, res) {
  const userId = req.params.userId;
  res.end(`var user = The ${JSON.stringify(users[userId])}`);
});

// Listen for a request on port 8888
app.listen(8888.function () {
  console.log('Example app listening on port 8888! ');
});
Copy the code

Second usage

At this point, we change the requirements again, we are the administrator, we need the information of multiple users, can not only get user ID 1, so we can write like this

<body>
  <button onclick="getUser(1)">user1</button>
  <button onclick="getUser(2)">user2</button>
    <script>
    function getUser(userId) {
        // Add a script element
        const script = document.createElement('script')

        // Add SRC to the new script tag
        script.src = 'http://localhost:8888/getUserInfo/' + userId

        // Insert into the body
        document.body.appendChild(script)

        // Print the result
        console.log(JSON.stringify(user))
    }
    </script>
</body>
Copy the code

This is fine in principle, but in practice the logic will fail if the interface is a step too late and the data is actually retrieved after console.log(json.stringify (user)) is executed.

Therefore, we can use callback functions to print the fetch results asynchronously, which leads to the third level of usage.

Level 3 Usage

First we need a callback function to print the interface data, function uUserInfo:

<body>
    <button onclick="getUser(1)">user1</button>
    <button onclick="getUser(2)">user2</button>
    <script>
        function uUserInfo(info) {
            // Print the result
            console.log(JSON.stringify(info))
        }
        function getUser(userId) {
            const script = document.createElement('script')
            script.src = 'http://localhost:8888/getUserInfo/' + userId
            document.body.appendChild(script)
        }
    </script>
</body>
Copy the code

The front end alone has no way of knowing when to retrieve the data, so it is up to the back end to execute the callback:

var express = require('express');
var app = express();

// Need to return the front-end data
const users = {
    '1': {
        name: 'eleven'.age: 25
    },
    '2': {
        name: 'twelve'.age: 35}}// Simply use the table query to return the correct data
app.get('/getUserInfo/:userId'.function (req, res) {
  const userId = req.params.userId;

  // Notice here! Returns a string for the function call, which is automatically executed when the script node is inserted into the body, passing the result as an argument
  res.end(`uUserInfo(The ${JSON.stringify(users[userId])}) `);
});

// Listen for a request on port 8888
app.listen(8888.function () {
  console.log('Example app listening on port 8888! ');
});
Copy the code

This solution looks pretty good, except for the fact that uUserInfo is a misspelled word. The new ocD front-end wants to change this small bug, so to inform the backend to do the modification, the backend said: very troublesome, if you agree this time, the next time also want to change the name how to do, please solve the naming problem once and for all.

All right, let’s go to the next level.

The last layer of usage

Since function definition is done by the front end, why not let the front end inform the back end of function names? So:

<body>
    <button onclick="getUser(1)">user1</button>
    <button onclick="getUser(2)">user2</button>
    <script>
        function useUserInfo(info) {
            // Print the result
            console.log(JSON.stringify(info))
        }
        function getUser(userId) {
            const script = document.createElement('script')
            script.src = `http://localhost:8888/getUserInfo/${userId}? cb=useUserInfo`
            document.body.appendChild(script)
        }
    </script>
</body>
Copy the code

Pass the function name to the back end via the URL, and the back end simply writes the passed argument:

var express = require('express');
var app = express();

// Need to return the front-end data
const users = {
    '1': {
        name: 'eleven'.age: 25
    },
    '2': {
        name: 'twelve'.age: 35}}// Simply use the table query to return the correct data
app.get('/getUserInfo/:userId'.function (req, res) {
  const userId = req.params.userId;
  const fnName = req.query.cb;

  // Notice here! Returns a string for the function call, which is automatically executed when the script node is inserted into the body, passing the result as an argument
  res.end(`${fnName}(The ${JSON.stringify(users[userId])}) `);
});

// Listen for a request on port 8888
app.listen(8888.function () {
  console.log('Example app listening on port 8888! ');
});
Copy the code

No problem! At this point we have put all the principles in JSONP into practice. Next, the technical summary:

JSONP is a result of the browser’s same-origin policy, which prevents Ajax from responding across domains when it gets data. The IMG script tag in HTML is not restricted by the same origin policy, so script can be used to obtain resources in different domains. The obtained content will be like other JS files obtained through the CDN, inserting the string directly into the script tag into executable code. This way, you can return a function call string, insert the result as a parameter into a script, and automatically execute functions defined inside other script tags, completing a JSONP operation.

The upper limit of JSONP is limited by the upper limit of script tags: only GET requests can be sent.