Lớp học lập trình này hướng dẫn bạn từng bước cách tạo một máy chủ thông báo đẩy. Khi kết thúc lớp học lập trình này, bạn sẽ có một máy chủ:
- Theo dõi các lượt đăng ký nhận thông báo đẩy (tức là máy chủ sẽ tạo một bản ghi cơ sở dữ liệu mới khi một ứng dụng chọn nhận thông báo đẩy và xoá một bản ghi cơ sở dữ liệu hiện có khi một ứng dụng chọn không nhận)
- Gửi thông báo đẩy đến một ứng dụng duy nhất
- Gửi thông báo đẩy đến tất cả các ứng dụng khách đã đăng ký
Lớp học lập trình này tập trung vào việc giúp bạn học bằng cách thực hành và không đề cập nhiều đến các khái niệm. Hãy xem bài viết Thông báo đẩy hoạt động như thế nào? để tìm hiểu về các khái niệm liên quan đến thông báo đẩy.
Mã ứng dụng của lớp học lập trình này đã hoàn tất. Bạn sẽ chỉ triển khai máy chủ trong lớp học lập trình này. Để tìm hiểu cách triển khai một ứng dụng thông báo đẩy, hãy xem Lớp học mã: Xây dựng một ứng dụng thông báo đẩy.
Khả năng tương thích với trình duyệt
Lớp học lập trình này hoạt động với các tổ hợp hệ điều hành và trình duyệt sau:
- Windows: Chrome, Edge
- macOS: Chrome, Firefox
- Android: Chrome, Firefox
Lớp học lập trình này không hoạt động với các hệ điều hành sau (hoặc tổ hợp hệ điều hành và trình duyệt):
- macOS: Brave, Edge, Safari
- iOS
Ngăn xếp ứng dụng
- Máy chủ này được xây dựng dựa trên Express.js.
- Thư viện web-push Node.js xử lý tất cả logic thông báo đẩy.
- Dữ liệu về lượt đăng ký được ghi vào một tệp JSON bằng lowdb.
Bạn không cần sử dụng bất kỳ công nghệ nào trong số này để triển khai thông báo đẩy. Chúng tôi chọn những công nghệ này vì chúng mang lại trải nghiệm đáng tin cậy trong lớp học lập trình.
Thiết lập
Thiết lập chế độ xác thực
Để có thể nhận thông báo đẩy, bạn cần thiết lập máy chủ và ứng dụng bằng các khoá xác thực. Hãy xem phần Ký các yêu cầu giao thức thông báo đẩy trên web để tìm hiểu lý do.
- Mở cửa sổ dòng lệnh.
- Trong thiết bị đầu cuối, hãy chạy
npx web-push generate-vapid-keys
. Sao chép các giá trị khoá riêng tư và khoá công khai. - Mở
.env
rồi cập nhậtVAPID_PUBLIC_KEY
vàVAPID_PRIVATE_KEY
. ĐặtVAPID_SUBJECT
thànhmailto:test@test.test
. Bạn nên đặt tất cả các giá trị này trong dấu ngoặc kép. Sau khi bạn cập nhật, tệp.env
của bạn sẽ có dạng như sau:
VAPID_PUBLIC_KEY="BKiwTvD9HA…"
VAPID_PRIVATE_KEY="4mXG9jBUaU…"
VAPID_SUBJECT="mailto:test@test.test"
- Mở
public/index.js
. - Thay thế
VAPID_PUBLIC_KEY_VALUE_HERE
bằng giá trị khoá công khai của bạn.
Quản lý gói thuê bao
Ứng dụng của bạn xử lý hầu hết quy trình đăng ký. Những việc chính mà máy chủ của bạn cần làm là lưu các gói thuê bao thông báo đẩy mới và xoá các gói thuê bao cũ. Những lượt đăng ký này cho phép bạn gửi thông báo đến các ứng dụng sau này. Hãy xem phần Đăng ký cho ứng dụng nhận thông báo đẩy để biết thêm thông tin về quy trình đăng ký.
Lưu thông tin mới về gói thuê bao
- Nhấp vào Register service worker (Đăng ký worker dịch vụ) trong thẻ ứng dụng. Trong hộp trạng thái, bạn sẽ thấy một thông báo tương tự như sau:
Service worker registered. Scope: https://example.com
- Trong thẻ ứng dụng, hãy nhấp vào Đăng ký nhận thông báo đẩy. Trình duyệt hoặc hệ điều hành của bạn có thể sẽ hỏi bạn có muốn cho phép trang web gửi thông báo đẩy cho bạn hay không. Nhấp vào Cho phép (hoặc cụm từ tương đương mà trình duyệt/hệ điều hành của bạn sử dụng). Trong hộp trạng thái, bạn sẽ thấy một thông báo tương tự như sau:
Service worker subscribed to push. Endpoint: https://fcm.googleapis.com/fcm/send/…
- Mở cửa sổ dòng lệnh để xem nhật ký. Bạn sẽ thấy
/add-subscription
, theo sau là một số dữ liệu./add-subscription
là URL mà ứng dụng gửi yêu cầu POST đến khi muốn đăng ký nhận thông báo đẩy. Dữ liệu sau đây là thông tin đăng ký của ứng dụng mà bạn cần lưu. - Mở
server.js
. - Cập nhật logic trình xử lý tuyến
/add-subscription
bằng mã sau:
app.post('/add-subscription', (request, response) => {
console.log('/add-subscription');
console.log(request.body);
console.log(`Subscribing ${request.body.endpoint}`);
db.get('subscriptions')
.push(request.body)
.write();
response.sendStatus(200);
});
Xoá thông tin cũ về gói thuê bao
- Quay lại thẻ ứng dụng.
- Nhấp vào Huỷ nhận thông báo đẩy.
- Xem lại nhật ký. Bạn sẽ thấy
/remove-subscription
, theo sau là thông tin về gói thuê bao của khách hàng. - Cập nhật logic trình xử lý tuyến
/remove-subscription
bằng mã sau:
app.post('/remove-subscription', (request, response) => {
console.log('/remove-subscription');
console.log(request.body);
console.log(`Unsubscribing ${request.body.endpoint}`);
db.get('subscriptions')
.remove({endpoint: request.body.endpoint})
.write();
response.sendStatus(200);
});
Gửi thông báo
Như đã giải thích trong phần Gửi thông báo đẩy, máy chủ của bạn không thực sự gửi trực tiếp thông báo đẩy đến các ứng dụng. Thay vào đó, ứng dụng này dựa vào một dịch vụ truyền tin để thực hiện việc đó. Về cơ bản, máy chủ của bạn chỉ cần bắt đầu quy trình đẩy thông báo đến các ứng dụng bằng cách đưa ra yêu cầu dịch vụ web (yêu cầu giao thức thông báo đẩy trên web) đến một dịch vụ web (dịch vụ đẩy) thuộc quyền sở hữu của nhà cung cấp trình duyệt mà người dùng của bạn sử dụng.
- Cập nhật logic trình xử lý tuyến
/notify-me
bằng mã sau:
app.post('/notify-me', (request, response) => {
console.log('/notify-me');
console.log(request.body);
console.log(`Notifying ${request.body.endpoint}`);
const subscription =
db.get('subscriptions').find({endpoint: request.body.endpoint}).value();
sendNotifications([subscription]);
response.sendStatus(200);
});
- Cập nhật hàm
sendNotifications()
bằng mã sau:
function sendNotifications(subscriptions) {
// TODO
// Create the notification content.
const notification = JSON.stringify({
title: "Hello, Notifications!",
options: {
body: `ID: ${Math.floor(Math.random() * 100)}`
}
});
// Customize how the push service should attempt to deliver the push message.
// And provide authentication information.
const options = {
TTL: 10000,
vapidDetails: vapidDetails
};
// Send a push message to each client specified in the subscriptions array.
subscriptions.forEach(subscription => {
const endpoint = subscription.endpoint;
const id = endpoint.substr((endpoint.length - 8), endpoint.length);
webpush.sendNotification(subscription, notification, options)
.then(result => {
console.log(`Endpoint ID: ${id}`);
console.log(`Result: ${result.statusCode}`);
})
.catch(error => {
console.log(`Endpoint ID: ${id}`);
console.log(`Error: ${error} `);
});
});
}
- Cập nhật logic trình xử lý tuyến
/notify-all
bằng mã sau:
app.post('/notify-all', (request, response) => {
console.log('/notify-all');
response.sendStatus(200);
console.log('Notifying all subscribers');
const subscriptions =
db.get('subscriptions').cloneDeep().value();
if (subscriptions.length > 0) {
sendNotifications(subscriptions);
response.sendStatus(200);
} else {
response.sendStatus(409);
}
});
- Quay lại thẻ ứng dụng.
- Nhấp vào Thông báo cho tôi. Bạn sẽ nhận được một thông báo đẩy. Tiêu đề phải là
Hello, Notifications!
và nội dung phải làID: <ID>
, trong đó<ID>
là một số ngẫu nhiên. - Mở ứng dụng của bạn trên các trình duyệt hoặc thiết bị khác và thử đăng ký nhận thông báo đẩy, sau đó nhấp vào nút Thông báo cho tất cả. Bạn sẽ nhận được cùng một thông báo trên tất cả các thiết bị mà bạn đã đăng ký (tức là mã nhận dạng trong nội dung thông báo đẩy phải giống nhau).
Các bước tiếp theo
- Đọc bài viết Tổng quan về thông báo đẩy để hiểu rõ hơn về khái niệm cách hoạt động của thông báo đẩy.
- Hãy xem Lớp học lập trình: Xây dựng một ứng dụng thông báo đẩy để tìm hiểu cách xây dựng một ứng dụng yêu cầu quyền thông báo, đăng ký thiết bị để nhận thông báo đẩy và sử dụng một trình thực thi dịch vụ để nhận thông báo đẩy và hiển thị thông báo.