Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Observação
Esta não é a versão mais recente deste artigo. Para a versão atual, consulte a versão do .NET 10 deste artigo.
Aviso
Esta versão do ASP.NET Core não tem mais suporte. Para obter mais informações, consulte a Política de Suporte do .NET e do .NET Core. Para a versão atual, consulte a versão do .NET 10 deste artigo.
Blazor As PWAs podem receber e exibir notificações por push (mensagens de dados) de um servidor de back-end, mesmo quando o usuário não estiver usando o aplicativo ativamente. Por exemplo, as notificações por push podem ser enviadas quando um usuário diferente executa uma ação em seu PWA instalado, ou quando o aplicativo ou os usuários interagem diretamente com o aplicativo de servidor de back-end executam uma ação.
Use notificações por push para:
- Notifique os usuários de que algo importante aconteceu, solicitando que eles retornem ao aplicativo.
- Atualize os dados armazenados no aplicativo, como um feed de notícias, para que o usuário tenha novos dados em seu próximo retorno ao aplicativo, mesmo que estejam offline quando a notificação por push for emitida.
Os mecanismos para enviar, receber e exibir uma notificação por push são independentes de Blazor WebAssembly. O envio de uma notificação por push é implementado pelo servidor de back-end, que pode usar qualquer tecnologia. O recebimento e a exibição de uma notificação por push no cliente são implementados no arquivo JavaScript (JS) do service worker.
O exemplo neste artigo usa notificações por push para fornecer atualizações de status do pedido aos clientes de uma pizzaria com base no aplicativo de demonstração PWA do Blazing Pizza Workshop. Você não precisa participar do workshop online para usar este artigo, mas o workshop é uma introdução útil ao Blazor desenvolvimento do PWA.
Observação
O aplicativo Blazing Pizza adota o padrão de repositório para criar uma camada de abstração entre a camada de interface do usuário e a camada de acesso a dados. Para obter mais informações, consulte o padrão UoW (Unidade de Trabalho) e Projetando a camada de persistência de infraestrutura.
Estabelecer chaves públicas e privadas
Gere as chaves públicas e privadas criptográficas para proteger as notificações por push localmente, por exemplo, com o PowerShell ou o IIS ou usando uma ferramenta online.
Marcadores de posição usados no código de exemplo deste artigo:
-
{PUBLIC KEY}: a chave pública. -
{PRIVATE KEY}: a chave privada.
Para os exemplos de C# deste artigo, atualize o someone@example.com endereço de email para corresponder ao endereço usado ao criar o par de chaves personalizado.
Ao implementar notificações por push, verifique se as chaves criptográficas são gerenciadas com segurança:
- Geração de chave: use uma biblioteca ou ferramenta confiável para gerar as chaves públicas e privadas. Evite usar algoritmos fracos ou desatualizados.
- Armazenamento de chaves: armazene chaves privadas com segurança no servidor, usando um mecanismo de armazenamento seguro, como um HSM (módulo de segurança de hardware) ou armazenamento criptografado. Nunca exponha chaves privadas ao cliente.
- Uso de chave: use a chave privada apenas para assinar cargas de notificação por push. Verifique se a chave pública é distribuída com segurança aos clientes.
Para obter mais informações sobre as práticas recomendadas de criptografia, consulte Serviços Criptográficos.
Criar uma assinatura
Antes de enviar notificações por push para um usuário, o aplicativo deve solicitar permissão ao usuário. Se eles concederem permissão para receber notificações, seu navegador gerará uma assinatura, que inclui um conjunto de tokens que o aplicativo pode usar para rotear notificações para o usuário.
A permissão pode ser obtida a qualquer momento pelo aplicativo, mas só recomendamos pedir permissão aos usuários quando estiver claro por que eles gostariam de assinar notificações do aplicativo. O exemplo a seguir pergunta aos usuários quando eles chegam na página de checkout (Checkout, componente), pois nesse ponto está claro que o usuário está realmente interessado em fazer um pedido.
Se o usuário concordar em receber notificações, o exemplo a seguir enviará os dados da assinatura de notificação por push para o servidor, em que os tokens de notificação por push são armazenados no banco de dados para uso posterior.
Adicione um arquivo de notificações JS por push para solicitar uma assinatura:
- Ligue
navigator.serviceWorker.getRegistrationpara obter o registro do trabalhador do serviço. - Ligue
worker.pushManager.getSubscriptionpara determinar se existe uma assinatura. - Se uma assinatura não existir, crie uma nova assinatura usando a
PushManager.subscribefunção e retorne a URL e os tokens da nova assinatura.
No aplicativo Blazing Pizza, o arquivo JS é nomeado pushNotifications.js e localizado na pasta de ativos estáticos públicos (wwwroot) do projeto de biblioteca de classes da solução (Razor) (BlazingPizza.ComponentsLibrary). A blazorPushNotifications.requestSubscription função solicita uma assinatura.
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);
}
})();
Observação
Para obter mais informações sobre a função anterior arrayBufferToBase64 , consulte Como posso converter um ArrayBuffer em uma cadeia de caracteres codificada em base64? (Stack Overflow).
Um objeto de assinatura e um ponto de extremidade de assinatura de notificação são criados no servidor. O ponto de extremidade recebe chamadas de API Web do cliente com dados de inscrição em notificações por push, incluindo tokens criptográficos. Os dados são armazenados no banco de dados para cada usuário do aplicativo.
No aplicativo Blazing Pizza, o objeto de assinatura é a NotificationSubscription classe. As propriedades P256dh e Auth são os tokens criptográficos do usuário.
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; }
}
O notifications/subscribe ponto de extremidade é definido no método de extensão do aplicativo MapPizzaApi, que é chamado no arquivo Program do aplicativo para configurar os pontos de extremidade da API web para o aplicativo. A assinatura de notificação do usuário (NotificationSubscriptionque inclui seus tokens de notificação por push) é armazenada no banco de dados. Apenas uma assinatura por usuário é armazenada. Como alternativa, você pode permitir que o usuário registre várias assinaturas de diferentes navegadores ou dispositivos.
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);
});
Em BlazingPizza.Client/HttpRepository.cs, o método SubscribeToNotifications emite um HTTP PUT para o endpoint de assinaturas no servidor.
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();
}
}
A interface do repositório (BlazingPizza.Shared/IRepository.cs) inclui a assinatura do método de SubscribeToNotifications:
public interface IRepository
{
...
Task SubscribeToNotifications(NotificationSubscription subscription);
}
Defina um método para solicitar uma assinatura e assinar notificações se a assinatura for estabelecida. Salve a assinatura no banco de dados para uso posterior.
Checkout No componente do aplicativo Blazing Pizza, o RequestNotificationSubscriptionAsync método executa as seguintes tarefas:
- A assinatura é criada via JS interop ao chamar
blazorPushNotifications.requestSubscription. O componente injeta o IJSRuntime serviço para invocar a JS função. - O
SubscribeToNotificationsmétodo é chamado para salvar a assinatura.
Em 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();
}
}
}
No componente Checkout, RequestNotificationSubscriptionAsync é chamado no método de ciclo de vida OnInitialized e executa-se na inicialização do componente. O método é assíncrono, mas pode ser executado em segundo plano e o Task que ele retorna pode ser descartado. Portanto, o método não é chamado no método de ciclo de vida assíncrono para inicialização de componente (OnInitializedAsync). Essa abordagem renderiza o componente mais rapidamente.
protected override void OnInitialized()
{
_ = RequestNotificationSubscriptionAsync();
}
Para demonstrar como o código funciona, execute o aplicativo Blazing Pizza e comece a fazer um pedido. Vá para a tela de check-out para ver a solicitação de assinatura:
Escolha Permitir e verifique no console das ferramentas do desenvolvedor do navegador para verificar erros. Você pode definir um ponto de interrupção no código PizzaApiExtensions do MapPut("/notifications/subscribe"...) e executar o aplicativo com depuração para inspecionar os dados de entrada do navegador. Os dados incluem uma URL de endpoint e tokens criptográficos.
Depois que o usuário tiver permitido ou bloqueado notificações para um determinado site, o navegador não perguntará novamente. Para redefinir a permissão para testes adicionais do Google Chrome ou do Microsoft Edge, selecione o ícone "informações" (🛈) à esquerda da barra de endereços do navegador e altere as Notificações de volta para Perguntar (padrão), conforme visto na imagem a seguir:
Selecionar "Perguntar (padrão)" em "Notificações" deste aplicativo para redefinir as notificações para um estado desativado.
Enviar uma notificação
O envio de uma notificação envolve a execução de algumas operações criptográficas complexas no servidor para proteger os dados em trânsito. A maior parte da complexidade é tratada para o aplicativo por um pacote NuGet de terceiros, WebPush que é usado pelo projeto de servidor (BlazingPizza.Server) no aplicativo Blazing Pizza.
O SendNotificationAsync método envia notificações de pedido usando a assinatura capturada. O código a seguir usa WebPush APIs para expedir a notificação. O conteúdo da notificação é serializado em JSON e inclui uma mensagem e uma URL. A mensagem é exibida para o usuário e a URL permite que o usuário alcance o pedido de pizza associado à notificação. Parâmetros adicionais podem ser serializados conforme necessário para outros cenários de notificação.
Cuidado
No exemplo a seguir, recomendamos usar uma abordagem segura para fornecer a chave privada. Ao trabalhar localmente no Development ambiente, uma chave privada pode ser fornecida ao aplicativo usando a ferramenta Gerenciador de Segredos . Em Development, Staging e Production ambientes, o Azure Key Vault com Identidades Gerenciadas do Azure pode ser usado, observando de passagem que, para obter a chave privada de um certificado de um cofre de chaves, o certificado deve possuir uma chave privada exportável.
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}");
}
}
O exemplo anterior permite que o servidor envie notificações, mas o navegador não reage às notificações sem lógica adicional. A exibição de notificações é abordada na seção Exibir notificações .
O console de ferramentas para desenvolvedores do navegador indica a chegada de notificações dez segundos após os pedidos serem feitos no aplicativo Blazing Pizza. Na guia Aplicativo , abra a seção Push Messaging . Selecione o círculo para iniciar a gravação:
Exibir notificações
O trabalhador de serviço do PWA (service-worker.js) deve lidar com notificações por push para que o aplicativo as exiba.
O manipulador de eventos a seguir push no aplicativo Blazing Pizza chama showNotification para criar uma notificação para o trabalho de serviço ativo.
Em 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 }
})
);
});
O código anterior só entra em vigor após o carregamento da próxima página quando o navegador registrar Installing service worker.... Ao tiver dificuldade para atualizar o trabalho de serviço, use a guia Aplicativo no console de ferramentas para desenvolvedores do navegador. Em Trabalhos de Serviço, escolha Atualizar ou usar Cancelar registro para forçar um novo registro na próxima carga.
Com o código anterior em vigor e o novo pedido feito por um usuário, o pedido muda para o status Em entrega após 10 segundos de acordo com a lógica de demonstração interna do aplicativo. O navegador recebe uma notificação por push:
Ao usar o aplicativo no Google Chrome ou no Microsoft Edge, a notificação será exibida mesmo que o usuário não esteja usando ativamente o aplicativo Blazing Pizza. No entanto, o navegador deve estar em execução ou a notificação será exibida na próxima vez que o navegador for aberto.
Ao usar o PWA instalado, a notificação deve ser entregue mesmo se o usuário não estiver executando o aplicativo.
Manipular cliques de notificação
Registre um notificationclick manipulador de eventos para processar um usuário selecionando (clicando) uma notificação por push em seu dispositivo:
- Feche a notificação chamando
event.notification.close. - Chame
clients.openWindowpara criar um novo contexto de navegação de nível superior e carregue a URL passada para o método.
O seguinte exemplo no aplicativo Blazing Pizza leva o usuário para a página de status do pedido relativo à notificação. A URL é fornecida pelo event.notification.data.url parâmetro, que é enviado pelo servidor no conteúdo da notificação.
No arquivo do trabalho de serviço (service-worker.js):
self.addEventListener('notificationclick', event => {
event.notification.close();
event.waitUntil(clients.openWindow(event.notification.data.url));
});
Se o PWA estiver instalado no dispositivo, o PWA será mostrado no dispositivo. Se o PWA não estiver instalado, o usuário será levado para a página do aplicativo no navegador.