Để gửi thông báo đẩy, trước tiên, bạn phải xin phép người dùng, sau đó đăng ký thiết bị của họ với một dịch vụ thông báo đẩy. Quá trình này bao gồm việc sử dụng JavaScript API để lấy đối tượng PushSubscription, sau đó bạn gửi đối tượng này đến máy chủ của mình.
JavaScript API quản lý quy trình này một cách đơn giản. Hướng dẫn này giải thích toàn bộ quy trình, bao gồm cả việc phát hiện tính năng, yêu cầu cấp quyền và quản lý quy trình đăng ký.
Phát hiện tính năng
Trước tiên, hãy kiểm tra xem trình duyệt có hỗ trợ tính năng nhắn tin đẩy hay không. Bạn có thể kiểm tra khả năng hỗ trợ đẩy bằng hai bước kiểm tra:
- Kiểm tra
serviceWorkertrên đối tượngnavigator. - Kiểm tra
PushManagertrên đối tượngwindow.
if (!('serviceWorker' in navigator)) {
// Service Worker isn't supported on this browser, disable or hide UI.
return;
}
if (!('PushManager' in window)) {
// Push isn't supported on this browser, disable or hide UI.
return;
}
Mặc dù khả năng hỗ trợ của trình duyệt cho cả trình chạy dịch vụ và thông báo đẩy đang tăng lên, nhưng hãy luôn phát hiện cả hai tính năng và dần cải thiện ứng dụng của bạn.
Đăng ký trình chạy dịch vụ
Sau khi phát hiện tính năng, bạn biết rằng trình chạy dịch vụ và thông báo đẩy được hỗ trợ. Tiếp theo, hãy đăng ký trình chạy dịch vụ.
Khi đăng ký một trình chạy dịch vụ, bạn sẽ cho trình duyệt biết vị trí của tệp trình chạy dịch vụ. Tệp này là tệp JavaScript, nhưng trình duyệt cấp cho tệp này quyền truy cập vào các API của trình chạy dịch vụ, bao gồm cả tính năng nhắn tin đẩy. Cụ thể, trình duyệt sẽ chạy tệp trong môi trường trình chạy dịch vụ.
Để đăng ký một trình chạy dịch vụ, hãy gọi navigator.serviceWorker.register() và truyền đường dẫn đến tệp của bạn. Ví dụ:
function registerServiceWorker() {
return navigator.serviceWorker
.register('/service-worker.js')
.then(function (registration) {
console.log('Service worker successfully registered.');
return registration;
})
.catch(function (err) {
console.error('Unable to register service worker.', err);
});
}
Hàm này cho trình duyệt biết vị trí của tệp trình chạy dịch vụ. Tại đây, tệp trình chạy dịch vụ nằm ở /service-worker.js. Sau khi bạn gọi register(), trình duyệt sẽ thực hiện các bước sau:
Tải tệp trình chạy dịch vụ xuống.
Chạy JavaScript.
Nếu tệp chạy đúng cách mà không có lỗi, thì lời hứa do
register()trả về sẽ được thực hiện. Nếu xảy ra lỗi, lời hứa sẽ từ chối.
Lưu ý: Nếu register() từ chối, hãy kiểm tra JavaScript của bạn để tìm lỗi chính tả hoặc lỗi trong Chrome DevTools.
Khi register() phân giải, phương thức này sẽ trả về một ServiceWorkerRegistration. Bạn sử dụng thông tin đăng ký này để truy cập vào PushManager API.
Khả năng tương thích của trình duyệt với PushManager API
Yêu cầu cấp quyền
Sau khi đăng ký trình chạy dịch vụ và được cấp quyền, hãy xin phép người dùng để gửi thông báo đẩy.
API để xin cấp quyền rất đơn giản. Tuy nhiên, API này gần đây đã thay đổi từ việc nhận lệnh gọi lại sang trả về một Promise. Vì không thể xác định phiên bản API mà trình duyệt triển khai, nên bạn phải triển khai và xử lý cả hai phiên bản.
function askPermission() {
return new Promise(function (resolve, reject) {
const permissionResult = Notification.requestPermission(function (result) {
resolve(result);
});
if (permissionResult) {
permissionResult.then(resolve, reject);
}
}).then(function (permissionResult) {
if (permissionResult !== 'granted') {
throw new Error("We weren't granted permission.");
}
});
}
Trong đoạn mã trước, lệnh gọi đến Notification.requestPermission() sẽ hiển thị lời nhắc cho người dùng:

