API trả về một ID dài, nhưng khi mở màn hình chi tiết thì chữ số cuối đã đổi. Lỗi có thể xuất hiện trước cả bước hiển thị: client đã đọc ID như Number và làm tròn nó. Đổi sang BigInt sau đó không khôi phục được chữ số ban đầu.
Bài viết tập trung vào ID số nguyên lớn đi qua JSON. Một ID thường dùng để đối chiếu, không cần cộng trừ; vì vậy giữ chuỗi xuyên suốt thường là contract dễ kiểm soát hơn việc chuyển kiểu ở nhiều nơi.
1. Có số nguyên biểu diễn được nhưng không còn an toàn
const a = Number('9007199254740992');
const b = Number('9007199254740993');
console.log(a === b); // true
console.log(Number.MAX_SAFE_INTEGER); // 9007199254740991
console.log(Number.isSafeInteger(a)); // false
MAX_SAFE_INTEGER là giới hạn nguyên dương an toàn của Number. Vượt giới hạn không có nghĩa mọi giá trị lập tức sai, mà không còn bảo đảm phân biệt chính xác các số nguyên liên tiếp. Hai chuỗi ID khác nhau trong ví dụ trở thành cùng một giá trị.
Kiểm tra dữ liệu tại từng biên: kiểu cột database, driver backend, serializer JSON, client và mã dựng URL. Database lưu đúng không chứng minh toàn bộ đường truyền giữ đúng.
2. JSON hợp lệ vẫn có thể mất chính xác khi parse
const parsed = JSON.parse('{"id":9007199254740993}');
console.log(String(parsed.id)); // 9007199254740992
console.log(String(BigInt(parsed.id))); // 9007199254740992
const exact = JSON.parse('{"id":"9007199254740993"}');
console.log(exact.id); // 9007199254740993
console.log(typeof exact.id); // string
Với JSON.parse mặc định trong ví dụ, token số được đọc thành Number. BigInt nhận giá trị đã bị làm tròn nên không biết bản gốc là gì. String(parsed.id) cũng chỉ biến giá trị sai thành chuỗi.
Nếu thiết kế API dùng ID chuỗi, backend phải phát ra chuỗi ngay từ giá trị còn chính xác. Đừng để backend làm tròn trước rồi chỉ thêm dấu ngoặc kép ở bước cuối. Khi API bên ngoài đã phát số lớn, cần cơ chế đọc không mất mát được kiểm chứng trên runtime mục tiêu, hoặc yêu cầu nhà cung cấp sửa contract.
3. Kiểm tra định dạng mà vẫn giữ ID là chuỗi
Ví dụ chọn contract riêng: ID dương dạng thập phân ASCII, không có số 0 đầu và tối đa 20 chữ số. Đây là giới hạn minh họa, không khẳng định mọi database đều chấp nhận toàn bộ miền này.
function requireId(raw) {
if (typeof raw !== 'string' || !/^[1-9][0-9]{0,19}$/.test(raw)) {
throw new TypeError('Expected a canonical positive decimal ID');
}
return raw;
}
console.log(requireId('9007199254740993')); // 9007199254740993
Hàm không trim hoặc tự chuyển Number thành chuỗi để tránh che lỗi của bên gọi. Nếu hệ thống cho phép ID 0, số âm hay số 0 đầu, hãy đổi contract rõ ràng. Giới hạn thực tế của backend vẫn cần kiểm tra riêng.
Dùng chuỗi đã xác nhận làm khóa tra cứu và tham số đường dẫn; không gọi Number, parseInt hoặc toán tử cộng một ngôi chỉ để “chuẩn hóa” ID. Kiểm tra định dạng cũng không thay thế bước xác thực quyền truy cập bản ghi ở server.
4. Dùng BigInt khi thật sự cần phép toán nguyên
const value = BigInt('9007199254740993');
console.log(String(value + 1n)); // 9007199254740994
console.log(String(5n / 2n)); // 2
try {
console.log(value + 1);
} catch (error) {
console.log(error.name); // TypeError
}
Khởi tạo từ chuỗi chính xác hoặc literal có hậu tố n. Các toán hạng của phép cộng ở đây phải cùng kiểu; 1n khác 1. Phép chia BigInt bỏ phần lẻ, nên không dùng nó như kiểu số thập phân tự động.
5. Quy định cách xuất BigInt ra JSON
const id = BigInt('9007199254740993');
try {
JSON.stringify({ id });
} catch (error) {
console.log(error.name); // TypeError
}
const wire = JSON.stringify({ id: id.toString() });
console.log(wire); // {"id":"9007199254740993"}
Trong môi trường mặc định của ví dụ, JSON.stringify không trực tiếp mã hóa BigInt. Chuyển đúng trường id về chuỗi tạo contract rõ ràng. Tránh tự khôi phục mọi chuỗi chỉ gồm chữ số thành BigInt khi đọc JSON: mã bưu chính hay mã có số 0 đầu có thể cần giữ nguyên văn bản.
6. Kiểm thử toàn bộ hành trình của ID
- Dùng hai ID liên tiếp quanh ngưỡng an toàn, xác nhận chúng vẫn khác nhau.
- Kiểm tra kiểu string trong JSON, state, khóa Map và URL gửi lại API.
- Thử round trip và so sánh đúng toàn bộ chuỗi, không chỉ độ dài.
- Thử dữ liệu sai kiểu, chuỗi rỗng, số mũ, khoảng trắng và ID quá dài.
- Kiểm tra giới hạn thật của backend cùng quyền truy cập bản ghi.
Các đoạn code đã chạy bằng Node.js local. Bài chưa kiểm thử driver hay API production của một ứng dụng nghiệp vụ. Tài liệu đối chiếu thêm: BigInt và JSON.parse.
Quyết định kiểu dữ liệu ở biên API giúp tránh những lần chuyển đổi âm thầm. Với ID chỉ dùng để định danh, giữ chuỗi chính xác từ nguồn đến đích là một lựa chọn dễ kiểm tra.




Chưa có bình luận. Hãy là người đầu tiên chia sẻ ý kiến.