JSONP là gì? Cách hoạt động và ứng dụng trong JavaScript
Javascript nâng cao | by
Trong quá trình phát triển ứng dụng web, một trong những thách thức phổ biến mà các lập trình viên thường gặp phải là giới hạn cùng nguồn (Same-Origin Policy) – một cơ chế bảo mật của trình duyệt ngăn chặn các request từ domain này truy cập tài nguyên của domain khác. Đây là vấn đề lớn khi bạn cần lấy dữ liệu từ các API bên ngoài không hỗ trợ CORS (Cross-Origin Resource Sharing).
Để vượt qua rào cản này, JSONP (JSON with Padding) đã từng là một giải pháp phổ biến và sáng tạo, cho phép truyền dữ liệu giữa các miền bằng cách “lách luật” thông qua thẻ <script>. Mặc dù ngày nay JSONP không còn phổ biến như trước do sự phát triển của CORS, nhưng việc hiểu cách JSONP hoạt động vẫn rất hữu ích – đặc biệt trong những tình huống cần tương thích với hệ thống cũ hoặc xử lý dữ liệu từ các nguồn hạn chế.
Trong bài viết này, chúng ta sẽ tìm hiểu JSONP là gì, cách nó hoạt động, và những ứng dụng thực tế mà kỹ thuật này từng đóng vai trò quan trọng trong các ứng dụng web.
JSONP là gì?
JSONP (JSON with Padding) là một kỹ thuật lập trình được sử dụng để truyền dữ liệu giữa các miền (cross-domain) trong trình duyệt – điều mà bình thường bị giới hạn bởi Same-Origin Policy. Kỹ thuật này không phải là một định dạng mới của JSON, mà là một cách để “bọc” dữ liệu JSON trong một hàm gọi (callback).
Cơ chế hoạt động cơ bản:
Thông thường, khi ta muốn lấy dữ liệu từ một API ở miền khác (ví dụ: https://api.example.com/data
), trình duyệt sẽ chặn nếu API không hỗ trợ CORS. Tuy nhiên, có một ngoại lệ: thẻ <script> có thể tải tài nguyên từ bất kỳ nguồn nào, không bị giới hạn bởi Same-Origin Policy.
JSONP tận dụng điều này bằng cách:
Tạo một thẻ <script> động trên trang.
Gửi yêu cầu đến server với một tham số callback trên URL, ví dụ:
https://api.example.com/data?callback=myFunction
Server không trả về JSON đơn thuần, mà trả về một đoạn mã JavaScript như:
myFunction({ "name": "Alice", "age": 25 });
Trình duyệt tải đoạn script này và gọi hàm myFunction()
với dữ liệu JSON là tham số.
Ví dụ đơn giản:
<script> function handleResponse(data) { console.log("Dữ liệu từ server:", data); } // Tạo thẻ script để gọi API JSONP const script = document.createElement('script'); script.src = 'https://example.com/api/users?callback=handleResponse'; document.body.appendChild(script); </script>
Khi server trả về:
handleResponse([{ "id": 1, "name": "Alice" }, { "id": 2, "name": "Bob" }]);
Trình duyệt sẽ thực thi đoạn mã trên và gọi hàm handleResponse
với dữ liệu JSON nhận được.
Tóm tắt:
-
JSONP không phải là định dạng mới, mà là một cách truyền dữ liệu JSON thông qua một hàm JavaScript.
-
Điểm mạnh của JSONP là bypass Same-Origin Policy bằng <script>.
-
Điểm yếu là không an toàn nếu dữ liệu server không đáng tin, và chỉ hỗ trợ phương thức GET.
Ở các phần tiếp theo, ta sẽ cùng tìm hiểu cách JSONP hoạt động cụ thể và những ứng dụng thực tế của nó.
Cách hoạt động của JSONP trong JavaScript
JSONP hoạt động dựa trên việc tận dụng thẻ <script> để tải dữ liệu từ các miền khác nhau, vì trình duyệt không giới hạn nguồn của các tệp script như với XMLHttpRequest hay fetch.
Các bước hoạt động chi tiết:
Client gửi request bằng cách thêm một thẻ <script> vào DOM
JavaScript trên trang sẽ tạo một thẻ <script> động, trong đó src
trỏ đến URL API của server có hỗ trợ JSONP. URL này thường chứa một tham số callback, thể hiện tên của hàm JavaScript mà server sẽ gọi khi trả về dữ liệu.
<script src="https://example.com/api?callback=myFunction"></script>
Hoặc trong JavaScript:
function myFunction(data) { console.log("Dữ liệu nhận được:", data); } const script = document.createElement('script'); script.src = 'https://example.com/api?callback=myFunction'; document.body.appendChild(script);
Server xử lý và phản hồi về một đoạn mã JavaScript
Khi nhận được request từ client, server sẽ tạo ra một phản hồi không phải là JSON thông thường, mà là một lời gọi hàm với dữ liệu JSON được “bọc” bên trong.
Ví dụ, server phản hồi:
myFunction({ "name": "Alice", "age": 25 });
Lưu ý: myFunction
là tên hàm mà client đã gửi trong query string (callback=myFunction
).
Trình duyệt thực thi đoạn mã script vừa tải về
Vì trình duyệt xử lý nội dung của <script>
như mã JavaScript thông thường, hàm myFunction()
sẽ được gọi tự động với dữ liệu JSON từ server.
Ví dụ đầy đủ:
Client-side:
<!DOCTYPE html> <html> <head> <title>Demo JSONP</title> </head> <body> <h2>Kết quả:</h2> <div id="result"></div> <script> function displayUser(data) { document.getElementById("result").innerText = `Tên: ${data.name}, Tuổi: ${data.age}`; } const script = document.createElement('script'); script.src = "https://example.com/api?callback=displayUser"; document.body.appendChild(script); </script> </body> </html>
Server-side (trả về):
displayUser({ "name": "Alice", "age": 25 });
Ưu điểm:
-
Hoạt động được trong trình duyệt cũ, không cần CORS.
-
Đơn giản, dễ sử dụng khi chỉ cần GET dữ liệu.
Lưu ý:
-
Chỉ hỗ trợ phương thức GET.
-
Tiềm ẩn rủi ro bảo mật (nếu server không đáng tin, có thể trả về mã độc).
-
Không có cách bắt lỗi trực tiếp như
.catch()
trongfetch()
.
Ưu điểm và Hạn chế của JSONP trong JavaScript
Ưu điểm của JSONP
Truy cập tài nguyên từ domain khác mà không cần CORS
Trong mô hình truyền thống, trình duyệt giới hạn việc gọi dữ liệu giữa các domain khác nhau (cross-origin) do chính sách bảo mật Same-Origin Policy.
Tuy nhiên, JSONP vượt qua giới hạn này bằng cách sử dụng thẻ <script>
, vì trình duyệt cho phép tải script từ bất kỳ nguồn nào mà không cần cấu hình đặc biệt từ phía server (không cần thiết lập CORS headers).
Dễ triển khai
JSONP hoạt động chỉ với một vài dòng mã đơn giản, không cần đến thư viện phức tạp. Chỉ cần tạo một function callback và thêm thẻ <script>
trỏ tới API là có thể nhận dữ liệu.
Hỗ trợ tốt trên trình duyệt cũ
Trước khi có chuẩn fetch()
hoặc XMLHttpRequest
với hỗ trợ CORS, JSONP từng là giải pháp duy nhất và hiệu quả để lấy dữ liệu từ bên ngoài trong các trình duyệt đời cũ như IE8, IE9.
Hạn chế của JSONP
Chỉ hỗ trợ phương thức GET
Do sử dụng <script src="...">
, JSONP không thể gửi dữ liệu thông qua POST, PUT, DELETE, hay thêm header tuỳ chỉnh. Điều này làm JSONP bị giới hạn trong khả năng giao tiếp và không phù hợp cho các ứng dụng yêu cầu thao tác phức tạp.
Không an toàn – dễ bị tấn công XSS (Cross-site Scripting)
Vì dữ liệu trả về là một đoạn mã JavaScript thực thi trực tiếp, nếu server không kiểm soát kỹ hoặc bị tấn công, hacker có thể chèn mã độc vào phản hồi.
Ví dụ:
myCallback(<script>alert('Bạn bị tấn công!')</script>)
Do đó, việc tin tưởng tuyệt đối vào JSONP từ nguồn bên ngoài là nguy hiểm, và JSONP không được khuyến khích sử dụng trong môi trường production.
Không thể bắt lỗi dễ dàng
Không giống như fetch()
hoặc XMLHttpRequest
, khi gọi JSONP nếu có lỗi (ví dụ sai URL, server không phản hồi), bạn không có cơ chế .catch()
hay onerror
hiệu quả để xử lý lỗi. Điều này gây khó khăn khi muốn tạo ra trải nghiệm người dùng mượt mà.
Không hỗ trợ gửi dữ liệu kèm theo header tùy chỉnh
Một số API yêu cầu client gửi token xác thực (Bearer token hoặc API Key trong header). JSONP không hỗ trợ việc gửi custom headers, vì vậy không thể tương tác với các API như vậy.
Cách sử dụng JSONP trong JavaScript
Để sử dụng JSONP, ta không thể dùng fetch()
hoặc XMLHttpRequest
, mà thay vào đó là tạo thẻ <script>
động, cho phép tải dữ liệu từ domain khác như đang tải một file JavaScript. Dữ liệu sẽ được trả về và tự động thực thi dưới dạng một lời gọi hàm JavaScript.
Định nghĩa hàm callback để xử lý dữ liệu
Hàm callback là nơi bạn sẽ xử lý dữ liệu JSON mà server trả về. Hàm này phải nằm ở phạm vi global (bên ngoài mọi function khác) để trình duyệt có thể gọi được khi phản hồi từ server đến.
function handleData(data) { console.log("Received:", data); // Ví dụ: hiển thị dữ liệu vào trang web document.getElementById("output").innerText = `Tên: ${data.name}, Tuổi: ${data.age}`; }
2. Tạo thẻ <script>
động bằng JavaScript
Bạn có thể dùng document.createElement()
để tạo một thẻ <script>
mới:
let script = document.createElement("script");
Gắn URL API vào src
, có chứa tham số callback=handleData
Thông thường các API hỗ trợ JSONP sẽ cho phép truyền tên hàm callback thông qua query string, ví dụ:
https://api.example.com/data?callback=handleData
script.src = "https://api.example.com/data?callback=handleData";
Gắn thẻ script vào tài liệu HTML
Khi bạn gắn thẻ <script>
vào DOM, trình duyệt sẽ gửi request tới API. Phản hồi sẽ là một đoạn mã JavaScript dạng:
handleData({ "name": "Alice", "age": 25 });
Và khi đoạn mã này được trình duyệt thực thi, hàm handleData
bạn định nghĩa trước đó sẽ được gọi với dữ liệu JSON là đối số.
document.body.appendChild(script);
Ví dụ hoàn chỉnh:
<!DOCTYPE html> <html> <head> <title>JSONP Demo</title> </head> <body> <h2>Dữ liệu từ API JSONP:</h2> <div id="output">Đang tải...</div> <script> // Bước 1: Định nghĩa hàm callback function handleData(data) { console.log("Received:", data); document.getElementById("output").innerText = `Tên: ${data.name}, Tuổi: ${data.age}`; } // Bước 2: Tạo thẻ script và gắn URL let script = document.createElement("script"); script.src = "https://api.example.com/data?callback=handleData"; // Giả lập URL JSONP // Bước 3: Gắn script vào DOM document.body.appendChild(script); </script> </body> </html>
Lưu ý: Với ví dụ trên, bạn cần thay "https://api.example.com/data?callback=handleData"
bằng một API thực tế hỗ trợ JSONP như https://jsonp.afeld.me/
hoặc các mock API có cấu hình JSONP.
Ứng dụng thực tế của JSONP trong JavaScript
JSONP từng là một giải pháp rất phổ biến khi các trình duyệt chưa hỗ trợ chuẩn CORS. Dưới đây là một số tình huống thực tế JSONP thường được sử dụng:
Tích hợp dữ liệu từ các API bên thứ ba không hỗ trợ CORS
Ví dụ, trước đây các API công khai như Twitter, Flickr, hoặc Wikipedia chưa hỗ trợ CORS. JSONP cho phép các trang web bên ngoài truy cập dữ liệu từ những API này bằng cách chèn <script>
để “lấy” dữ liệu.
Hiển thị nội dung động trong widget hoặc trang web tĩnh
JSONP giúp các widget nhúng (ví dụ hiển thị tin tức, thời tiết, thống kê…) từ domain khác có thể nhúng vào website của bạn mà không cần server trung gian.
Ví dụ:
<script src="https://example.com/widget?callback=renderWidget"></script>
Dùng trong các thư viện cũ như jQuery
jQuery hỗ trợ JSONP một cách đơn giản với cú pháp $.ajax()
:
$.ajax({ url: "https://api.example.com/data?callback=?", dataType: "jsonp", success: function(response) { console.log(response); } });
Thư viện sẽ tự động tạo hàm callback ngẫu nhiên và xử lý phản hồi từ server.
So sánh JSONP và CORS trong JavaScript
Tiêu chí | JSONP | CORS |
---|---|---|
Hỗ trợ method | Chỉ hỗ trợ GET |
Hỗ trợ đầy đủ: GET , POST , PUT , DELETE … |
Bảo mật | Không an toàn – dễ bị XSS | Bảo mật hơn nhờ kiểm soát qua Access-Control-Allow-Origin |
Cách dùng | Dùng thẻ <script> để tải dữ liệu |
Dùng fetch() hoặc XMLHttpRequest |
Trình duyệt cũ | Tương thích với hầu hết trình duyệt | Một số trình duyệt cũ không hỗ trợ đầy đủ |
Tóm lại: CORS là giải pháp hiện đại, bảo mật và linh hoạt hơn. JSONP chỉ nên được sử dụng khi bắt buộc, như khi API không hỗ trợ CORS.
Lưu ý khi sử dụng JSONP trong JavaScript
Kiểm soát dữ liệu đầu vào từ server
Do dữ liệu phản hồi là mã JavaScript và được thực thi trực tiếp, nên nếu server trả về mã độc, trình duyệt sẽ chạy luôn. Vì vậy, chỉ nên sử dụng JSONP với các nguồn đáng tin cậy.
Không nên dùng trong hệ thống nhạy cảm
Tránh sử dụng JSONP trong các ứng dụng có yêu cầu bảo mật cao như: thanh toán, quản trị, hệ thống nội bộ…
Chỉ dùng JSONP khi thực sự cần thiết
Trong hầu hết các trường hợp hiện đại, CORS là giải pháp tốt hơn. Chỉ dùng JSONP nếu:
-
API bạn cần truy cập không hỗ trợ CORS.
-
Bạn không thể điều chỉnh phía server.
-
API đó hỗ trợ callback JSONP.
Kết bài
JSONP từng là một giải pháp sáng tạo và tiện lợi để vượt qua giới hạn cùng-origin trong trình duyệt trước khi CORS trở nên phổ biến. Với cơ chế đơn giản dựa trên thẻ <script>
và hàm callback, JSONP cho phép lấy dữ liệu từ các nguồn bên ngoài một cách nhanh chóng. Tuy nhiên, do những hạn chế về bảo mật và chức năng, JSONP hiện chỉ nên sử dụng trong các trường hợp đặc biệt khi không còn lựa chọn nào khác.
Ngày nay, với sự hỗ trợ rộng rãi của CORS và các công cụ hiện đại như fetch
hay axios
, JSONP dần trở thành một giải pháp cũ nhưng vẫn hữu ích trong việc hiểu rõ hơn về cơ chế giao tiếp giữa client và server trong JavaScript. Nếu bạn đang học hoặc bảo trì các hệ thống cũ, việc nắm vững JSONP vẫn là một kỹ năng cần thiết trong hành trang phát triển web của bạn.