Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Замечание
Это не последняя версия этой статьи. В текущей версии см. версию .NET 10 этой статьи.
Предупреждение
Эта версия ASP.NET Core больше не поддерживается. Для получения дополнительной информации см. Политику поддержки .NET и .NET Core. В текущей версии см. версию .NET 10 этой статьи.
Blazor PWA могут получать и отображать push-уведомления (сообщения данных) с серверной части, даже если пользователь не использует активно приложение. Например, push-уведомления можно отправлять, когда другой пользователь выполняет действие в установленной PWA или когда приложение или пользователи, взаимодействующие непосредственно с серверным серверным приложением, выполняют действие.
Используйте push-уведомления для:
- Уведомите пользователей о том, что произошло что-то важное, запрашивая их вернуться в приложение.
- Обновите данные, хранящиеся в приложении, например канал новостей, чтобы у пользователя были свежие данные при следующем возвращении в приложение, даже если он будет в автономном режиме в момент отправки push-уведомления.
Механизмы отправки, получения и отображения push-уведомлений не зависят от Blazor WebAssembly. Отправка push-уведомления реализуется сервером серверной части, который может использовать любую технологию. Получение и отображение push-уведомления на клиенте реализуется в JavaScript-файле службы .JS
В примере в этой статье используются push-уведомления для предоставления обновлений состояния заказа клиентам ресторана пиццы на основе демонстрационного приложения Blazing Pizza Workshop PWA. Вам не нужно участвовать в онлайн-семинаре, чтобы использовать эту статью, но семинар является полезным введением в Blazor разработку PWA.
Замечание
Приложение Blazing Pizza принимает шаблон репозитория для создания слоя абстракции между уровнем пользовательского интерфейса и уровнем доступа к данным. Дополнительные сведения см. в шаблонах единиц работы (UoW) и проектировании уровня сохраняемости инфраструктуры.
Установление открытых и закрытых ключей
Создайте криптографические открытые и закрытые ключи для защиты push-уведомлений локально, например с помощью PowerShell или IIS или с помощью интерактивного средства.
Заполнители, используемые в примере кода этой статьи:
-
{PUBLIC KEY}: открытый ключ. -
{PRIVATE KEY}: закрытый ключ.
В примерах C# этой статьи обновите someone@example.com адрес электронной почты, соответствующий адресу, используемому при создании пользовательской пары ключей.
При реализации push-уведомлений убедитесь, что криптографические ключи управляются безопасно:
- Создание ключей: используйте надежную библиотеку или средство для создания открытых и закрытых ключей. Избегайте использования слабых или устаревших алгоритмов.
- Хранилище ключей: безопасно храните закрытые ключи на сервере, используя безопасный механизм хранения, например аппаратный модуль безопасности (HSM) или зашифрованное хранилище. Никогда не предоставлять закрытые ключи клиенту.
- Использование ключа: используйте закрытый ключ только для подписывания полезных данных push-уведомлений. Убедитесь, что открытый ключ безопасно распределяется клиентам.
Дополнительные сведения о рекомендациях по шифрованию см. в разделе "Службы шифрования".
Создание подписки
Перед отправкой push-уведомлений пользователю приложение должно запросить разрешение. Если они предоставляют разрешение на получение уведомлений, браузер создает подписку, которая включает набор маркеров, которые приложение может использовать для маршрутизации уведомлений пользователю.
Разрешение может быть получено в любое время приложением, но мы рекомендуем запрашивать у пользователей разрешение, когда понятно, почему они хотят подписаться на уведомления из приложения. В следующем примере пользователям задается вопрос, когда они переходят на страницу оформления заказа (Checkout компонент), так как на этом этапе ясно, что покупатель серьезно намерен сделать заказ.
Если пользователь соглашается получать уведомления, следующий пример отправляет данные подписки push-уведомлений на сервер, где маркеры push-уведомлений хранятся в базе данных для последующего использования.
Добавьте файл push-уведомлений JS для запроса подписки:
- Вызовите
navigator.serviceWorker.getRegistration, чтобы получить регистрацию сервисного работника. - Вызов
worker.pushManager.getSubscription, чтобы определить, существует ли подписка. - Если подписка не существует, создайте новую подписку с помощью
PushManager.subscribeфункции и верните URL-адрес и маркеры новой подписки.
В приложении Blazing Pizza файл называется JS и находится в папке общедоступных статических ресурсов (pushNotifications.js) классовой библиотеки проекта решения wwwroot (Razor). Функция blazorPushNotifications.requestSubscription запрашивает подписку.
BlazingPizza.ComponentsLibrary/wwwroot/pushNotifications.js:
(function () {
const applicationServerPublicKey = '{PUBLIC KEY}';
window.blazorPushNotifications = {
requestSubscription: async () => {
const worker = await navigator.serviceWorker.getRegistration();
const existingSubscription = await worker.pushManager.getSubscription();
if (!existingSubscription) {
const newSubscription = await subscribe(worker);
if (newSubscription) {
return {
url: newSubscription.endpoint,
p256dh: arrayBufferToBase64(newSubscription.getKey('p256dh')),
auth: arrayBufferToBase64(newSubscription.getKey('auth'))
};
}
}
}
};
async function subscribe(worker) {
try {
return await worker.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: applicationServerPublicKey
});
} catch (error) {
if (error.name === 'NotAllowedError') {
return null;
}
throw error;
}
}
function arrayBufferToBase64(buffer) {
var binary = '';
var bytes = new Uint8Array(buffer);
var len = bytes.byteLength;
for (var i = 0; i < len; i++) {
binary += String.fromCharCode(bytes[i]);
}
return window.btoa(binary);
}
})();
Замечание
Дополнительные сведения о предыдущей arrayBufferToBase64 функции см. в статье "Как преобразовать ArrayBuffer в строку в кодировке Base64? (Stack Overflow).
На сервере создается объект подписки и конечная точка подписки уведомлений. Конечная точка получает вызовы клиентского веб-API с данными подписки push-уведомлений, включая криптографические маркеры. Данные хранятся в базе данных для каждого пользователя приложения.
В приложении Blazing Pizza объект подписки является классом NotificationSubscription . Свойства P256dh и Auth представляют собой криптографические токены пользователя.
BlazingPizza.Shared/NotificationSubscription.cs:
public class NotificationSubscription
{
public int? NotificationSubscriptionId { get; set; }
public string? UserId { get; set; }
public string? Url { get; set; }
public string? P256dh { get; set; }
public string? Auth { get; set; }
}
notifications/subscribe Конечная точка определена в методе расширения приложения, который вызывается в файле приложения MapPizzaApiProgram для настройки конечных точек веб-API для приложения. Подписка на уведомления пользователя (NotificationSubscription), которая включает маркеры push-уведомлений, хранится в базе данных. Хранится только одна подписка на пользователя. Кроме того, можно разрешить пользователю зарегистрировать несколько подписок из разных браузеров или устройств.
app.MapPut("/notifications/subscribe",
[Authorize] async (
HttpContext context,
PizzaStoreContext db,
NotificationSubscription subscription) =>
{
var userId = GetUserId(context);
if (userId is null)
{
return Results.Unauthorized();
}
// Remove old subscriptions for this user
var oldSubscriptions = db.NotificationSubscriptions.Where(
e => e.UserId == userId);
db.NotificationSubscriptions.RemoveRange(oldSubscriptions);
// Store the new subscription
subscription.UserId = userId;
db.NotificationSubscriptions.Add(subscription);
await db.SaveChangesAsync();
return Results.Ok(subscription);
});
В BlazingPizza.Client/HttpRepository.cs методе SubscribeToNotifications отправляется запрос HTTP PUT к конечной точке подписок на сервере.
public class HttpRepository : IRepository
{
private readonly HttpClient _httpClient;
public HttpRepository(HttpClient httpClient)
{
_httpClient = httpClient;
}
...
public async Task SubscribeToNotifications(NotificationSubscription subscription)
{
var response = await _httpClient.PutAsJsonAsync("notifications/subscribe",
subscription);
response.EnsureSuccessStatusCode();
}
}
Интерфейс репозитория (BlazingPizza.Shared/IRepository.cs) включает сигнатуру метода SubscribeToNotifications:
public interface IRepository
{
...
Task SubscribeToNotifications(NotificationSubscription subscription);
}
Определите метод для запроса подписки и подписки на уведомления, если подписка установлена. Сохраните подписку в базе данных для последующего использования.
В компоненте Checkout приложения Blazing Pizza метод RequestNotificationSubscriptionAsync выполняет следующие функции:
- Подписка создается через JS интероп, вызывая
blazorPushNotifications.requestSubscription. Компонент внедряет IJSRuntime службу для вызова JS функции. - Метод
SubscribeToNotificationsвызывается для сохранения подписки.
В BlazingPizza.Client/Components/Pages/Checkout.razor:
async Task RequestNotificationSubscriptionAsync()
{
var subscription = await JSRuntime.InvokeAsync<NotificationSubscription>(
"blazorPushNotifications.requestSubscription");
if (subscription is not null)
{
try
{
await Repository.SubscribeToNotifications(subscription);
}
catch (AccessTokenNotAvailableException ex)
{
ex.Redirect();
}
}
}
В компоненте CheckoutRequestNotificationSubscriptionAsync вызывается в OnInitialized методе жизненного цикла и выполняется при инициализации компонента. Метод является асинхронным, но может выполняться в фоновом режиме, и возвращаемое Task можно не обрабатывать. Поэтому метод не вызывается в асинхронном методе жизненного цикла для инициализации компонентов (OnInitializedAsync). Этот подход ускоряет работу компонента.
protected override void OnInitialized()
{
_ = RequestNotificationSubscriptionAsync();
}
Чтобы продемонстрировать, как работает код, запустите приложение Blazing Pizza и начните размещать заказ. Перейдите на экран проверки, чтобы просмотреть запрос на подписку:
Выберите Разрешить и проверьте наличие ошибок в консоли средств разработчика браузера. Можно задать точку останова в PizzaApiExtensionsMapPut("/notifications/subscribe"...) коде и запустить приложение с отладкой, чтобы проверить входящие данные из браузера. Данные включают URL-адрес конечной точки и криптографические маркеры.
После того как пользователь разрешил или заблокировал уведомления для данного сайта, браузер снова не будет спрашивать. Чтобы сбросить разрешение для дальнейшего тестирования для Google Chrome или Microsoft Edge, щелкните значок "сведения" (🛈) слева от адресной строки браузера и измените уведомления обратно на Ask (по умолчанию), как показано на следующем рисунке:
Отправка уведомления
Отправка уведомления включает выполнение некоторых сложных криптографических операций на сервере для защиты передаваемых данных. Основная часть сложности обрабатывается для приложения сторонним пакетом NuGet, WebPushкоторый используется серверным проектом (BlazingPizza.Server) в приложении Blazing Pizza.
Метод SendNotificationAsync отправляет уведомления о заказе с помощью записанной подписки. Следующий код использует API для отправки WebPush уведомления. Полезные данные уведомления сериализуются в формате JSON и включают сообщение и URL-адрес. Сообщение отображается пользователю, и URL-адрес позволяет пользователю получить доступ к заказу пиццы, связанному с уведомлением. Дополнительные параметры можно сериализовать по мере необходимости для других сценариев уведомлений.
Осторожность
В следующем примере рекомендуется использовать безопасный подход для предоставления закрытого ключа. При работе локально в среде Development, закрытый ключ можно предоставить приложению с помощью средства Secret Manager. В средах Development, Staging и Production можно использовать Azure Key Vault с управляемыми удостоверениями Azure, отметив при этом, что для получения закрытого ключа сертификата из хранилища ключей, сертификат должен иметь экспортируемый закрытый ключ.
private static async Task SendNotificationAsync(Order order,
NotificationSubscription subscription, string message)
{
var publicKey = "{PUBLIC KEY}";
var privateKey = "{PRIVATE KEY}";
var pushSubscription = new PushSubscription(subscription.Url,
subscription.P256dh, subscription.Auth);
var vapidDetails = new VapidDetails("mailto:<someone@example.com>", publicKey,
privateKey);
var webPushClient = new WebPushClient();
try
{
var payload = JsonSerializer.Serialize(new
{
message,
url = $"myorders/{order.OrderId}",
});
await webPushClient.SendNotificationAsync(pushSubscription, payload,
vapidDetails);
}
catch (Exception ex)
{
Console.Error.WriteLine($"Error sending push notification: {ex.Message}");
}
}
В предыдущем примере сервер может отправлять уведомления, но браузер не реагирует на уведомления без дополнительной логики. Отображение уведомлений рассматривается в разделе "Отображаемые уведомления".
Консоль средств разработчика браузера указывает на поступление уведомлений через десять секунд после размещения заказов в приложении Blazing Pizza. На вкладке "Приложение" откройте раздел Push Messaging . Выберите круг для начала записи:
Отображение уведомлений
Рабочая роль службы PWA (service-worker.js) должна обрабатывать push-уведомления, чтобы приложение отображалось.
push Следующий обработчик событий в приложении Blazing Pizza вызывает showNotification для создания уведомления для активного сервисного работника.
В BlazingPizza/wwwroot/service-worker.js:
self.addEventListener('push', event => {
const payload = event.data.json();
event.waitUntil(
self.registration.showNotification('Blazing Pizza', {
body: payload.message,
icon: 'img/icon-512.png',
vibrate: [100, 50, 100],
data: { url: payload.url }
})
);
});
Предыдущий код не вступает в силу до следующей загрузки страницы, когда браузер регистрирует Installing service worker.... При попытке обновить рабочую роль службы используйте вкладку "Приложение " в консоли средств разработчика браузера. В разделе "Рабочие службы" выберите "Обновить " или " Отменить регистрацию ", чтобы принудительно выполнить новую регистрацию при следующей загрузке.
При использовании предыдущего кода и размещении новым пользователем заказа, заказ через 10 секунд переходит в статус Передан на доставку в соответствии с встроенной демонстрационной логикой приложения. Браузер получает push-уведомление:
При использовании приложения в Google Chrome или Microsoft Edge уведомление отображается даже в том случае, если пользователь не использует приложение Blazing Pizza. Однако браузер должен быть запущен или появится уведомление при следующем открытии браузера.
При использовании установленной PWA уведомление должно быть доставлено, даже если пользователь не запускает приложение.
Обработка нажатий на уведомления
notificationclick Зарегистрируйте обработчик событий, чтобы обработать выбор пользователя при нажатии на push-уведомление на своем устройстве:
- Закройте уведомление путем вызова
event.notification.close. - Вызовите
clients.openWindow, чтобы создать новый контекст просмотра верхнего уровня и загрузить URL-адрес, переданный методу.
В следующем примере в приложении Blazing Pizza пользователь перейдет на страницу состояния заказа для заказа, относящегося к уведомлению. URL-адрес предоставляется параметром event.notification.data.url, который отправляется сервером в нагрузке уведомления.
В файле service worker (service-worker.js):
self.addEventListener('notificationclick', event => {
event.notification.close();
event.waitUntil(clients.openWindow(event.notification.data.url));
});
Если PWA установлен на устройстве, он отображается на этом устройстве. Если PWA не установлен, пользователь перейдет на страницу приложения в браузере.
Дополнительные ресурсы
ASP.NET Core