Esercitazione: Inviare notifiche push alle app Xamarin.Forms con Hub di notifica di Azure tramite un servizio back-end
In questa esercitazione viene usato Hub di notifica di Azure per inviare notifiche push a un'applicazione Xamarin.Forms per Android e iOS.
Un back-end dell'API Web ASP.NET Coreviene usato per gestire la registrazione del dispositivo per il client usando il metodo di installazione più recente e migliore. Il servizio invierà inoltre notifiche push in modalità multipiattaforma.
Queste operazioni vengono gestite tramite l'SDK di Hub di notifica per le operazioni back-end. Per altre informazioni sull'approccio generale, vedere Registrazione dal back-end dell'app.
Durante l'esercitazione vengono eseguiti i passaggi seguenti:
- Configurare i servizi di notifica push e Hub di notifica di Azure.
- Creare un'applicazione back-end API Web ASP.NET Core.
- Creare un'applicazione Xamarin.Forms multipiattaforma.
- Configurare il progetto Android nativo per le notifiche push.
- Configurare il progetto iOS nativo per le notifiche push.
- Testare la soluzione.
Prerequisiti
Per seguire l'esercitazione occorre:
- Una sottoscrizione di Azure in cui sia possibile creare e gestire risorse.
- Un Mac con Visual Studio per Mac installato o un PC che esegue Visual Studio 2019.
- Gli utenti di Visual Studio 2019 devono anche avere i carichi di lavoro Sviluppo di applicazioni per dispositivi mobili con .NET e Sviluppo ASP.NET e Web installati.
- La possibilità di eseguire l'app in Android (dispositivo fisico o emulatore) o iOS (solo dispositivo fisico).
Per Android, è necessario avere:
- Un dispositivo fisico sbloccato per lo sviluppo o un emulatore (che esegue l'API 26 e versioni successive con Google Play Services) .
Per iOS, è necessario avere:
- Un account Apple Developer attivo.
- Dispositivo iOS fisico registrato nell'account sviluppatore(che esegue iOS 13.0 e versioni successive).
- Un certificato con estensione p12development installato nella keychain che consente di eseguire un'app in un dispositivo fisico.
Nota
Il simulatore iOS non supporta le notifiche remote, quindi è necessario un dispositivo fisico per esplorare questo esempio in iOS. Non è però necessario eseguire l'app sia in Android che in iOS per completare questa esercitazione.
Non occorre avere un'esperienza precedente per seguire i passaggi di questo esempio. È tuttavia utile avere familiarità con gli aspetti seguenti.
- Portale Apple Developer
- ASP.NET Core e API Web
- Google Firebase Console
- Microsoft Azure e Inviare notifiche push alle app iOS con Hub di notifica di Azure.
- Xamarin e Xamarin.Forms.
Importante
I passaggi indicati sono specifici per Visual Studio per Mac. È possibile seguirli usando Visual Studio 2019 ma potrebbero esserci alcune differenze da risolvere, ad esempio le descrizioni dell'interfaccia utente e dei flussi di lavoro, i nomi dei modelli, la configurazione dell'ambiente e così via.
Configurare i servizi di notifica push e Hub di notifica di Azure
In questa sezione vengono configurati Firebase Cloud Messaging (FCM) e Apple Push Notification Services (APNS) . Viene quindi creato e configurato un hub di notifica da usare con questi servizi.
Creare un progetto Firebase e abilitare Firebase Cloud Messaging per Android
Accedere alla console di Firebase. Creare un nuovo progetto Firebase immettendo PushDemo in Project name (Nome progetto).
Nota
Verrà generato automaticamente un nome univoco. Per impostazione predefinita, questo nome è costituito da una variante in minuscolo del nome specificato più un numero generato separato da un trattino. Se si vuole è possibile modificarlo, purché rimanga un nome univoco globale.
Dopo aver creato il progetto, selezionare Add Firebase to your Android app (Aggiungi Firebase all'app Android).

Nella pagina Add Firebase to your Android app (Aggiungere Firebase all'app Android) eseguire la procedura seguente.
In Android package name (Nome pacchetto Android) immettere un nome per il pacchetto. Ad esempio:
com.<organization_identifier>.<package_name>.
Selezionare Registra l'app.
Selezionare Scaricare google-services.json. Salvare quindi il file in una cartella locale per usarlo in un secondo momento e selezionare Avanti.

Selezionare Avanti.
Selezionare Continue to console (Passa alla console).
Nota
Se il pulsante Continue to console non è abilitato, a causa del controllo di verifica dell'installazione, scegliere Ignora questo passaggio.
Nella console di Firebase selezionare il file COG per il progetto. Selezionare quindi Project Settings (Impostazioni progetto).

Nota
Se non è stato scaricato il file google-services.json, è possibile scaricarlo da questa pagina.
Passare alla scheda Cloud Messaging in alto. Copiare e salvare il valore di Chiave server per un uso successivo. Questo valore verrà usato per configurare l'hub di notifica.

Registrare l'app iOS per le notifiche push
Per inviare notifiche push a un'app per iOS, registrare l'applicazione con Apple ed eseguire un'altra registrazione per abilitare le notifiche push.
Se l'app non è stata ancora registrata, passare al portale di provisioning iOS in Apple Developer Center. Accedere al portale con l'ID Apple, passare a Certificati, Profili identificatori e quindi selezionare Identificatori&. Fare clic su + per registrare una nuova app.

Nella schermata Register a New Identifier (Registra un nuovo identificatore) selezionare il pulsante di opzione App IDs (ID app). Selezionare quindi Continua.

Aggiornare i tre valori seguenti per la nuova app e quindi selezionare Continue (Continua).
Descrizione: digitare un nome descrittivo per l'app.
ID bundle: immettere un ID bundle del modulo com.organization_identifier<>.<>product_name come indicato nella Guida alla distribuzione delle app. Nello screenshot seguente il valore
mobcatviene usato come identificatore dell'organizzazione e il valore PushDemo viene usato come nome del prodotto.
Push Notifications (Notifiche Push): selezionare l'opzione Push Notifications (Notifiche push) nella sezione Capabilities (Funzionalità).

Questa azione consente di generare l'ID app e richiede all'utente di confermare le informazioni. Fare clic su Continue (Continua) e quindi selezionare Register per confermare il nuovo ID app.

Dopo aver selezionato Registra, viene visualizzato il nuovo ID app come elemento della riga nella pagina Certificati, Profili identificatori&.
Nella pagina Certificati, Identificatori profili, in Identificatori & individuare l'elemento della riga ID app creato. Selezionare quindi la relativa riga per visualizzare la schermata Edit your App ID Configuration (Modifica la configurazione dell'ID app).
Creazione di un certificato per Hub di notifica
Per garantire il funzionamento dell'hub di notifica con Apple Push Notification Services (APNS) è necessario un certificato, che può essere fornito in uno dei due modi seguenti:
Creazione di un certificato push p12 che può essere caricato direttamente in Hub di notifica (approccio originale)
Creazione di un certificato p8 che può essere usato per l'autenticazione basata su token (approccio consigliato più recente)
L'approccio più recente presenta diversi vantaggi, come documentato in Autenticazione basata su token (HTTP/2) per APNS. Oltre a richiedere un minor numero di passaggi, è una scelta obbligata per scenari specifici. Sono però state fornite le procedure per entrambi gli approcci, perché sono ugualmente applicabili ai fini di questa esercitazione.
OPZIONE 1: Creazione di un certificato push p12 che può essere caricato direttamente in Hub di notifica
Sul Mac eseguire lo strumento Accesso Portachiavi. Può essere aperto dalla cartella Utilities (Utility) o Other (Altro) nella finestra di avvio.
Selezionare Accesso Portachiavi, espandere Certificate Assistant (Assistente certificati) e quindi selezionare Request a Certificate from a Certificate Authority (Richiedi certificato da Autorità di certificazione).

Nota
Per impostazione predefinita, lo strumento seleziona la prima voce dell'elenco. Ciò potrebbe causare problemi se la prima voce dell'elenco nella sezione Certificates (Certificati) non è Apple Worldwide Developer Relations Certification Authority (Autorità di certificazione relazioni sviluppatori Apple a livello mondiale). Prima di generare la richiesta di firma del certificato, verificare che sia presente una voce non di chiave oppure che sia selezionata la chiave Apple Worldwide Developer Relations Certification Authority.
Selezionare User Email Address (Indirizzo di posta elettronica utente), immettere il valore Common Name (Nome comune), assicurarsi di specificare Saved to disk (Salvata su disco) e quindi selezionare Continue (Continua). Lasciare vuoto il campo CA Email Address (Indirizzo di posta elettronica CA), in quanto non è obbligatorio.

Immettere un nome per il file di richiesta di firma del certificato (CSR) in Salva con nome, selezionare il percorso in Where (Dove) e quindi selezionare Save (Salva).

Questa azione consente di salvare il file CSR nel percorso selezionato. Il percorso predefinito è Desktop. Tenere a mente il percorso scelto per il file.
Tornare alla pagina Certificati, Identificatori & profili nel portale di provisioning iOS, scorrere verso il basso fino all'opzione Notifiche push selezionata e quindi selezionare Configura per creare il certificato.

Verrà visualizzata la finestra Apple Push Notification service TLS/SSL Certificates (Certificati TLS/SSL di Apple Push Notification Service). Fare clic sul pulsante Create Certificate (Crea certificato) nella sezione Development TLS/SSL Certificate (Certificato TLS/SSL per lo sviluppo).

Verrà visualizzata la schermata Create a new Certificate (Crea un nuovo certificato).
Nota
Questa esercitazione usa un certificato di sviluppo. La stessa procedura viene usata per registrare un certificato di produzione. Per l'invio delle notifiche, assicurarsi di usare lo stesso tipo di certificato.
Selezionare Choose File (Scegli file), passare al percorso in cui è stato salvato il file CSR e quindi fare doppio clic sul nome del certificato per caricarlo. Selezionare quindi Continua.
Dopo che il portale avrà creato il certificato, fare clic sul pulsante Download (Scarica). Salvare il certificato e prendere nota del percorso di salvataggio.

Il certificato viene scaricato e salvato nel computer nella cartella Download.

Nota
Per impostazione predefinita, il certificato di sviluppo scaricato viene denominato aps_development.cer.
Fare doppio clic sul certificato push scaricato aps_development.cer. Questa azione consente di installare il nuovo certificato in Keychain, come illustrato nell'immagine seguente:

Nota
Anche potrebbe essere diverso, se il nome del certificato verrà preceduto da Apple Development iOS Push Services (Servizi push iOS per lo sviluppo Apple) e sarà associato all'identificatore di bundle appropriato.
In Accesso Portachiavi premere CTRL + clic sul nuovo certificato push creato nella categoria Certificates (Certificati). Selezionare Export (Esporta), assegnare un nome al file, selezionare il formato p12 e quindi selezionare Save (Salva).

Facoltativamente è possibile scegliere di proteggere il certificato tramite una password. Fare clic su OK se si desidera ignorare la creazione della password. Prendere nota del nome del file e del percorso del certificato p12 esportato. Verranno usati per abilitare l'autenticazione con Apple Push Notification Service.
Nota
Il nome e il percorso del file p12 potrebbero essere diversi rispetto a quelli illustrati in questa esercitazione.
OPZIONE 2: Creazione di un certificato p8 che può essere usato per l'autenticazione basata su token
Prendere nota dei dettagli seguenti:
- App ID Prefix (Prefisso ID app) (Team ID) (ID team)
- Bundle ID (ID bundle)
Tornare in Certificati, Identificatori & profili, fare clic su Chiavi.
Nota
Se è già stata configurata una chiave per APNS, è possibile riutilizzare il certificato p8 scaricato subito dopo la creazione. In tal caso, è possibile ignorare i passaggi da 3 a 5.
Per creare una nuova chiave, fare clic sul pulsante + o sul pulsante Create a key (Crea una chiave).
Specificare un valore appropriato in Key Name (Nome chiave) e quindi selezionare l'opzione Apple Push Notifications service (APNS) e quindi fare clic su Continue (Continua), seguito da Register (Registra) nella schermata successiva.
Fare clic su Download (Scarica) e quindi spostare il file p8 (e prefisso AuthKey_ ) in una directory locale sicura, infine fare clic su Done (Fine).
Nota
Assicurarsi di conservare il file p8 in un luogo sicuro e salvare un backup. Dopo aver scaricato la chiave, non è possibile scaricarla di nuovo perché la copia del server è stata rimossa.
In Keys (Chiavi) fare clic sulla chiave creata (oppure su una chiave esistente se si è scelto di usare tale chiave).
Prendere nota del valore di Key ID (ID chiave).
Aprire il certificato p8 in un'applicazione appropriata di propria scelta, ad esempio Visual Studio Code. Prendere nota del valore della chiave, ovvero del valore compreso tra -----BEGIN PRIVATE KEY----- and -----END PRIVATE KEY----- .
-----BEGIN PRIVATE KEY-----
<key_value>
-----END PRIVATE KEY-----Nota
Si tratta del valore del token che verrà usato in seguito per configurare Hub di notifica.
Al termine di questa procedura, è necessario disporre delle informazioni seguenti per usarle in un secondo momento in Configurare l'hub di notifica con le informazioni del servizio APNS:
- Team ID (ID team) (vedere il passaggio 1)
- Bundle ID (ID bundle) (vedere il passaggio 1)
- Key ID (ID chiave) (vedere il passaggio 7)
- Valore token (Valore token) (valore della chiave p8 ottenuto nel passaggio 8)
Creare un profilo di provisioning per l'app
Tornare al portale di provisioning iOS, selezionare Certificati, Profili identificatori&, selezionare Profili dal menu a sinistra e quindi selezionare + per creare un nuovo profilo. Verrà visualizzata la schermata Register a New Provisioning Profile (Registra un nuovo profilo di provisioning).
In Development (Sviluppo) selezionare iOS App Development (Sviluppo app iOS) come tipo di profilo di provisioning e quindi Continue (Continua).

Selezionare quindi l'ID app creata nell'elenco a discesa App ID (ID app) e selezionare Continue (Continua).

Nella finestra Select certificates (Seleziona certificati) selezionare il certificato di sviluppo usato per la firma del codice e quindi fare clic su Continue (Continua).
Nota
Questo non è il certificato push creato nel passaggio precedente. Si tratta invece del certificato di sviluppo. Se non ne esiste uno, è necessario crearlo perché è un prerequisito di questa esercitazione. È possibile creare certificati di sviluppo nel portale Apple Developer, tramite Xcode o in Visual Studio.
Tornare alla pagina Certificati, Identificatori & profili , selezionare Profili dal menu a sinistra e quindi selezionare + per creare un nuovo profilo. Verrà visualizzata la schermata Register a New Provisioning Profile (Registra un nuovo profilo di provisioning).
Nella finestra Select certificates (Seleziona certificati) selezionare il certificato di sviluppo creato. Selezionare quindi Continua.
Selezionare quindi i dispositivi da usare per il test e selezionare Continue (Continua).
Scegliere infine un nome per il profilo in Provisioning Profile Name (Nome profilo di provisioning) e selezionare Generate (Genera).

Quando viene creato il nuovo profilo di provisioning, selezionare Download. Prendere nota del percorso di salvataggio.
Passare al percorso del profilo di provisioning e quindi fare doppio clic su di esso per installarlo nel computer di sviluppo.
Creare un hub di notifica
In questa sezione viene creato un hub di notifica e viene configurata l'autenticazione con APNS. È possibile usare un certificato push p12 o l'autenticazione basata su token. Se si vuole usare un hub di notifica che è già stato creato, è possibile ignorare il passaggio 5.
Accedere ad Azure.
Fare clic su Crea una risorsa, quindi cercare e scegliere Hub di notifica e fare clic su Crea.
Aggiornare i campi seguenti, quindi fare clic su Crea:
DETTAGLI DI BASE
Sottoscrizione: scegliere la sottoscrizione di destinazione dall'elenco a discesa Sottoscrizione
Gruppo di risorse: creare un nuovo gruppo di risorse o selezionarne uno esistenteDETTAGLI DELLO SPAZIO DEI NOMI
Spazio dei nomi di Hub di notifica: immettere un nome univoco globale per lo spazio dei nomi di Hub di notifica
Nota
Verificare che sia selezionata l'opzione Crea nuovo per questo campo.
DETTAGLI DELL'HUB DI NOTIFICA
Hub di notifica: immettere un nome per Hub di notifica
Località: scegliere una località idonea dall'elenco a discesa
Piano tariffario: mantenere l'opzione predefinita GratuitoNota
A meno che non sia stato raggiunto il numero massimo di hub per il livello gratuito.
Dopo il provisioning di Hub di notifica, passare a tale risorsa.
Passare al nuovo hub di notifica.
Selezionare Criteri di accesso dall'elenco (in GESTISCI).
Prendere nota dei valori di Nome criteri insieme ai corrispondenti valori di Stringa di connessione.
Configurare l'hub di notifica con le informazioni del servizio APN
In Notification Services (Servizi di notifica) selezionare Apple e quindi seguire i passaggi appropriati in base all'approccio scelto in precedenza nella sezione Creazione di un certificato per Hub di notifica.
Nota
Usare la modalità Production (Produzione) per Application Mode (Modalità applicazione) solo se si vuole inviare notifiche push agli utenti che hanno acquistato l'app dallo Store.
OPZIONE 1: Uso di un certificato push con estensione .p12
Selezionare Certificate.
Selezionare l'icona del file.
Selezionare il file con estensione p12 esportato in precedenza e quindi selezionare Open (Apri).
Se necessario, specificare la password corretta.
Selezionare la modalità Sandbox.
Selezionare Salva.
OPZIONE 2: Uso dell'autenticazione basata su token
Selezionare Token.
Immettere i valori seguenti acquisiti in precedenza:
- Key ID (ID chiave)
- Bundle ID (ID bundle)
- Team ID (ID team)
- Token
Scegliere Sandbox
Selezionare Salva.
Configurare l'hub di notifica con le informazioni di FCM
- Selezionare Google (GCM/FCM) nella sezione Settings (Impostazioni) nel menu a sinistra.
- Immettere la chiave del server annotata da Google Firebase Console.
- Sulla barra degli strumenti selezionare Salva.
Creare un'applicazione back-end API Web ASP.NET Core
In questa sezione viene creato il back-end dell'API Web ASP.NET Core per gestire la registrazione del dispositivo e l'invio delle notifiche all'app per dispositivi mobili Xamarin.Forms.
Creare un progetto Web
In Visual Studio selezionare File>Nuova soluzione.
Selezionare .NET Core>App>ASP.NET Core>API>Avanti.
Nella finestra di dialogo Configura la nuova API Web ASP.NET Core selezionare .NET Core 3.1 in Framework di destinazione.
Immettere PushDemoApi in Nome progetto e quindi selezionare Crea.
Avviare il debug (CMD + INVIO) per testare l'app basata su modello.
Nota
L'app basata su modello è configurata per l'uso di WeatherForecastController come launchUrl. L'impostazione viene eseguita in Proprietà>launchSettings.json.
Se viene visualizzato il messaggio Il certificato di sviluppo trovato non è valido:
Fare clic su Sì per consentire l'esecuzione dello strumento "dotnet dev-certs https" per risolvere il problema. Lo strumento "dotnet dev-certs https" chiede quindi di immettere una password per il certificato e la password del keychain.
Fare clic su Sì quando viene chiesto di installare e considerare attendibile il nuovo certificato, quindi immettere la password del Keychain.
Espandere la cartella Controllers, quindi eliminare WeatherForecastController.cs.
Eliminare WeatherForecast.cs.
Configurare i valori di configurazione locali usando lo strumento Secret Manager. La separazione dei segreti dalla soluzione assicura che non finiscano nel controllo del codice sorgente. Aprire il Terminale e quindi passare alla directory del file di progetto ed eseguire i comandi seguenti:
dotnet user-secrets init dotnet user-secrets set "NotificationHub:Name" <value> dotnet user-secrets set "NotificationHub:ConnectionString" <value>Sostituire i valori segnaposto con il nome dell'hub di notifica e i valori della stringa di connessione. Ne è stata presa nota nella sezione Creare un hub di notifica. Se non è stato fatto, è possibile cercarli in Azure.
NotificationsHub:Name:
Vedere Nome nel riepilogo Informazioni di base all'inizio della finestra Panoramica.NotificationHub:ConnectionString:
Vedere DefaultFullSharedAccessSignature in Criteri di accessoNota
Per gli scenari di produzione è possibile usare opzioni come Azure Key Vault per archiviare la stringa di connessione in modo sicuro. Per semplicità, i segreti verranno aggiunti alle impostazioni dell'applicazione Servizio app di Azure.
Autenticare i client usando una chiave API (facoltativo)
Le chiavi API non sono sicure come i token, ma sono sufficienti ai fini di questa esercitazione. Una chiave API può essere configurata facilmente tramite il middleware ASP.NET.
Aggiungere la chiave API ai valori di configurazione locali.
dotnet user-secrets set "Authentication:ApiKey" <value>Nota
Sostituire il valore segnaposto con il valore effettivo e prenderne nota.
PremereCTRL + clic sul progetto PushDemoApi, scegliere Nuova cartella dal menu Aggiungi, quindi fare clic su Aggiungi e specificare Authentication in Nome cartella.
Premere CTRL + clic sulla cartella Authentication, quindi scegliere Nuovo file dal menu Aggiungi.
Selezionare Generale>Classe vuota, immettere ApiKeyAuthOptions.cs in Nome, quindi fare clic su Nuovo e aggiungere l'implementazione seguente.
using Microsoft.AspNetCore.Authentication; namespace PushDemoApi.Authentication { public class ApiKeyAuthOptions : AuthenticationSchemeOptions { public const string DefaultScheme = "ApiKey"; public string Scheme => DefaultScheme; public string ApiKey { get; set; } } }Aggiungere alla cartella Authentication un'altra classe vuota denominata ApiKeyAuthHandler.cs, quindi aggiungere l'implementazione seguente.
using System; using System.Collections.Generic; using System.Linq; using System.Security.Claims; using System.Text.Encodings.Web; using System.Threading.Tasks; using Microsoft.AspNetCore.Authentication; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; namespace PushDemoApi.Authentication { public class ApiKeyAuthHandler : AuthenticationHandler<ApiKeyAuthOptions> { const string ApiKeyIdentifier = "apikey"; public ApiKeyAuthHandler( IOptionsMonitor<ApiKeyAuthOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock) {} protected override Task<AuthenticateResult> HandleAuthenticateAsync() { string key = string.Empty; if (Request.Headers[ApiKeyIdentifier].Any()) { key = Request.Headers[ApiKeyIdentifier].FirstOrDefault(); } else if (Request.Query.ContainsKey(ApiKeyIdentifier)) { if (Request.Query.TryGetValue(ApiKeyIdentifier, out var queryKey)) key = queryKey; } if (string.IsNullOrWhiteSpace(key)) return Task.FromResult(AuthenticateResult.Fail("No api key provided")); if (!string.Equals(key, Options.ApiKey, StringComparison.Ordinal)) return Task.FromResult(AuthenticateResult.Fail("Invalid api key.")); var identities = new List<ClaimsIdentity> { new ClaimsIdentity("ApiKeyIdentity") }; var ticket = new AuthenticationTicket( new ClaimsPrincipal(identities), Options.Scheme); return Task.FromResult(AuthenticateResult.Success(ticket)); } } }Nota
Un gestore di autenticazione è un tipo che implementa il comportamento di uno schema, in questo caso uno schema di chiave API personalizzato.
Aggiungere alla cartella Authentication un'altra classe vuota denominata ApiKeyAuthenticationBuilderExtensions.cs, quindi aggiungere l'implementazione seguente.
using System; using Microsoft.AspNetCore.Authentication; namespace PushDemoApi.Authentication { public static class AuthenticationBuilderExtensions { public static AuthenticationBuilder AddApiKeyAuth( this AuthenticationBuilder builder, Action<ApiKeyAuthOptions> configureOptions) { return builder .AddScheme<ApiKeyAuthOptions, ApiKeyAuthHandler>( ApiKeyAuthOptions.DefaultScheme, configureOptions); } } }Nota
Questo metodo di estensione semplifica il codice di configurazione del middleware in Startup.cs, rendendolo più leggibile e generalmente più semplice da seguire.
In Startup.cs aggiornare il metodo ConfigureServices per configurare l'autenticazione della chiave API sotto la chiamata al metodo services.AddControllers.
using PushDemoApi.Authentication; using PushDemoApi.Models; using PushDemoApi.Services; public void ConfigureServices(IServiceCollection services) { services.AddControllers(); services.AddAuthentication(options => { options.DefaultAuthenticateScheme = ApiKeyAuthOptions.DefaultScheme; options.DefaultChallengeScheme = ApiKeyAuthOptions.DefaultScheme; }).AddApiKeyAuth(Configuration.GetSection("Authentication").Bind); }Sempre in Startup.cs aggiornare il metodo Configure per chiamare i metodi di estensione UseAuthentication e UseAuthorization sull'interfaccia IApplicationBuilder dell'app. Assicurarsi che questi metodi vengano chiamati dopo UseRouting e prima di app.UseEndpoints.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseHttpsRedirection(); app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); }Nota
Chiamando UseAuthentication viene registrato il middleware che usa gli schemi di autenticazione registrati in precedenza (da ConfigureServices). Questo metodo deve essere chiamato prima di qualsiasi middleware che dipende dagli utenti da autenticare.
Aggiungere le dipendenze e configurare i servizi
ASP.NET Core supporta lo schema progettuale di software per l'inserimento di dipendenze, una tecnica per ottenere l'IoC (Inversion of Control) tra le classi e le relative dipendenze.
L'uso dell'hub di notifica e dell'SDK di Hub di notifica per le operazioni back-endè incapsulato in un servizio. Il servizio viene registrato e reso disponibile mediante un'astrazione idonea.
Premere CTRL + clic sulla cartella Dependencies e quindi scegliere Gestisci pacchetti NuGet.
Cercare Microsoft.Azure.NotificationHubs e assicurarsi che sia selezionato.
Fare clic su Aggiungi pacchetti, quindi fare clic su Accetta quando viene chiesto di accettare le condizioni di licenza.
PremereCTRL + clic sul progetto PushDemoApi, scegliere Nuova cartella dal menu Aggiungi, quindi fare clic su Aggiungi e specificare Models in Nome cartella.
Premere CTRL + clic sulla cartella Models, quindi scegliere Nuovo file dal menu Aggiungi.
Selezionare Generale>Classe vuota, immettere PushTemplates.cs in Nome, quindi fare clic su Nuovo e aggiungere l'implementazione seguente.
namespace PushDemoApi.Models { public class PushTemplates { public class Generic { public const string Android = "{ \"notification\": { \"title\" : \"PushDemo\", \"body\" : \"$(alertMessage)\"}, \"data\" : { \"action\" : \"$(alertAction)\" } }"; public const string iOS = "{ \"aps\" : {\"alert\" : \"$(alertMessage)\"}, \"action\" : \"$(alertAction)\" }"; } public class Silent { public const string Android = "{ \"data\" : {\"message\" : \"$(alertMessage)\", \"action\" : \"$(alertAction)\"} }"; public const string iOS = "{ \"aps\" : {\"content-available\" : 1, \"apns-priority\": 5, \"sound\" : \"\", \"badge\" : 0}, \"message\" : \"$(alertMessage)\", \"action\" : \"$(alertAction)\" }"; } } }Nota
Questa classe contiene i payload di notifica in formato token per le notifiche generiche e automatiche richieste da questo scenario. I payload sono definiti al di fuori dell'installazione per consentire la sperimentazione senza dover aggiornare le installazioni esistenti tramite il servizio. Questa modalità di gestione delle modifiche alle installazioni non rientra nell'ambito di questa esercitazione. Per gli scenari di produzione, prendere in considerazione i modelli personalizzati.
Aggiungere alla cartella Models un'altra classe vuota denominata DeviceInstallation.cs, quindi aggiungere l'implementazione seguente.
using System.Collections.Generic; using System.ComponentModel.DataAnnotations; namespace PushDemoApi.Models { public class DeviceInstallation { [Required] public string InstallationId { get; set; } [Required] public string Platform { get; set; } [Required] public string PushChannel { get; set; } public IList<string> Tags { get; set; } = Array.Empty<string>(); } }Aggiungere alla cartella Models un'altra classe vuota denominata NotificationRequest.cs, quindi aggiungere l'implementazione seguente.
using System; namespace PushDemoApi.Models { public class NotificationRequest { public string Text { get; set; } public string Action { get; set; } public string[] Tags { get; set; } = Array.Empty<string>(); public bool Silent { get; set; } } }Aggiungere alla cartella Models un'altra classe vuota denominata NotificationHubOptions.cs, quindi aggiungere l'implementazione seguente.
using System.ComponentModel.DataAnnotations; namespace PushDemoApi.Models { public class NotificationHubOptions { [Required] public string Name { get; set; } [Required] public string ConnectionString { get; set; } } }Aggiungere al progetto PushDemoApi una nuova cartella denominata Services.
Aggiungere alla cartella Services un'interfaccia vuota denominata INotificationService.cs, quindi aggiungere l'implementazione seguente.
using System.Threading; using System.Threading.Tasks; using PushDemoApi.Models; namespace PushDemoApi.Services { public interface INotificationService { Task<bool> CreateOrUpdateInstallationAsync(DeviceInstallation deviceInstallation, CancellationToken token); Task<bool> DeleteInstallationByIdAsync(string installationId, CancellationToken token); Task<bool> RequestNotificationAsync(NotificationRequest notificationRequest, CancellationToken token); } }Aggiungere alla cartella Services una classe vuota denominata NotificationHubsService.cs, quindi aggiungere il codice seguente per implementare l'interfaccia INotificationService:
using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.Azure.NotificationHubs; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using PushDemoApi.Models; namespace PushDemoApi.Services { public class NotificationHubService : INotificationService { readonly NotificationHubClient _hub; readonly Dictionary<string, NotificationPlatform> _installationPlatform; readonly ILogger<NotificationHubService> _logger; public NotificationHubService(IOptions<NotificationHubOptions> options, ILogger<NotificationHubService> logger) { _logger = logger; _hub = NotificationHubClient.CreateClientFromConnectionString( options.Value.ConnectionString, options.Value.Name); _installationPlatform = new Dictionary<string, NotificationPlatform> { { nameof(NotificationPlatform.Apns).ToLower(), NotificationPlatform.Apns }, { nameof(NotificationPlatform.Fcm).ToLower(), NotificationPlatform.Fcm } }; } public async Task<bool> CreateOrUpdateInstallationAsync(DeviceInstallation deviceInstallation, CancellationToken token) { if (string.IsNullOrWhiteSpace(deviceInstallation?.InstallationId) || string.IsNullOrWhiteSpace(deviceInstallation?.Platform) || string.IsNullOrWhiteSpace(deviceInstallation?.PushChannel)) return false; var installation = new Installation() { InstallationId = deviceInstallation.InstallationId, PushChannel = deviceInstallation.PushChannel, Tags = deviceInstallation.Tags }; if (_installationPlatform.TryGetValue(deviceInstallation.Platform, out var platform)) installation.Platform = platform; else return false; try { await _hub.CreateOrUpdateInstallationAsync(installation, token); } catch { return false; } return true; } public async Task<bool> DeleteInstallationByIdAsync(string installationId, CancellationToken token) { if (string.IsNullOrWhiteSpace(installationId)) return false; try { await _hub.DeleteInstallationAsync(installationId, token); } catch { return false; } return true; } public async Task<bool> RequestNotificationAsync(NotificationRequest notificationRequest, CancellationToken token) { if ((notificationRequest.Silent && string.IsNullOrWhiteSpace(notificationRequest?.Action)) || (!notificationRequest.Silent && (string.IsNullOrWhiteSpace(notificationRequest?.Text)) || string.IsNullOrWhiteSpace(notificationRequest?.Action))) return false; var androidPushTemplate = notificationRequest.Silent ? PushTemplates.Silent.Android : PushTemplates.Generic.Android; var iOSPushTemplate = notificationRequest.Silent ? PushTemplates.Silent.iOS : PushTemplates.Generic.iOS; var androidPayload = PrepareNotificationPayload( androidPushTemplate, notificationRequest.Text, notificationRequest.Action); var iOSPayload = PrepareNotificationPayload( iOSPushTemplate, notificationRequest.Text, notificationRequest.Action); try { if (notificationRequest.Tags.Length == 0) { // This will broadcast to all users registered in the notification hub await SendPlatformNotificationsAsync(androidPayload, iOSPayload, token); } else if (notificationRequest.Tags.Length <= 20) { await SendPlatformNotificationsAsync(androidPayload, iOSPayload, notificationRequest.Tags, token); } else { var notificationTasks = notificationRequest.Tags .Select((value, index) => (value, index)) .GroupBy(g => g.index / 20, i => i.value) .Select(tags => SendPlatformNotificationsAsync(androidPayload, iOSPayload, tags, token)); await Task.WhenAll(notificationTasks); } return true; } catch (Exception e) { _logger.LogError(e, "Unexpected error sending notification"); return false; } } string PrepareNotificationPayload(string template, string text, string action) => template .Replace("$(alertMessage)", text, StringComparison.InvariantCulture) .Replace("$(alertAction)", action, StringComparison.InvariantCulture); Task SendPlatformNotificationsAsync(string androidPayload, string iOSPayload, CancellationToken token) { var sendTasks = new Task[] { _hub.SendFcmNativeNotificationAsync(androidPayload, token), _hub.SendAppleNativeNotificationAsync(iOSPayload, token) }; return Task.WhenAll(sendTasks); } Task SendPlatformNotificationsAsync(string androidPayload, string iOSPayload, IEnumerable<string> tags, CancellationToken token) { var sendTasks = new Task[] { _hub.SendFcmNativeNotificationAsync(androidPayload, tags, token), _hub.SendAppleNativeNotificationAsync(iOSPayload, tags, token) }; return Task.WhenAll(sendTasks); } } }Nota
L'espressione tag fornita a SendTemplateNotificationAsync è limitata a 20 tag. È limitata a 6 per la maggior parte degli operatori, ma in questo caso l'espressione contiene solo operatori OR (| |). Se la richiesta contiene più di 20 tag, è necessario suddividerla in più richieste. Per altre informazioni, vedere Espressioni di routing e tag.
In Startup.cs aggiornare il metodo ConfigureServices per aggiungere NotificationHubsService come implementazione singleton di INotificationService.
using PushDemoApi.Models; using PushDemoApi.Services; public void ConfigureServices(IServiceCollection services) { ... services.AddSingleton<INotificationService, NotificationHubService>(); services.AddOptions<NotificationHubOptions>() .Configure(Configuration.GetSection("NotificationHub").Bind) .ValidateDataAnnotations(); }
Creare l'API Notifiche
Premere CTRL + clic sulla cartella Controllers, quindi scegliere Nuovo file dal menu Aggiungi.
Selezionare ASP.NET Core>Classe controller API Web, immettere NotificationsController in Nome, quindi fare clic su Nuovo.
Nota
Se si usa Visual Studio 2019, scegliere il modello Controller API con azioni di lettura/scrittura.
Aggiungere gli spazi dei nomi seguenti all'inizio del file.
using System.ComponentModel.DataAnnotations; using System.Net; using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using PushDemoApi.Models; using PushDemoApi.Services;Aggiornare il controller basato su modello in modo che derivi da ControllerBase e sia decorato con l'attributo ApiController.
[ApiController] [Route("api/[controller]")] public class NotificationsController : ControllerBase { // Templated methods here }Nota
La classe di base Controller fornisce il supporto per le viste, che non è però necessario in questo caso, quindi è possibile usare ControllerBase al suo posto. Se si usa Visual Studio 2019, è possibile ignorare questo passaggio.
Se si è scelto di completare la sezione Autenticare i client usando una chiave API, si dovrebbe anche decorare NotificationsController con l'attributo Authorize.
[Authorize]Aggiornare il costruttore in modo che accetti l'istanza registrata di INotificationService come argomento e la assegni a un membro di sola lettura.
readonly INotificationService _notificationService; public NotificationsController(INotificationService notificationService) { _notificationService = notificationService; }In launchSettings.json (all'interno della cartella Properties) modificare launchUrl da
weatherforecasta api/notifications in modo che corrisponda all'URL specificato nell'attributo RegistrationsControllerRoute.Avviare il debug (CMD + INVIO) per verificare che l'app usi il nuovo NotificationsController e restituisca uno stato 401 Non autorizzato.
Nota
Visual Studio non può avviare automaticamente l'app nel browser. D'ora in avanti si userà Postman per testare l'API.
In una nuova scheda di Postman impostare la richiesta su GET. Immettere l'indirizzo seguente sostituendo il segnaposto <applicationUrl> con https applicationUrl trovato in PropertieslaunchSettings.json>.
<applicationUrl>/api/notificationsNota
ApplicationUrl deve essere 'https://localhost:5001' per il profilo predefinito. Se si usa IIS (impostazione predefinita in Visual Studio 2019 in Windows), è consigliabile usare applicationUrl specificato nell'elemento iisSettings. Se l'indirizzo non è corretto, si riceverà una risposta 404.
Se si è scelto di completare la sezione Autenticare i client usando una chiave API, assicurarsi di configurare le intestazioni della richiesta in modo da includere il valore di apikey.
Chiave Valore apikey <your_api_key> Fare clic sul pulsante Send (Invia).
Nota
Si dovrebbe ricevere uno stato 200 OK con del contenuto JSON.
Se si riceve un avviso di verifica dei certificati SSL, è possibile disattivare l'impostazione di richiesta di verifica dei certificati SSL di Postman in Settings (Impostazioni).
Sostituire i metodi della classe basata su modello in NotificationsController.cs con il codice seguente.
[HttpPut] [Route("installations")] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest)] [ProducesResponseType((int)HttpStatusCode.UnprocessableEntity)] public async Task<IActionResult> UpdateInstallation( [Required]DeviceInstallation deviceInstallation) { var success = await _notificationService .CreateOrUpdateInstallationAsync(deviceInstallation, HttpContext.RequestAborted); if (!success) return new UnprocessableEntityResult(); return new OkResult(); } [HttpDelete()] [Route("installations/{installationId}")] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest)] [ProducesResponseType((int)HttpStatusCode.UnprocessableEntity)] public async Task<ActionResult> DeleteInstallation( [Required][FromRoute]string installationId) { var success = await _notificationService .DeleteInstallationByIdAsync(installationId, CancellationToken.None); if (!success) return new UnprocessableEntityResult(); return new OkResult(); } [HttpPost] [Route("requests")] [ProducesResponseType((int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.BadRequest)] [ProducesResponseType((int)HttpStatusCode.UnprocessableEntity)] public async Task<IActionResult> RequestPush( [Required]NotificationRequest notificationRequest) { if ((notificationRequest.Silent && string.IsNullOrWhiteSpace(notificationRequest?.Action)) || (!notificationRequest.Silent && string.IsNullOrWhiteSpace(notificationRequest?.Text))) return new BadRequestResult(); var success = await _notificationService .RequestNotificationAsync(notificationRequest, HttpContext.RequestAborted); if (!success) return new UnprocessableEntityResult(); return new OkResult(); }
Creare l'app per le API
Si creerà ora un'app per le API in Servizio app di Azure per l'hosting del servizio back-end.
Accedere al portale di Azure.
Fare clic su Crea una risorsa, quindi cercare e scegliere App per le API e fare clic su Crea.
Aggiornare i campi seguenti, quindi fare clic su Crea.
Nome app:
Immettere un nome univoco globale per App per le APISottoscrizione:
Scegliere la stessa sottoscrizione di destinazione in cui è stato creato l'hub di notifica.Gruppo di risorse:
Scegliere lo stesso gruppo di risorse in cui è stato creato l'hub di notifica.Piano di servizio app/Località:
Crea un nuovo piano di servizio appNota
Passare dall'opzione predefinita a un piano che includa il supporto di SSL. In caso contrario, sarà necessario eseguire i passaggi appropriati quando si usa l'app per dispositivi mobili per evitare che le richieste http vengano bloccate.
Application Insights:
Mantenere l'opzione suggerita (verrà creata una nuova risorsa con quel nome) o selezionare una risorsa esistente.Dopo il provisioning dell'app per le API, passare a tale risorsa.
Prendere nota della proprietà URL nel riepilogo Informazioni di base all'inizio della pagina Panoramica. Questo URL è l'endpoint back-end che verrà usato più avanti in questa esercitazione.
Nota
L'URL usa il nome dell'app per le API specificato prima, con il formato
https://<app_name>.azurewebsites.net.Selezionare Configurazione nell'elenco (sotto Impostazioni).
Per ognuna delle impostazioni seguenti fare clic su Nuova impostazione applicazione per compilare i campi Nome e Valore, quindi fare clic su OK.
Nome Valore Authentication:ApiKey<api_key_value> NotificationHub:Name<hub_name_value> NotificationHub:ConnectionString<hub_connection_string_value> Nota
Sono le stesse impostazioni definite in precedenza nelle impostazioni utente. Dovrebbe essere possibile copiarle. L'impostazione Authentication:ApiKey è obbligatoria solo se si è scelto di completare la sezione Autenticare i client usando una chiave API. Per gli scenari di produzione è possibile usare opzioni come Azure Key Vault. Queste opzioni sono state aggiunte come impostazioni dell'applicazione per semplicità in questo caso.
Dopo aver aggiunto tutte le impostazioni dell'applicazione, fare clic su Salva, quindi su Continua.
Pubblicare il servizio back-end
L'app verrà ora distribuita nell'app per le API per renderla accessibile da tutti i dispositivi.
Nota
I passaggi seguenti sono specifici per Visual Studio per Mac. Se si segue con Visual Studio 2019 in Windows, il flusso di pubblicazione sarà diverso. Vedere Pubblicare in Servizio app di Azure in Windows.
Impostare la configurazione da Debug a Rilascio, se non è già stato fatto.
Premere CTRL + clic sul progetto PushDemoApi e quindi scegliere Pubblica in Azure dal menu Pubblica.
Se richiesto, seguire il flusso di autenticazione. Usare l'account usato nella sezione precedente Creare l'app per le API.
Selezionare dall'elenco l'app per le API del servizio app di Azure creata in precedenza come destinazione della pubblicazione e quindi fare clic su Pubblica.
Al termine della procedura guidata, l'app viene pubblicata in Azure e aperta. Prendere nota dell'URL, se non è già stato fatto. Questo URL è l'endpoint back-end usato più avanti in questa esercitazione.
Convalida dell'API pubblicata
In Postman aprire una nuova scheda, impostare la richiesta su PUT e immettere l'indirizzo seguente. Sostituire il segnaposto con l'indirizzo di base di cui si è preso nota nella sezione precedente Pubblicare il servizio back-end.
https://<app_name>.azurewebsites.net/api/notifications/installationsNota
L'indirizzo di base deve essere in formato
https://<app_name>.azurewebsites.net/Se si è scelto di completare la sezione Autenticare i client usando una chiave API, assicurarsi di configurare le intestazioni della richiesta in modo da includere il valore di apikey.
Chiave Valore apikey <your_api_key> Scegliere l'opzione raw (non elaborato) per il corpo, quindi scegliere JSON dall'elenco delle opzioni di formato e includere del contenuto JSON segnaposto:
{}Fare clic su Send.
Nota
Dovrebbe essere visualizzato lo stato 422 UnprocessableEntity dal servizio.
Eseguire di nuovo i passaggi da 1 a 4, ma questa volta specificando l'endpoint delle richieste per convalidare la ricezione di una risposta di richiesta non valida 400 .
https://<app_name>.azurewebsites.net/api/notifications/requests
Nota
Non è ancora possibile testare l'API usando dati di richiesta validi, in quanto questa operazione richiede informazioni specifiche della piattaforma dall'app per dispositivi mobili client.
Creare un'applicazione Xamarin.Forms multipiattaforma
In questa sezione viene compilata un'applicazione per dispositivi mobili Xamarin.Forms che implementa le notifiche push in modalità multipiattaforma.
Consente di registrare e annullare la registrazione da un hub di notifica tramite il servizio back-end creato.
Viene visualizzato un avviso quando si specifica un'azione e l'app è in primo piano. Altrimenti le notifiche vengono visualizzate nel centro notifiche.
Nota
In genere le azioni di registrazione (e annullamento della registrazione) vengono eseguite nel punto appropriato del ciclo di vita dell'applicazione (o come parte del completamento dell'installazione) senza input espliciti dell'utente per tali azioni. Tuttavia, in questo esempio verrà richiesto un input esplicito dell'utente per facilitare l'esplorazione e il test di questa funzionalità.
Creare la soluzione Xamarin.Forms
In Visual Studio creare una nuova soluzione Xamarin.Forms usando App Forms vuota come modello e immettendo PushDemo in Nome progetto.
Nota
Nella finestra di dialogo Configura App Forms vuota verificare che il valore di Identificatore dell'organizzazione corrisponda al valore usato in precedenza e che siano selezionate entrambe le destinazioni Android e iOS.
Premere CTRL + clic sulla soluzione PushDemo, quindi scegliere Aggiorna pacchetti NuGet.
Premere CTRL + clic sulla soluzione PushDemo e quindi scegliere Gestisci pacchetti NuGet.
Cercare Newtonsoft.Json e verificare che sia selezionato.
Fare clic su Aggiungi pacchetti, quindi fare clic su Accetta quando viene chiesto di accettare le condizioni di licenza.
Compilare ed eseguire l'app su ogni piattaforma di destinazione (CMD + INVIO) per testare le esecuzioni dell'app basata su modello nei dispositivi.
Implementare i componenti multipiattaforma
PremereCTRL + clic sul progetto PushDemo, scegliere Nuova cartella dal menu Aggiungi, quindi fare clic su Aggiungi e specificare Models in Nome cartella.
Premere CTRL + clic sulla cartella Models, quindi scegliere Nuovo file dal menu Aggiungi.
Selezionare Generale>Classe vuota, immettere DeviceInstallation.cs, quindi aggiungere l'implementazione seguente.
using System.Collections.Generic; using Newtonsoft.Json; namespace PushDemo.Models { public class DeviceInstallation { [JsonProperty("installationId")] public string InstallationId { get; set; } [JsonProperty("platform")] public string Platform { get; set; } [JsonProperty("pushChannel")] public string PushChannel { get; set; } [JsonProperty("tags")] public List<string> Tags { get; set; } = new List<string>(); } }Aggiungere alla cartella Models una enumerazione vuota denominata PushDemoAction.cs con l'implementazione seguente.
namespace PushDemo.Models { public enum PushDemoAction { ActionA, ActionB } }Aggiungere al progetto PushDemo una nuova cartella denominata Services, quindi aggiungere alla cartella una classe vuota denominata ServiceContainer.cs con l'implementazione seguente.
using System; using System.Collections.Generic; namespace PushDemo.Services { public static class ServiceContainer { static readonly Dictionary<Type, Lazy<object>> services = new Dictionary<Type, Lazy<object>>(); public static void Register<T>(Func<T> function) => services[typeof(T)] = new Lazy<object>(() => function()); public static T Resolve<T>() => (T)Resolve(typeof(T)); public static object Resolve(Type type) { { if (services.TryGetValue(type, out var service)) return service.Value; throw new KeyNotFoundException($"Service not found for type '{type}'"); } } } }Nota
È una versione ridotta della classe ServiceContainer dal repository XamCAT. Verrà usata come contenitore IoC (Inversion of Control) leggero.
Aggiungere alla cartella Services un'interfaccia vuota denominata IDeviceInstallationService.cs, quindi aggiungere il codice seguente.
using PushDemo.Models; namespace PushDemo.Services { public interface IDeviceInstallationService { string Token { get; set; } bool NotificationsSupported { get; } string GetDeviceId(); DeviceInstallation GetDeviceInstallation(params string[] tags); } }Nota
Questa interfaccia verrà implementata e avviata tramite bootstrap da ogni destinazione in un secondo momento per fornire le funzionalità specifiche della piattaforma e le informazioni su DeviceInstallation richieste dal servizio back-end.
Aggiungere alla cartella Services un'altra interfaccia vuota denominata INotificationRegistrationService.cs, quindi aggiungere il codice seguente.
using System.Threading.Tasks; namespace PushDemo.Services { public interface INotificationRegistrationService { Task DeregisterDeviceAsync(); Task RegisterDeviceAsync(params string[] tags); Task RefreshRegistrationAsync(); } }Nota
Questa interfaccia gestirà l'interazione tra il client e il servizio back-end.
Aggiungere alla cartella Services un'altra interfaccia vuota denominata INotificationActionService.cs, quindi aggiungere il codice seguente.
namespace PushDemo.Services { public interface INotificationActionService { void TriggerAction(string action); } }Nota
Questa interfaccia viene usata come meccanismo semplice per centralizzare la gestione delle azioni di notifica.
Aggiungere alla cartella Services un'interfaccia vuota denominata IPushDemoNotificationActionService.cs che deriva da INotificationActionService, con l'implementazione seguente.
using System; using PushDemo.Models; namespace PushDemo.Services { public interface IPushDemoNotificationActionService : INotificationActionService { event EventHandler<PushDemoAction> ActionTriggered; } }Nota
Questo tipo è specifico dell'applicazione PushDemo e usa l'enumerazione PushDemoAction per identificare l'azione che viene attivata in modo fortemente tipizzato.
Aggiungere alla cartella Services una classe vuota denominata NotificationRegistrationService.cs che implementa l'interfaccia INotificationRegistrationService con il codice seguente.
using System; using System.Net.Http; using System.Text; using System.Threading.Tasks; using Newtonsoft.Json; using PushDemo.Models; using Xamarin.Essentials; namespace PushDemo.Services { public class NotificationRegistrationService : INotificationRegistrationService { const string RequestUrl = "api/notifications/installations"; const string CachedDeviceTokenKey = "cached_device_token"; const string CachedTagsKey = "cached_tags"; string _baseApiUrl; HttpClient _client; IDeviceInstallationService _deviceInstallationService; public NotificationRegistrationService(string baseApiUri, string apiKey) { _client = new HttpClient(); _client.DefaultRequestHeaders.Add("Accept", "application/json"); _client.DefaultRequestHeaders.Add("apikey", apiKey); _baseApiUrl = baseApiUri; } IDeviceInstallationService DeviceInstallationService => _deviceInstallationService ?? (_deviceInstallationService = ServiceContainer.Resolve<IDeviceInstallationService>()); public async Task DeregisterDeviceAsync() { var cachedToken = await SecureStorage.GetAsync(CachedDeviceTokenKey) .ConfigureAwait(false); if (cachedToken == null) return; var deviceId = DeviceInstallationService?.GetDeviceId(); if (string.IsNullOrWhiteSpace(deviceId)) throw new Exception("Unable to resolve an ID for the device."); await SendAsync(HttpMethod.Delete, $"{RequestUrl}/{deviceId}") .ConfigureAwait(false); SecureStorage.Remove(CachedDeviceTokenKey); SecureStorage.Remove(CachedTagsKey); } public async Task RegisterDeviceAsync(params string[] tags) { var deviceInstallation = DeviceInstallationService?.GetDeviceInstallation(tags); await SendAsync<DeviceInstallation>(HttpMethod.Put, RequestUrl, deviceInstallation) .ConfigureAwait(false); await SecureStorage.SetAsync(CachedDeviceTokenKey, deviceInstallation.PushChannel) .ConfigureAwait(false); await SecureStorage.SetAsync(CachedTagsKey, JsonConvert.SerializeObject(tags)); } public async Task RefreshRegistrationAsync() { var cachedToken = await SecureStorage.GetAsync(CachedDeviceTokenKey) .ConfigureAwait(false); var serializedTags = await SecureStorage.GetAsync(CachedTagsKey) .ConfigureAwait(false); if (string.IsNullOrWhiteSpace(cachedToken) || string.IsNullOrWhiteSpace(serializedTags) || string.IsNullOrWhiteSpace(DeviceInstallationService.Token) || cachedToken == DeviceInstallationService.Token) return; var tags = JsonConvert.DeserializeObject<string[]>(serializedTags); await RegisterDeviceAsync(tags); } async Task SendAsync<T>(HttpMethod requestType, string requestUri, T obj) { string serializedContent = null; await Task.Run(() => serializedContent = JsonConvert.SerializeObject(obj)) .ConfigureAwait(false); await SendAsync(requestType, requestUri, serializedContent); } async Task SendAsync( HttpMethod requestType, string requestUri, string jsonRequest = null) { var request = new HttpRequestMessage(requestType, new Uri($"{_baseApiUrl}{requestUri}")); if (jsonRequest != null) request.Content = new StringContent(jsonRequest, Encoding.UTF8, "application/json"); var response = await _client.SendAsync(request).ConfigureAwait(false); response.EnsureSuccessStatusCode(); } } }Nota
L'argomento apiKey è obbligatorio solo se si è scelto di completare la sezione Autenticare i client usando una chiave API.
Aggiungere alla cartella Services una classe vuota denominata PushDemoNotificationActionService.cs che implementa l'interfaccia IPushDemoNotificationActionService con il codice seguente.
using System; using System.Collections.Generic; using System.Linq; using PushDemo.Models; namespace PushDemo.Services { public class PushDemoNotificationActionService : IPushDemoNotificationActionService { readonly Dictionary<string, PushDemoAction> _actionMappings = new Dictionary<string, PushDemoAction> { { "action_a", PushDemoAction.ActionA }, { "action_b", PushDemoAction.ActionB } }; public event EventHandler<PushDemoAction> ActionTriggered = delegate { }; public void TriggerAction(string action) { if (!_actionMappings.TryGetValue(action, out var pushDemoAction)) return; List<Exception> exceptions = new List<Exception>(); foreach (var handler in ActionTriggered?.GetInvocationList()) { try { handler.DynamicInvoke(this, pushDemoAction); } catch (Exception ex) { exceptions.Add(ex); } } if (exceptions.Any()) throw new AggregateException(exceptions); } } }Aggiungere al progetto PushDemo una classe vuota denominata Config.cs con l'implementazione seguente.
namespace PushDemo { public static partial class Config { public static string ApiKey = "API_KEY"; public static string BackendServiceEndpoint = "BACKEND_SERVICE_ENDPOINT"; } }Nota
Questa classe viene usata come metodo semplice per mantenere i segreti fuori dal controllo del codice sorgente. È possibile sostituire questi valori come parte di una compilazione automatizzata o eseguirne l'override usando una classe parziale locale. Questa operazione verrà eseguita nel prossimo passaggio.
Il campo ApiKey è obbligatorio solo se si è scelto di completare la sezione Autenticare i client usando una chiave API.
Aggiungere al progetto PushDemo un'altra classe vuota denominata Config.local_secrets.cs con l'implementazione seguente.
namespace PushDemo { public static partial class Config { static Config() { ApiKey = "<your_api_key>"; BackendServiceEndpoint = "<your_api_app_url>"; } } }Nota
Sostituire i valori segnaposto con i propri. Si dovrebbe aver preso nota di questi valori durante la compilazione del servizio back-end. L'URL dell'app per le API dovrebbe essere
https://<api_app_name>.azurewebsites.net/. Ricordare di aggiungere*.local_secrets.*al file gitignore per evitare di eseguire il commit di questo file.Il campo ApiKey è obbligatorio solo se si è scelto di completare la sezione Autenticare i client usando una chiave API.
Aggiungere al progetto PushDemo una classe vuota denominata Bootstrap.cs con l'implementazione seguente.
using System; using PushDemo.Services; namespace PushDemo { public static class Bootstrap { public static void Begin(Func<IDeviceInstallationService> deviceInstallationService) { ServiceContainer.Register(deviceInstallationService); ServiceContainer.Register<IPushDemoNotificationActionService>(() => new PushDemoNotificationActionService()); ServiceContainer.Register<INotificationRegistrationService>(() => new NotificationRegistrationService( Config.BackendServiceEndpoint, Config.ApiKey)); } } }Nota
Il metodo Begin viene chiamato da ogni piattaforma quando l'app viene avviata passando un'implementazione specifica della piattaforma di IDeviceInstallationService.
L'argomento del costruttore NotificationRegistrationServiceapiKey è obbligatorio solo se si sceglie di completare la sezione Autenticare i client usando una chiave API.
Implementare l'interfaccia utente multipiattaforma
Nel progetto PushDemo aprire MainPage.xaml e sostituire il controllo StackLayout con il codice seguente.
<StackLayout VerticalOptions="EndAndExpand" HorizontalOptions="FillAndExpand" Padding="20,40"> <Button x:Name="RegisterButton" Text="Register" Clicked="RegisterButtonClicked" /> <Button x:Name="DeregisterButton" Text="Deregister" Clicked="DeregisterButtonClicked" /> </StackLayout>In MainPage.xaml.cs aggiungere ora un campo sottostante readonly in cui archiviare un riferimento all'implementazione di INotificationRegistrationService.
readonly INotificationRegistrationService _notificationRegistrationService;Nel costruttore MainPage risolvere l'implementazione di INotificationRegistrationService usando ServiceContainer e assegnarla al campo sottostante notificationRegistrationService.
public MainPage() { InitializeComponent(); _notificationRegistrationService = ServiceContainer.Resolve<INotificationRegistrationService>(); }Implementare i gestori eventi per gli eventi Clicked dei pulsanti RegisterButton e DeregisterButton chiamando i metodi Register/Deregister corrispondenti.
void RegisterButtonClicked(object sender, EventArgs e) => _notificationRegistrationService.RegisterDeviceAsync().ContinueWith((task) => { ShowAlert(task.IsFaulted ? task.Exception.Message : $"Device registered"); }); void DeregisterButtonClicked(object sender, EventArgs e) => _notificationRegistrationService.DeregisterDeviceAsync().ContinueWith((task) => { ShowAlert(task.IsFaulted ? task.Exception.Message : $"Device deregistered"); }); void ShowAlert(string message) => MainThread.BeginInvokeOnMainThread(() => DisplayAlert("PushDemo", message, "OK").ContinueWith((task) => { if (task.IsFaulted) throw task.Exception; }));In App.xaml.cs verificare che gli spazi dei nomi seguenti siano referenziati.
using PushDemo.Models; using PushDemo.Services; using Xamarin.Essentials; using Xamarin.Forms;Implementare il gestore eventi per l'evento IPushDemoNotificationActionServiceActionTriggered.
void NotificationActionTriggered(object sender, PushDemoAction e) => ShowActionAlert(e); void ShowActionAlert(PushDemoAction action) => MainThread.BeginInvokeOnMainThread(() => MainPage?.DisplayAlert("PushDemo", $"{action} action received", "OK") .ContinueWith((task) => { if (task.IsFaulted) throw task.Exception; }));Nel costruttore App risolvere l'implementazione di IPushNotificationActionService usando ServiceContainer e sottoscrivere l'evento IPushDemoNotificationActionServiceActionTriggered.
public App() { InitializeComponent(); ServiceContainer.Resolve<IPushDemoNotificationActionService>() .ActionTriggered += NotificationActionTriggered; MainPage = new MainPage(); }Nota
Serve semplicemente a dimostrare la ricezione e la propagazione delle azioni di notifica push. In genere queste azioni vengono gestite in modo automatico, ad esempio passando a una visualizzazione specifica o aggiornando alcuni dati anziché visualizzare un avviso tramite la classe Page radice, MainPage in questo caso.
Configurare il progetto Android nativo per le notifiche push
Convalidare il nome del pacchetto e le autorizzazioni
In PushDemo.Android aprire Opzioni progetto e quindi Applicazione Android nella sezione Compila.
Verificare che il nome del pacchetto corrisponda al valore usato nel progetto FirebaseConsolePushDemo. Il formato di Nome del pacchetto era
com.<organization>.pushdemo.Impostare Versione minima di Android su Android 8.0 (livello API 26) e Versione di destinazione di Android sul livello API più recente.
Nota
Ai fini di questa esercitazione sono supportati solo i dispositivi che eseguono il livello API 26 e successivo, tuttavia è possibile estendere il supporto ai dispositivi che eseguono versioni precedenti.
Verificare che le autorizzazioni INTERNET e READ_PHONE_STATE siano abilitate in Autorizzazioni necessarie.
Fare clic su OK.
Aggiungere i pacchetti Xamarin.GooglePlayServices.Base e Xamarin.Firebase.Messaging
In PushDemo.Android premere CTRL + clic sulla cartella Packages e quindi scegliere Gestisci pacchetti NuGet.
Cercare Xamarin.GooglePlayServices.Base (non Basement) e verificare che sia selezionato.
Cercare Xamarin.Firebase.Messaging e verificare che sia selezionato.
Fare clic su Aggiungi pacchetti, quindi fare clic su Accetta quando viene chiesto di accettare le condizioni di licenza.
Aggiungere il file JSON di Google Services
Premere CTRL + clic sul progetto
PushDemo.Android, quindi scegliere File esistente dal menu Aggiungi.Selezionare il file google-services.json scaricato in precedenza quando è stato configurato il progetto PushDemo in Firebase Console, quindi fare clic su Apri.
Quando richiesto, scegliere Copia il file nella directory.
Premere CTRL + clic sul file google-services.json nel progetto
PushDemo.Android, quindi verificare che GoogleServicesJson sia impostato come Azione di compilazione.
Gestire le notifiche push per Android
PremereCTRL + clic sul progetto
PushDemo.Android, scegliere Nuova cartella dal menu Aggiungi, quindi fare clic su Aggiungi e specificare Services in Nome cartella.Premere CTRL + clic sulla cartella Services, quindi scegliere Nuovo file dal menu Aggiungi.
Selezionare Generale>Classe vuota, immettere DeviceInstallationService.cs in Nome, quindi fare clic su Nuovo e aggiungere l'implementazione seguente.
using System; using Android.App; using Android.Gms.Common; using PushDemo.Models; using PushDemo.Services; using static Android.Provider.Settings; namespace PushDemo.Droid.Services { public class DeviceInstallationService : IDeviceInstallationService { public string Token { get; set; } public bool NotificationsSupported => GoogleApiAvailability.Instance .IsGooglePlayServicesAvailable(Application.Context) == ConnectionResult.Success; public string GetDeviceId() => Secure.GetString(Application.Context.ContentResolver, Secure.AndroidId); public DeviceInstallation GetDeviceInstallation(params string[] tags) { if (!NotificationsSupported) throw new Exception(GetPlayServicesError()); if (string.IsNullOrWhiteSpace(Token)) throw new Exception("Unable to resolve token for FCM"); var installation = new DeviceInstallation { InstallationId = GetDeviceId(), Platform = "fcm", PushChannel = Token }; installation.Tags.AddRange(tags); return installation; } string GetPlayServicesError() { int resultCode = GoogleApiAvailability.Instance.IsGooglePlayServicesAvailable(Application.Context); if (resultCode != ConnectionResult.Success) return GoogleApiAvailability.Instance.IsUserResolvableError(resultCode) ? GoogleApiAvailability.Instance.GetErrorString(resultCode) : "This device is not supported"; return "An error occurred preventing the use of push notifications"; } } }Nota
Questa classe fornisce un ID univoco (con Secure.AndroidId) come parte del payload di registrazione dell'hub di notifica.
Aggiungere alla cartella Services un'altra classe vuota denominata PushNotificationFirebaseMessagingService.cs, quindi aggiungere l'implementazione seguente.
using Android.App; using Android.Content; using Firebase.Messaging; using PushDemo.Services; namespace PushDemo.Droid.Services { [Service] [IntentFilter(new[] { "com.google.firebase.MESSAGING_EVENT" })] public class PushNotificationFirebaseMessagingService : FirebaseMessagingService { IPushDemoNotificationActionService _notificationActionService; INotificationRegistrationService _notificationRegistrationService; IDeviceInstallationService _deviceInstallationService; IPushDemoNotificationActionService NotificationActionService => _notificationActionService ?? (_notificationActionService = ServiceContainer.Resolve<IPushDemoNotificationActionService>()); INotificationRegistrationService NotificationRegistrationService => _notificationRegistrationService ?? (_notificationRegistrationService = ServiceContainer.Resolve<INotificationRegistrationService>()); IDeviceInstallationService DeviceInstallationService => _deviceInstallationService ?? (_deviceInstallationService = ServiceContainer.Resolve<IDeviceInstallationService>()); public override void OnNewToken(string token) { DeviceInstallationService.Token = token; NotificationRegistrationService.RefreshRegistrationAsync() .ContinueWith((task) => { if (task.IsFaulted) throw task.Exception; }); } public override void OnMessageReceived(RemoteMessage message) { if(message.Data.TryGetValue("action", out var messageAction)) NotificationActionService.TriggerAction(messageAction); } } }In MainActivity.cs verificare che gli spazi dei nomi seguenti siano stati aggiunti all'inizio del file.
using System; using Android.App; using Android.Content; using Android.Content.PM; using Android.OS; using Android.Runtime; using Firebase.Iid; using PushDemo.Droid.Services; using PushDemo.Services;In MainActivity.cs impostare LaunchMode su SingleTop in modo che MainActivity non venga creato di nuovo all'apertura.
[Activity( Label = "PushDemo", LaunchMode = LaunchMode.SingleTop, Icon = "@mipmap/icon", Theme = "@style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]Aggiungere proprietà private e i campi sottostanti corrispondenti per archiviare un riferimento alle implementazioni di IPushNotificationActionService e IDeviceInstallationService.
IPushDemoNotificationActionService _notificationActionService; IDeviceInstallationService _deviceInstallationService; IPushDemoNotificationActionService NotificationActionService => _notificationActionService ?? (_notificationActionService = ServiceContainer.Resolve<IPushDemoNotificationActionService>()); IDeviceInstallationService DeviceInstallationService => _deviceInstallationService ?? (_deviceInstallationService = ServiceContainer.Resolve<IDeviceInstallationService>());Implementare l'interfaccia IOnSuccessListener per recuperare e archiviare il token Firebase.
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity, Android.Gms.Tasks.IOnSuccessListener { ... public void OnSuccess(Java.Lang.Object result) => DeviceInstallationService.Token = result.Class.GetMethod("getToken").Invoke(result).ToString(); }Aggiungere un nuovo metodo denominato ProcessNotificationActions che controlla se un determinato Intent ha un valore aggiuntivo denominato action. Attivare tale azione in modo condizionale usando l'implementazione di IPushDemoNotificationActionService.
void ProcessNotificationActions(Intent intent) { try { if (intent?.HasExtra("action") == true) { var action = intent.GetStringExtra("action"); if (!string.IsNullOrEmpty(action)) NotificationActionService.TriggerAction(action); } } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex.Message); } }Eseguire l'override del metodo OnNewIntent per chiamare il metodo ProcessNotificationActions.
protected override void OnNewIntent(Intent intent) { base.OnNewIntent(intent); ProcessNotificationActions(intent); }Nota
Poiché LaunchMode per Activity è impostato su SingleTop, verrà inviato un Intent all'istanza di Activity esistente tramite il metodo OnNewIntent invece del metodo OnCreate, pertanto è necessario gestire una finalità in ingresso in entrambi i metodi OnCreate e OnNewIntent.
Aggiornare il metodo OnCreate per chiamare
Bootstrap.Beginsubito dopo la chiamata abase.OnCreatepassando l'implementazione specifica della piattaforma di IDeviceInstallationService.Bootstrap.Begin(() => new DeviceInstallationService());Nello stesso metodo, chiamare in modo condizionale GetInstanceId nell'istanza di FirebaseApp, subito dopo la chiamata a
Bootstrap.Begin, aggiungendo MainActivity come IOnSuccessListener.if (DeviceInstallationService.NotificationsSupported) { FirebaseInstanceId.GetInstance(Firebase.FirebaseApp.Instance) .GetInstanceId() .AddOnSuccessListener(this); }Sempre in OnCreate, chiamare ProcessNotificationActions subito dopo la chiamata a
LoadApplicationpassando l'elemento Intent corrente.... LoadApplication(new App()); ProcessNotificationActions(Intent);
Nota
È necessario ripetere la registrazione dell'app ogni volta che la si esegue e arrestarla da una sessione di debug per continuare a ricevere le notifiche push.
Configurare il progetto iOS nativo per le notifiche push
Configurare Info.plist e Entitlements.plist
Assicurarsi di avere eseguito l'accesso con il proprio account Apple Developer in Visual Studio>Preferenze>Pubblicazione>Account per sviluppatore Apple e che siano stati scaricati il certificato e il profilo di provisioning appropriati. Questi asset dovrebbero essere stati creati nei passaggi precedenti.
In PushDemo.iOS aprire Info.plist e assicurarsi che bundleIdentifier corrisponda al valore usato per il rispettivo profilo di provisioning nel portale per sviluppatori Apple. BundleIdentifier era in formato
com.<organization>.PushDemo.Nello stesso file impostare Versione minima del sistema su 13.0.
Nota
Ai fini di questa esercitazione sono supportati solo i dispositivi che eseguono iOS 13.0 e versioni successive, tuttavia è possibile estendere il supporto ai dispositivi che eseguono versioni precedenti.
Aprire Opzioni progetto per PushDemo.iOS (fare doppio clic sul progetto).
In Project Opzioni, in Build iOS Bundle Signing (Crea > firma bundle iOS) assicurarsi che l'account sviluppatore sia selezionato in Team. Verificare quindi che sia selezionato "Automatically manage signing" (Gestisci firma automaticamente) e che siano automaticamente selezionati il certificato di firma e il profilo di provisioning.
Nota
Se il certificato di firma e il profilo di provisioning non sono stati selezionati automaticamente, scegliere Provisioning manuale, quindi fare clic su Bundle Signing Options (Opzioni di firma del bundle). Verificare che il proprio Team sia selezionato per Identità di firma e che il profilo di provisioning specifico di PushDemo sia selezionato per Profilo di provisioning per entrambe le configurazioni Debug e Rilascio, controllando anche che sia selezionato iPhone per Piattaforma in entrambi i casi.
In PushDemo.iOS aprire Entitlements.plist e assicurarsi che l'opzione Abilita Notifiche push sia selezionata quando viene visualizzata nella scheda Entitlement. Verificare quindi che Ambiente APS sia impostato su Sviluppo quando viene visualizzato nella scheda Origine.
Gestire le notifiche push per iOS
PremereCTRL + clic sul progetto PushDemo.iOS, scegliere Nuova cartella dal menu Aggiungi, quindi fare clic su Aggiungi e specificare Services in Nome cartella.
Premere CTRL + clic sulla cartella Services, quindi scegliere Nuovo file dal menu Aggiungi.
Selezionare Generale>Classe vuota, immettere DeviceInstallationService.cs in Nome, quindi fare clic su Nuovo e aggiungere l'implementazione seguente.
using System; using PushDemo.Models; using PushDemo.Services; using UIKit; namespace PushDemo.iOS.Services { public class DeviceInstallationService : IDeviceInstallationService { const int SupportedVersionMajor = 13; const int SupportedVersionMinor = 0; public string Token { get; set; } public bool NotificationsSupported => UIDevice.CurrentDevice.CheckSystemVersion(SupportedVersionMajor, SupportedVersionMinor); public string GetDeviceId() => UIDevice.CurrentDevice.IdentifierForVendor.ToString(); public DeviceInstallation GetDeviceInstallation(params string[] tags) { if (!NotificationsSupported) throw new Exception(GetNotificationsSupportError()); if (string.IsNullOrWhiteSpace(Token)) throw new Exception("Unable to resolve token for APNS"); var installation = new DeviceInstallation { InstallationId = GetDeviceId(), Platform = "apns", PushChannel = Token }; installation.Tags.AddRange(tags); return installation; } string GetNotificationsSupportError() { if (!NotificationsSupported) return $"This app only supports notifications on iOS {SupportedVersionMajor}.{SupportedVersionMinor} and above. You are running {UIDevice.CurrentDevice.SystemVersion}."; if (Token == null) return $"This app can support notifications but you must enable this in your settings."; return "An error occurred preventing the use of push notifications"; } } }Nota
Questa classe fornisce un ID univoco (con il valore UIDevice.IdentifierForVendor) e il payload di registrazione dell'hub di notifica.
Aggiungere al progetto PushDemo.iOS una nuova cartella denominata Extensions, quindi aggiungere alla cartella una classe vuota denominata NSDataExtensions.cs con l'implementazione seguente.
using System.Text; using Foundation; namespace PushDemo.iOS.Extensions { internal static class NSDataExtensions { internal static string ToHexString(this NSData data) { var bytes = data.ToArray(); if (bytes == null) return null; StringBuilder sb = new StringBuilder(bytes.Length * 2); foreach (byte b in bytes) sb.AppendFormat("{0:x2}", b); return sb.ToString().ToUpperInvariant(); } } }In AppDelegate.cs verificare che gli spazi dei nomi seguenti siano stati aggiunti all'inizio del file.
using System; using System.Diagnostics; using System.Threading.Tasks; using Foundation; using PushDemo.iOS.Extensions; using PushDemo.iOS.Services; using PushDemo.Services; using UIKit; using UserNotifications; using Xamarin.Essentials;Aggiungere proprietà private e i rispettivi campi sottostanti per archiviare un riferimento alle implementazioni IPushDemoNotificationActionService, INotificationRegistrationService e IDeviceInstallationService.
IPushDemoNotificationActionService _notificationActionService; INotificationRegistrationService _notificationRegistrationService; IDeviceInstallationService _deviceInstallationService; IPushDemoNotificationActionService NotificationActionService => _notificationActionService ?? (_notificationActionService = ServiceContainer.Resolve<IPushDemoNotificationActionService>()); INotificationRegistrationService NotificationRegistrationService => _notificationRegistrationService ?? (_notificationRegistrationService = ServiceContainer.Resolve<INotificationRegistrationService>()); IDeviceInstallationService DeviceInstallationService => _deviceInstallationService ?? (_deviceInstallationService = ServiceContainer.Resolve<IDeviceInstallationService>());Aggiungere il metodo RegisterForRemoteNotifications per registrare le impostazioni delle notifiche utente e quindi per le notifiche remote con APNS.
void RegisterForRemoteNotifications() { MainThread.BeginInvokeOnMainThread(() => { var pushSettings = UIUserNotificationSettings.GetSettingsForTypes( UIUserNotificationType.Alert | UIUserNotificationType.Badge | UIUserNotificationType.Sound, new NSSet()); UIApplication.SharedApplication.RegisterUserNotificationSettings(pushSettings); UIApplication.SharedApplication.RegisterForRemoteNotifications(); }); }Aggiungere il metodo CompleteRegistrationAsync per impostare il valore della proprietà
IDeviceInstallationService.Token. Aggiornare la registrazione e memorizzare nella cache il token del dispositivo, se è stato aggiornato dall'ultima archiviazione.Task CompleteRegistrationAsync(NSData deviceToken) { DeviceInstallationService.Token = deviceToken.ToHexString(); return NotificationRegistrationService.RefreshRegistrationAsync(); }Aggiungere il metodo ProcessNotificationActions per elaborare i dati di notifica NSDictionary e chiamare in modo condizionale NotificationActionService.TriggerAction.
void ProcessNotificationActions(NSDictionary userInfo) { if (userInfo == null) return; try { var actionValue = userInfo.ObjectForKey(new NSString("action")) as NSString; if (!string.IsNullOrWhiteSpace(actionValue?.Description)) NotificationActionService.TriggerAction(actionValue.Description); } catch (Exception ex) { Debug.WriteLine(ex.Message); } }Eseguire l'override del metodo RegisteredForRemoteNotifications passando l'argomento deviceToken al metodo CompleteRegistrationAsync.
public override void RegisteredForRemoteNotifications( UIApplication application, NSData deviceToken) => CompleteRegistrationAsync(deviceToken).ContinueWith((task) => { if (task.IsFaulted) throw task.Exception; });Eseguire l'override del metodo ReceivedRemoteNotification passando l'argomento userInfo al metodo ProcessNotificationActions.
public override void ReceivedRemoteNotification( UIApplication application, NSDictionary userInfo) => ProcessNotificationActions(userInfo);Eseguire l'override del metodo FailedToRegisterForRemoteNotifications per registrare l'errore.
public override void FailedToRegisterForRemoteNotifications( UIApplication application, NSError error) => Debug.WriteLine(error.Description);Nota
In questo caso è un segnaposto. È opportuno implementare la registrazione e la gestione degli errori appropriate per gli scenari di produzione.
Aggiornare il metodo FinishedLaunching per chiamare
Bootstrap.Beginsubito dopo la chiamata aForms.Initpassando l'implementazione specifica della piattaforma di IDeviceInstallationService.Bootstrap.Begin(() => new DeviceInstallationService());Nello stesso metodo, richiedere l'autorizzazione in modo condizionale e registrarsi per le notifiche remote immediatamente dopo
Bootstrap.Begin.if (DeviceInstallationService.NotificationsSupported) { UNUserNotificationCenter.Current.RequestAuthorization( UNAuthorizationOptions.Alert | UNAuthorizationOptions.Badge | UNAuthorizationOptions.Sound, (approvalGranted, error) => { if (approvalGranted && error == null) RegisterForRemoteNotifications(); }); }Sempre in FinishedLaunching, chiamare ProcessNotificationActions subito dopo la chiamata a
LoadApplicationse l'argomento options contiene UIApplication.LaunchOptionsRemoteNotificationKey passando l'oggetto userInfo risultante.using (var userInfo = options?.ObjectForKey( UIApplication.LaunchOptionsRemoteNotificationKey) as NSDictionary) ProcessNotificationActions(userInfo);
Testare la soluzione
A questo punto è possibile testare l'invio di notifiche tramite il servizio back-end.
Invio di una notifica di prova
Aprire una nuova scheda in Postman.
Impostare la richiesta su POST e immettere l'indirizzo seguente:
https://<app_name>.azurewebsites.net/api/notifications/requestsSe si è scelto di completare la sezione Autenticare i client usando una chiave API, assicurarsi di configurare le intestazioni della richiesta in modo da includere il valore di apikey.
Chiave Valore apikey <your_api_key> Scegliere l'opzione raw (non elaborato) per il corpo, quindi scegliere JSON dall'elenco delle opzioni di formato e includere del contenuto JSON segnaposto:
{ "text": "Message from Postman!", "action": "action_a" }Selezionare il pulsante Code (Codice), che si trova sotto il pulsante Save (Salva) nella parte superiore destra della finestra. La richiesta dovrebbe avere un aspetto simile all'esempio seguente quando viene visualizzata per HTML (a seconda che sia stata inclusa un'intestazione apikey):
POST /api/notifications/requests HTTP/1.1 Host: https://<app_name>.azurewebsites.net apikey: <your_api_key> Content-Type: application/json { "text": "Message from backend service", "action": "action_a" }Eseguire l'applicazione PushDemo in una o entrambe le piattaforme di destinazione (Android e iOS).
Nota
Se si esegue il test in Android, assicurarsi di non essere in modalità Debug o, se l'app è stata distribuita eseguendo l'applicazione, forzare la chiusura dell'app e avviarla di nuovo dall'utilità di avvio.
Nell'app PushDemo toccare il pulsante Register (Registra).
Tornare in Postman , chiudere la finestra Generate Code Snippets (Genera frammenti di codice), se non lo si è già fatto, quindi fare clic sul pulsante Send (Invia).
Verificare di ricevere una risposta 200 OK in Postman e che nell'app venga visualizzato l'avviso ActionA action received (Azione ActionA ricevuta).
Chiudere l'app PushDemo, quindi fare di nuovo clic sul pulsante Send (Invia) in Postman .
Verificare di ricevere di nuovo una risposta 200 OK in Postman . Verificare che nell'area di notifica venga visualizzata una notifica per l'app PushDemo con il messaggio corretto.
Toccare la notifica per verificare che apra l'app con l'avviso ActionA action received (Azione ActionA ricevuta) visualizzato.
Tornare in Postman , modificare il corpo della richiesta precedente per inviare una notifica automatica che specifica action_b anziché action_a per il valore action.
{ "action": "action_b", "silent": true }Con l'app ancora aperta, fare clic sul pulsante Send (Invia) in Postman .
Verificare di ricevere una risposta 200 OK in Postman e che nell'app venga visualizzato l'avviso ActionB action received (Azione ActionB ricevuta) invece di ActionA action received (Azione ActionA ricevuta).
Chiudere l'app PushDemo, quindi fare di nuovo clic sul pulsante Send (Invia) in Postman .
Verificare di ricevere una risposta 200 OK in Postman e che la notifica automatica non venga visualizzata nell'area di notifica.
Risoluzione dei problemi
Nessuna risposta dal servizio back-end
Durante i test in locale assicurarsi che il servizio back-end sia in esecuzione e che usi la porta corretta.
Se si eseguono i test con l'app per le API di Azure, controllare che il servizio sia in esecuzione, che sia stato distribuito e che sia stato avviato senza errori.
Assicurarsi di aver specificato correttamente l'indirizzo di base in Postman o nella configurazione dell'app per dispositivi mobili durante i test tramite il client. L'indirizzo di base deve essere indicativamente https://<api_name>.azurewebsites.net/ o https://localhost:5001/ quando si eseguono test in locale.
Mancata ricezione delle notifiche in Android dopo l'avvio o l'arresto di una sessione di debug
Assicurarsi di eseguire di nuovo la registrazione dopo l'avvio o l'arresto di una sessione di debug. Il debugger causerà la generazione di un nuovo token Firebase. È necessario aggiornare anche l'installazione dell'hub di notifica.
Ricezione di un codice di stato 401 dal servizio back-end
Verificare che si stia impostando l'intestazione della richiesta apikey e che questo valore corrisponda a quello configurato per il servizio back-end.
Se questo errore viene visualizzato durante i test in locale, assicurarsi che il valore della chiave definito nella configurazione del client corrisponda al valore dell'impostazione utente Authentication:ApiKey usato dall'API.
Se si eseguono test con un'app per le API, assicurarsi che il valore della chiave nel file di configurazione client corrisponda all'impostazione dell'applicazione Authentication:ApiKey usata nell'app per le API.
Nota
Se questa impostazione è stata creata o modificata dopo la distribuzione del servizio back-end, è necessario riavviare il servizio per renderla effettiva.
Se si è scelto di non completare la sezione Autenticare i client usando una chiave API, assicurarsi di non aver applicato l'attributo Authorize alla classe NotificationsController.
Ricezione di un codice di stato 404 dal servizio back-end
Verificare che il metodo dell'endpoint e della richiesta HTTP sia corretto. Gli endpoint, ad esempio, devono essere indicativamente:
- [PUT]
https://<api_name>.azurewebsites.net/api/notifications/installations - [DELETE]
https://<api_name>.azurewebsites.net/api/notifications/installations/<installation_id> - [POST]
https://<api_name>.azurewebsites.net/api/notifications/requests
Oppure durante i test in locale:
- [PUT]
https://localhost:5001/api/notifications/installations - [DELETE]
https://localhost:5001/api/notifications/installations/<installation_id> - [POST]
https://localhost:5001/api/notifications/requests
Quando si specifica l'indirizzo di base nell'app client, assicurarsi che termini con /. L'indirizzo di base deve essere indicativamente https://<api_name>.azurewebsites.net/ o https://localhost:5001/ quando si eseguono test in locale.
Non è possibile eseguire la registrazione e viene visualizzato un messaggio di errore dell'hub di notifica
Verificare che il dispositivo di test abbia connettività di rete. Determinare quindi il codice di stato della risposta HTTP impostando un punto di interruzione per esaminare il valore della proprietà StatusCode in HttpResponse.
Esaminare i suggerimenti precedenti per la risoluzione dei problemi, se applicabili in base al codice di stato.
Impostare un punto di interruzione sulle righe che restituiscono questi codici di stato specifici per la rispettiva API. Provare quindi a chiamare il servizio back-end durante il debug in locale.
Verificare che il servizio back-end funzioni come previsto tramite Postman usando il payload appropriato. Usare il payload effettivo creato dal codice client per la piattaforma in questione.
Esaminare le sezioni di configurazione specifiche della piattaforma per assicurarsi di aver eseguito tutti i passaggi. Verificare le variabili installation id e token vengano sostituite dai valori corretti per la piattaforma.
Visualizzazione del messaggio di errore che informa che non è possibile risolvere un ID per il dispositivo
Esaminare le sezioni di configurazione specifiche della piattaforma per assicurarsi di aver eseguito tutti i passaggi.
Collegamenti correlati
- Panoramica di Hub di notifica di Azure
- Installazione di Visual Studio per Mac
- Installazione di Xamarin in Windows
- SDK di Hub di notifica per le operazioni back-end
- SDK di Hub di notifica su GitHub
- Registrazione con il back-end dell'applicazione
- Gestione delle registrazioni
- Uso dei tag
- Uso di modelli personalizzati
Passaggi successivi
Si dispone ora di un'app Xamarin.Forms di base connessa a un hub di notifica tramite un servizio back-end e in grado di inviare e ricevere notifiche.
Sarà probabilmente necessario adattare l'esempio usato in questa esercitazione in base alla propria situazione. È consigliabile anche implementare funzionalità più efficaci di gestione degli errori, logica di ripetizione dei tentativi e registrazione.
Visual Studio App Center può essere rapidamente incorporato in app per dispositivi mobili a cui fornisce funzionalità di analisi e diagnostica per facilitare la risoluzione dei problemi.
Scaricare l'esempio