Sau khi người dùng tương tác với lời nhắc cấp quyền bằng cách chọn Cho phép, Chặn hoặc đóng lời nhắc, bạn sẽ nhận được kết quả dưới dạng chuỗi: 'granted', 'default' hoặc 'denied'.
Trong mã mẫu, lời hứa do askPermission() trả về sẽ được thực hiện nếu quyền được cấp; nếu không, lời hứa sẽ gửi lỗi và từ chối.
Xử lý trường hợp hiếm gặp khi người dùng nhấp vào nút Chặn. Nếu điều này xảy ra, ứng dụng web của bạn không thể yêu cầu người dùng cấp quyền lại. Người dùng phải bỏ chặn ứng dụng của bạn theo cách thủ công bằng cách thay đổi trạng thái quyền của ứng dụng trong bảng điều khiển cài đặt. Hãy cân nhắc kỹ thời điểm và cách thức yêu cầu cấp quyền, vì nếu người dùng nhấp vào Chặn, thì việc đảo ngược quyết định đó sẽ không dễ dàng.
Hầu hết người dùng đều cấp quyền nếu họ hiểu lý do ứng dụng yêu cầu quyền đó.
Tài liệu này thảo luận về cách một số trang web phổ biến yêu cầu cấp quyền ở phần sau của tài liệu này.
Đăng ký người dùng bằng PushManager
Sau khi đăng ký trình chạy dịch vụ và được cấp quyền, bạn có thể đăng ký người dùng bằng cách gọi registration.pushManager.subscribe().
function subscribeUserToPush() {
return navigator.serviceWorker
.register('/service-worker.js')
.then(function (registration) {
const subscribeOptions = {
userVisibleOnly: true,
applicationServerKey: urlBase64ToUint8Array(
'BEl62iUYgUivxIkv69yViEuiBIa-Ib9-SkvMeAtA3LFgDzkrxZJjSgSnfckjBJuBkr3qBUYIHBQFLXYp5Nksh8U',
),
};
return registration.pushManager.subscribe(subscribeOptions);
})
.then(function (pushSubscription) {
console.log(
'Received PushSubscription: ',
JSON.stringify(pushSubscription),
);
return pushSubscription;
});
}
Khi gọi phương thức subscribe(), bạn sẽ truyền một đối tượng options bao gồm các tham số bắt buộc và không bắt buộc.
Phần này mô tả các tuỳ chọn mà bạn có thể truyền.
tuỳ chọn userVisibleOnly
Khi tính năng thông báo đẩy lần đầu tiên được thêm vào trình duyệt, các nhà phát triển không chắc chắn về việc gửi thông báo đẩy mà không hiển thị thông báo. Điều này thường được gọi là thông báo đẩy im lặng vì người dùng không biết rằng một sự kiện đã xảy ra ở chế độ nền.
Mối lo ngại là nhà phát triển có thể liên tục theo dõi vị trí của người dùng mà người dùng không biết.
Để tránh trường hợp này và cho phép tác giả thông số kỹ thuật cân nhắc cách tốt nhất để hỗ trợ tính năng này, chúng tôi đã thêm tuỳ chọn userVisibleOnly. Việc truyền giá trị true là một thoả thuận mang tính biểu tượng với trình duyệt rằng ứng dụng web sẽ hiển thị thông báo mỗi khi nhận được thông báo đẩy (tức là không có thông báo đẩy im lặng).
Bạn phải truyền giá trị true. Nếu không thêm khoá userVisibleOnly hoặc truyền false, bạn sẽ nhận được lỗi sau:
Chrome currently only supports the Push API for subscriptions that will result
in user-visible messages. You can indicate this by calling
`pushManager.subscribe({userVisibleOnly: true})` instead. See
[https://goo.gl/yqv4Q4](https://goo.gl/yqv4Q4) for more details.
Chrome chỉ hỗ trợ Push API cho những gói thuê bao dẫn đến thông báo mà người dùng nhìn thấy. Hãy cho biết điều này bằng cách gọi pushManager.subscribe({userVisibleOnly: true}). Để biết thêm thông tin, hãy xem https://goo.gl/yqv4Q4.
Có vẻ như Chrome sẽ không triển khai tính năng thông báo đẩy im lặng toàn diện. Thay vào đó, các tác giả thông số kỹ thuật đang khám phá một API ngân sách cho phép các ứng dụng web gửi một số lượng thông báo đẩy im lặng nhất định dựa trên mức sử dụng ứng dụng web.
tuỳ chọn applicationServerKey
Tài liệu này trước đây đã đề cập đến khoá máy chủ ứng dụng. Dịch vụ đẩy sử dụng khoá máy chủ ứng dụng để xác định ứng dụng mà người dùng đăng ký và đảm bảo rằng cùng một ứng dụng sẽ gửi thông báo cho người dùng đó.
Khoá máy chủ ứng dụng là một cặp khoá công khai và riêng tư dành riêng cho ứng dụng của bạn. Giữ bí mật khoá riêng tư cho ứng dụng của bạn và chia sẻ khoá công khai một cách thoải mái.
Tuỳ chọn applicationServerKey được truyền vào lệnh gọi subscribe() là khoá công khai của ứng dụng. Trình duyệt sẽ chuyển khoá này đến một dịch vụ đẩy khi đăng ký người dùng, cho phép dịch vụ đẩy liên kết khoá công khai của ứng dụng với PushSubscription của người dùng.
Sơ đồ dưới đây minh hoạ các bước này.
- Tải ứng dụng web của bạn trong trình duyệt và gọi
subscribe(), truyền khoá máy chủ ứng dụng công khai của bạn. - Sau đó, trình duyệt sẽ gửi một yêu cầu mạng đến dịch vụ đẩy. Dịch vụ này sẽ tạo một điểm cuối, liên kết điểm cuối này với khoá công khai của ứng dụng và trả về điểm cuối cho trình duyệt.
- Trình duyệt sẽ thêm điểm cuối này vào
PushSubscriptionmà lời hứasubscribe()trả về.
Khi bạn gửi thông báo đẩy, hãy tạo một tiêu đề Authorization (Uỷ quyền) chứa thông tin được ký bằng khoá riêng tư của máy chủ ứng dụng. Khi nhận được yêu cầu gửi thông báo đẩy, dịch vụ thông báo đẩy sẽ xác thực tiêu đề Authorization (Uỷ quyền) đã ký này bằng cách tra cứu khoá công khai được liên kết với điểm cuối nhận yêu cầu. Nếu chữ ký hợp lệ, dịch vụ đẩy sẽ biết rằng yêu cầu đến từ máy chủ ứng dụng có khoá riêng tư khớp. Đây là một biện pháp bảo mật giúp ngăn người khác gửi tin nhắn cho người dùng ứng dụng của bạn.
Về mặt kỹ thuật, applicationServerKey là không bắt buộc. Tuy nhiên, cách triển khai đơn giản nhất trên Chrome yêu cầu điều này và các trình duyệt khác có thể yêu cầu điều này trong tương lai. Đây là lựa chọn không bắt buộc trên Firefox.
Thông số kỹ thuật VAPID xác định khoá máy chủ ứng dụng. Khi bạn thấy thông tin tham chiếu đến khoá máy chủ ứng dụng hoặc khoá VAPID, hãy nhớ rằng chúng giống nhau.
Tạo khoá máy chủ ứng dụng
Bạn có thể tạo một bộ khoá máy chủ ứng dụng công khai và riêng tư bằng cách sử dụng dòng lệnh web-push để tạo khoá như sau:
$ npm install -g web-push
$ web-push generate-vapid-keys
Chỉ tạo các khoá này một lần cho ứng dụng của bạn và đảm bảo bạn giữ khoá riêng tư ở chế độ riêng tư.
Quyền và subscribe()
Việc gọi subscribe() có một tác dụng phụ. Nếu ứng dụng web của bạn không có quyền hiển thị thông báo khi bạn gọi subscribe(), thì trình duyệt sẽ yêu cầu cấp quyền cho bạn. Điều này rất hữu ích nếu giao diện người dùng của bạn hoạt động với quy trình này, nhưng nếu bạn muốn có nhiều quyền kiểm soát hơn (điều mà hầu hết các nhà phát triển đều muốn), hãy sử dụng API Notification.requestPermission() mà tài liệu này đã thảo luận trước đó.
Tổng quan về PushSubscription
Bạn gọi subscribe(), truyền các tuỳ chọn và nhận một lời hứa sẽ phân giải thành PushSubscription. Ví dụ:
function subscribeUserToPush() {
return navigator.serviceWorker
.register('/service-worker.js')
.then(function (registration) {
const subscribeOptions = {
userVisibleOnly: true,
applicationServerKey: urlBase64ToUint8Array(
'BEl62iUYgUivxIkv69yViEuiBIa-Ib9-SkvMeAtA3LFgDzkrxZJjSgSnfckjBJuBkr3qBUYIHBQFLXYp5Nksh8U',
),
};
return registration.pushManager.subscribe(subscribeOptions);
})
.then(function (pushSubscription) {
console.log(
'Received PushSubscription: ',
JSON.stringify(pushSubscription),
);
return pushSubscription;
});
}
Đối tượng PushSubscription chứa tất cả thông tin cần thiết để gửi thông báo đẩy cho người dùng đó. Nếu in nội dung bằng JSON.stringify(), bạn sẽ thấy kết quả như sau:
{
"endpoint": "https://some.pushservice.com/something-unique",
"keys": {
"p256dh":
"BIPUL12DLfytvTajnryr2PRdAgXS3HGKiLqndGcJGabyhHheJYlNGCeXl1dn18gSJ1WAkAPIxr4gK0_dQds4yiI=",
"auth":"FPssNDTKnInHVndSTdbKFw=="
}
}
endpoint là URL của dịch vụ đẩy. Để kích hoạt thông báo đẩy, hãy gửi yêu cầu POST đến URL này.
Đối tượng keys chứa các giá trị dùng để mã hoá dữ liệu thông báo được gửi kèm theo thông báo đẩy. (Tài liệu này sẽ thảo luận về việc mã hoá thư ở phần sau.)
Gửi gói thuê bao đến máy chủ của bạn
Sau khi bạn có gói thuê bao thông báo đẩy, hãy gửi gói thuê bao đó đến máy chủ của bạn. Bạn quyết định cách gửi, nhưng bạn nên sử dụng JSON.stringify() để trích xuất tất cả dữ liệu cần thiết từ đối tượng gói thuê bao. Ngoài ra, bạn có thể tập hợp cùng một kết quả theo cách thủ công, ví dụ:
const subscriptionObject = {
endpoint: pushSubscription.endpoint,
keys: {
p256dh: pushSubscription.getKeys('p256dh'),
auth: pushSubscription.getKeys('auth'),
},
};
// The above is the same output as:
const subscriptionObjectToo = JSON.stringify(pushSubscription);
Để gửi gói thuê bao từ trang web, hãy sử dụng đoạn mã sau:
function sendSubscriptionToBackEnd(subscription) {
return fetch('/api/save-subscription/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(subscription),
})
.then(function (response) {
if (!response.ok) {
throw new Error('Bad status code from server.');
}
return response.json();
})
.then(function (responseData) {
if (!(responseData.data && responseData.data.success)) {
throw new Error('Bad response from server.');
}
});
}
Máy chủ Node.js nhận yêu cầu này và lưu dữ liệu vào cơ sở dữ liệu để sử dụng sau.
app.post('/api/save-subscription/', function (req, res) {
if (!isValidSaveRequest(req, res)) {
return;
}
return saveSubscriptionToDatabase(req.body)
.then(function (subscriptionId) {
res.setHeader('Content-Type', 'application/json');
res.send(JSON.stringify({data: {success: true}}));
})
.catch(function (err) {
res.status(500);
res.setHeader('Content-Type', 'application/json');
res.send(
JSON.stringify({
error: {
id: 'unable-to-save-subscription',
message:
'The subscription was received but we were unable to save it to our database.',
},
}),
);
});
});
Khi có thông tin chi tiết về PushSubscription trên máy chủ, bạn có thể gửi tin nhắn cho người dùng bất cứ lúc nào.
Đăng ký lại thường xuyên để tránh hết hạn
Khi đăng ký nhận thông báo đẩy, bạn thường nhận được PushSubscription.expirationTime của null. Về lý thuyết, điều này có nghĩa là gói thuê bao không bao giờ hết hạn. (Ngược lại, DOMHighResTimeStamp cho biết thời gian hết hạn chính xác.) Tuy nhiên, trên thực tế, các trình duyệt thường cho phép gói thuê bao hết hạn. Ví dụ: điều này có thể xảy ra nếu không nhận được thông báo đẩy trong một thời gian dài hoặc nếu trình duyệt phát hiện người dùng không sử dụng ứng dụng có quyền gửi thông báo đẩy. Một mẫu để ngăn chặn điều này là đăng ký lại cho người dùng khi nhận được mỗi thông báo, như đoạn mã sau đây cho thấy. Điều này yêu cầu bạn gửi thông báo đủ thường xuyên để ngăn trình duyệt tự động hết hạn gói thuê bao. Bạn nên cân nhắc kỹ lưỡng ưu và nhược điểm của nhu cầu thông báo chính đáng so với việc vô tình gửi thông báo rác cho người dùng chỉ để ngăn gói thuê bao hết hạn. Cuối cùng, bạn không nên tìm cách lách các biện pháp của trình duyệt nhằm bảo vệ người dùng khỏi các gói thuê bao thông báo đã bị lãng quên từ lâu.
/* In the Service Worker. */
self.addEventListener('push', function(event) {
console.log('Received a push message', event);
// Display notification or handle data
// Example: show a notification
const title = 'New Notification';
const body = 'You have new updates!';
const icon = '/images/icon.png';
const tag = 'simple-push-demo-notification-tag';
event.waitUntil(
self.registration.showNotification(title, {
body: body,
icon: icon,
tag: tag
})
);
// Attempt to resubscribe after receiving a notification
event.waitUntil(resubscribeToPush());
});
function resubscribeToPush() {
return self.registration.pushManager.getSubscription()
.then(function(subscription) {
if (subscription) {
return subscription.unsubscribe();
}
})
.then(function() {
return self.registration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: urlBase64ToUint8Array('YOUR_PUBLIC_VAPID_KEY_HERE')
});
})
.then(function(subscription) {
console.log('Resubscribed to push notifications:', subscription);
// Optionally, send new subscription details to your server
})
.catch(function(error) {
console.error('Failed to resubscribe:', error);
});
}
Câu hỏi thường gặp
Sau đây là một số câu hỏi thường gặp:
Bạn có thể thay đổi dịch vụ thông báo đẩy mà trình duyệt sử dụng không?
Không, trình duyệt sẽ chọn dịch vụ đẩy. Như tài liệu này đã thảo luận về lệnh gọi subscribe(), trình duyệt sẽ gửi yêu cầu mạng đến dịch vụ đẩy để truy xuất thông tin chi tiết tạo nên PushSubscription.
Các dịch vụ thông báo đẩy khác nhau có sử dụng các API khác nhau không?
Tất cả dịch vụ đẩy đều yêu cầu cùng một API.
API phổ biến này (gọi là Giao thức thông báo đẩy trên web) mô tả yêu cầu mạng mà ứng dụng của bạn đưa ra để kích hoạt thông báo đẩy.
Nếu bạn đăng ký cho người dùng trên máy tính, thì họ có được đăng ký trên điện thoại không?
Không. Người dùng phải đăng ký nhận thông báo đẩy trên mỗi trình duyệt mà họ muốn nhận thông báo. Điều này cũng yêu cầu người dùng cấp quyền trên từng thiết bị.
Các bước tiếp theo
- Tổng quan về thông báo đẩy trên web
- Cách hoạt động của thông báo đẩy
- Đăng ký cho người dùng
- Trải nghiệm người dùng về quyền
- Gửi thông báo bằng thư viện thông báo đẩy trên web
- Giao thức thông báo đẩy trên web
- Xử lý sự kiện đẩy
- Hiện thông báo
- Cách hoạt động của thông báo
- Các mẫu thông báo phổ biến
- Câu hỏi thường gặp về thông báo đẩy
- Các vấn đề thường gặp và cách báo cáo lỗi