Nota
O acesso a esta página requer autorização. Podes tentar iniciar sessão ou mudar de diretório.
O acesso a esta página requer autorização. Podes tentar mudar de diretório.
Observação
Esta não é a versão mais recente deste artigo. Para a versão atual, consulte a versão .NET 10 deste artigo.
Advertência
Esta versão do ASP.NET Core não é mais suportada. 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 .NET 10 deste artigo.
Blazor Os PWAs podem receber e exibir notificações push (mensagens de dados) de um servidor back-end, mesmo quando o usuário não está usando ativamente o aplicativo. 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 que 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 dados atualizados em seu próximo retorno ao aplicativo, mesmo que esteja offline quando a notificação por push for emitida.
Os mecanismos para enviar, receber e exibir uma notificação por push são independentes do Blazor WebAssembly. O envio de uma notificação por push é implementado pelo servidor back-end, que pode usar qualquer tecnologia. Receber e exibir uma notificação por push no cliente é implementado no arquivo JavaScript (JS) do operador de serviço.
O exemplo neste artigo usa notificações por push para fornecer atualizações de status de pedidos aos clientes de uma pizzaria com base no aplicativo de demonstração PWA Blazing Pizza Workshop. Você não é obrigado a participar do workshop on-line 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 Padrão de unidade de trabalho (UoW) 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 notificações por push localmente, por exemplo, com o PowerShell ou o IIS, ou usando uma ferramenta online.
Espaços reservados usados no código de exemplo deste artigo:
-
{PUBLIC KEY}: A chave pública. -
{PRIVATE KEY}: A chave privada.
Para exemplos de C# deste artigo, atualize o endereço de someone@example.com email para corresponder ao endereço usado ao criar o par de chaves personalizadas.
Ao implementar notificações push, certifique-se de que as chaves criptográficas são gerenciadas com segurança:
- Geração de chaves: use uma biblioteca ou ferramenta confiável para gerar as chaves pública e privada. 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 módulo de segurança de hardware (HSM) ou armazenamento criptografado. Nunca exponha chaves privadas ao cliente.
- Uso da chave: use a chave privada apenas para assinar as cargas úteis de notificações por push. Certifique-se de que a chave pública seja distribuída com segurança aos clientes.
Para obter mais informações sobre práticas recomendadas de criptografia, consulte Serviços de criptografia.
Criar uma subscrição
Antes de enviar notificações por push para um usuário, o aplicativo deve pedir 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 se inscrever para receber notificações do aplicativo. O exemplo a seguir pergunta aos utilizadores quando eles chegam à página de checkout (Checkout componente) porque naquele momento é claro que o utilizador está seriamente interessado em fazer um pedido.
Se o usuário concordar em receber notificações, o exemplo a seguir envia os dados da assinatura de notificação por push para o servidor, onde 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 de 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 numa string codificada em base64? (Stack Overflow).
Um objeto de subscrição e um ponto de extremidade para notificações de subscrição são criados no servidor. O endpoint recebe chamadas de API da Web do cliente com dados de subscrição de notificações por push, incluindo os tokens criptográficos. Os dados são armazenados no banco de dados de 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 utilizador.
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 endpoint é definido no método de extensão da MapPizzaApi aplicação, que é chamado no ficheiro da Program aplicação para configurar endpoints da API da Web para a aplicação. A assinatura de notificação do usuário (NotificationSubscription), que 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);
});
No BlazingPizza.Client/HttpRepository.cs, o método SubscribeToNotifications emite um HTTP PUT para o endpoint de subscrições 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 através da chamada a
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 do OnInitialized e é executado 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 componentes (OnInitializedAsync). Essa abordagem torna o componente mais rápido.
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 checkout para ver a solicitação de assinatura:
Escolha Permitir e verifique se há erros no console das ferramentas de desenvolvedor do navegador. Você pode configurar um ponto de interrupção no código do PizzaApiExtensions e do MapPut("/notifications/subscribe"...) e executar o aplicativo com depuração para inspecionar os dados recebidos do navegador. Os dados incluem um URL de ponto de extremidade e os 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 para o Google Chrome ou o Microsoft Edge, selecione o ícone "informações" (🛈) à esquerda da barra de endereço do navegador e altere as Notificações de volta para Perguntar (padrão), como visto na imagem a seguir:
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 a aplicação por um pacote NuGet de terceiros, WebPush que é usado pelo projeto de servidor (BlazingPizza.Server) na aplicação Blazing Pizza.
O SendNotificationAsync método envia notificações de pedidos usando a assinatura capturada. O código a seguir faz uso das APIs WebPush para despachar a notificação. A carga da notificação é serializada por JSON e inclui uma mensagem e uma URL. A mensagem é exibida para o usuário, e o 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.
Atenção
No exemplo a seguir, recomendamos o uso de uma abordagem segura para fornecer a chave privada. Ao trabalhar localmente no Development ambiente, uma chave privada pode ser fornecida à aplicação usando a ferramenta Secret Manager . Em Development, Staging, e Production ambientes, pode ser usado o Azure Key Vault com Azure Managed Identities , notando de passagem que, para obter a chave privada de um certificado a partir de um cofre de chaves, o certificado deve ter 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 de desenvolvedor 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 Mensagens por push . 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 trabalhador 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 registra Installing service worker.... Ao ter dificuldades para fazer com que o service worker atualize, use a guia Aplicativo no console de ferramentas de desenvolvedor do navegador. Em Service Workers, escolha Atualizar ou use Cancelar registo para forçar um novo registo no próximo carregamento.
Com o código anterior em vigor e com o novo pedido feito por um utilizador, o pedido é movido para o estado Em entrega após 10 segundos, de acordo com a lógica de demonstração integrada da aplicação. O navegador recebe uma notificação push:
Ao usar o aplicativo no Google Chrome ou no Microsoft Edge, a notificação aparece 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 aparece na próxima vez que o navegador for aberto.
Ao usar o PWA instalado, a notificação deve ser entregue mesmo que o usuário não esteja executando o aplicativo.
Lidar com 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 para carregar a URL passada para o método.
O exemplo a seguir no aplicativo Blazing Pizza leva o usuário à página de status do pedido referente à notificação. A URL é fornecida pelo parâmetro event.notification.data.url, que é enviado pelo servidor no conteúdo da notificação.
No arquivo do service worker (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á direcionado para a página do aplicativo em seu navegador.