1. Phương thức JSONP là gì?

JSONP là viết tắt của JSON With Padding . Nó là một phương pháp hay còn gọi là phương tiện giao tiếp giải quyết vấn đề truy cập dữ liệu giữa các domain khác nhau. Do chính sách cùng nguồn gốc chính vì vậy các trang web có thể lấy dữ liệu JSON được tạo động từ các nguồn khác và chế độ sử dụng này được gọi là JSONP. Dữ liệu được thu thập bằng JSONP không phải là JSON, mà là Js tùy ý. Nó được trình thông dịch Js thực thi thay vì được phân tích cú pháp bởi trình phân tích cú pháp JSON.

JSONP hiểu nôm na nhất nó là phương pháp gửi để gửi dữ liệu JSON mà ta sẽ không cần lo lắng về các vấn đề tên miền chéo. JSONP không sử dụng đối tượng XMLHttpRequest mà thay vào đó JSONP sử dụng thẻ <script> để thay thế.

Ví dụ: <script src="demo_jsonp.php">

2. Cách thức hoạt động của JSONP

Theo mặc định, ta không thể tải tệp JSON từ một miền và cổng không phải là cổng hiện tại và sử dụng nó trong ứng dụng của mình. Ta có thể phân phối ứng dụng từ localhost:8080 . Nhưng API được cung cấp bởi một ứng dụng Node.js khác đang chạy trên localhost:2001. Hoặc là ta có thể muốn truy cập một số API có sẵn công khai được phân phối dưới dạng JSON, trong trình duyệt.

Điều quan trọng nhất là máy chủ phải hỗ trợ JSONP. Ví dụ như Express cho phép ta sử dụng phương pháp Response.jsonp() , giống như Response.json() nhưng xử lý các lệnh gọi lại JSONP:

res.jsonp({ username: 'Thành Nguyễn' })

Ở phía máy khách, ta tải điểm cuối chỉ định bởi một hàm gọi lại:

<script src="https://localhost:2001/api.json?callback=theCallbackFunction"></script>

Hàm gọi lại phải là một hàm chung sẽ nhận dữ liệu JSON:

const theCallbackFunction = (data) => {
  console.log(data)
}

jQuery có một cách hữu ích để đơn giản hóa cách tiếp cận này bằng cách trừu tượng hóa JSONP trong phương pháp ajax() :

$.ajax({
  url: 'http://localhost:2001/api.json',
  dataType: 'jsonp',
  success: (data) => {
    console.log(data)
  }
})

3. Tệp lệnh tới máy chủ

Tệp trên máy chủ sẽ bao bọc kết quả bên trong một lệnh gọi hàm. Kết quả trả về một cuộc gọi đến một hàm có tên với dữ liệu JSON làm tham số. Ta cũng phải đảm bảo rằng chức năng tồn tại trên máy khách.

Ví dụ: kết quả trả về một cuộc gọi đến một hàm là myFunc với dữ liệu JSON làm tham số

<?php

$myJSON = '{"name":"Thành", "age":19, "city":"Hà Nội"}';

echo "myFunc(".$myJSON.");";

?>

Hàm có myFunc nằm trên máy khách và sẵn sàng xử lý dữ liệu JSON:

<!DOCTYPE html>
<html>
<body>

<h2>Yêu cầu JSON bằng thẻ script</h2>
<p>Tệp PHP trả về một lệnh gọi đến một hàm sẽ xử lý dữ liệu JSON.</p>
<p id="demo"></p>

<script>
function myFunc(myObj) {
  document.getElementById("demo").innerHTML = myObj.name;
}
</script>

<script src="demo_jsonp.php"></script>

</body>
</html>

4. Tạo một tập thẻ động

Cũng là ví dụ trên. Ta sẽ thực thi hàm myFunc khi trang đang tải, dựa trên vị trí đã đặt thẻ script . Điều này gây ra một số sự không hài lòng. Vì thẻ script chỉ nên được tạo khi cần.

Ví dụ : Tạo và chèn thẻ <script> khi một nút được ấn:

<!DOCTYPE html>
<html>

<body>

<p>Một thẻ script có thuộc tính src được tạo và đặt trong document.</p>
<p>Tệp PHP trả về lời gọi hàm với đối tượng JSON làm tham số.</p>

<button onclick="clickButton()">Click me!</button>

<p id="demo"></p>

<script>
function clickButton() {
  let s = document.createElement("script");
  s.src = "demo_jsonp.php";
  document.body.appendChild(s);
}

function myFunc(myObj) {
  document.getElementById("demo").innerHTML = myObj.name;
}
</script>

</body>
</html>

5. Kết quả của JSONP động

Với các ví dụ trên gần như đều là ví dụ tĩnh. Ta sẽ sửa đổi nó thành một ví dụ động. Bằng cách gửi JSON đến tệp PHP và để tệp PHP trả về một đối tượng JSON dựa trên thông tin mà nó nhận được.

<?php
header("Content-Type: application/json; charset=UTF-8");
$obj = json_decode($_GET["x"], false);

$conn = new mysqli("myServer", "myUser", "myPassword", "Northwind");
$result = $conn->query("SELECT name FROM ".$obj->$table." LIMIT ".$obj->$limit);
$outp = array();
$outp = $result->fetch_all(MYSQLI_ASSOC);

echo "myFunc(".json_encode($outp).")";
?>

Tệp PHP này đã giúp ta:

  • Chuyển đổi yêu cầu thành một đối tượng với việc sử dụng hàm PHP json_decode()
  • Truy cập cơ sở dữ liệu và điền vào một mảng với dữ liệu được yêu cầu
  • Thêm mảng vào một đối tượng
  • Chuyển đổi mảng thành JSON bằng cách sử dụng hàm json_encode()
  • Bọc hàm myFunc() xung quanh đối tượng trả về

Hàm myFunc() sẽ được gọi từ một tệp PHP:

<!DOCTYPE html>
<html>
<body>

<p>Một thẻ script có thuộc tính src được tạo và đặt trong document</p>
<p>Tệp PHP trả về một cuộc gọi đến một hàm với một đối tượng là một tham số.</p>
<p id="demo"></p>

<p>Hãy thử thay đổi thuộc tính bảng từ "customers" thành "products".</p>

<script>
const obj = { table: "customers", limit: 10 };
let s = document.createElement("script");
s.src = "jsonp_demo_db.php?x=" + JSON.stringify(obj);
document.body.appendChild(s);

function myFunc(myObj) {
  let txt = "";
  for (let x in myObj) {
    txt += myObj[x].name + "<br>";
  }
  document.getElementById("demo").innerHTML = txt;
}
</script>

</body>
</html>

6. Chức năng gọi lại

Khi ta không có quyền kiểm soát tệp máy chủ, làm cách nào để tệp máy chủ gọi đúng chức năng? Lúc này ta sẽ sử dụng một hàm gọi lại (callback) . Đôi khi tệp máy chủ sẽ cung cấp hàm gọi lại dưới dạng tham số

Ví dụ:

<!DOCTYPE html>
<html>
<body>

<h2>Yêu cầu với chức năng gọi lại</h2>
<p>Tệp PHP trả về một lệnh gọi đến hàm mà ta gửi dưới dạng một lệnh gọi lại.</p>
<p id="demo"></p>

<script>
let s = document.createElement("script");
s.src = "demo_jsonp2.php?callback=myDisplayFunction";
document.body.appendChild(s);

function myDisplayFunction(myObj) {
  document.getElementById("demo").innerHTML = myObj.name;
}
</script>

</body>
</html>