Esercitazione: Inviare notifiche push alle app Xamarin.Forms usando Hub di notifica di Azure tramite un servizio back-end

Scaricare l'esempio Scaricare l'esempio

In questa esercitazione si usano Hub di notifica di Azure per eseguire il push delle notifiche a un'applicazione Xamarin.Forms destinata ad Android e iOS.

Un back-end dell'API Web ASP.NET Core viene usato per gestire la registrazione del dispositivo per il client usando l'approccio di installazione più recente e ottimale. Il servizio invierà anche notifiche push in modo multipiattaforma.

Queste operazioni vengono gestite usando Hub di notifica SDK per le operazioni back-end. Altri dettagli sull'approccio generale sono disponibili nella documentazione relativa alla registrazione dal back-end dell'app .

Questa esercitazione illustra i passaggi seguenti:

Prerequisiti

Per seguire questa procedura, è necessario:

  • Una sottoscrizione di Azure in cui è possibile creare e gestire le risorse.
  • Un Mac con Visual Studio per Mac installato o un PC che esegue Visual Studio 2019.
  • Gli utenti di Visual Studio 2019 devono avere anche carichi di lavoro sviluppo per dispositivi mobili con .NET e ASP.NET e sviluppo Web.
  • Possibilità di eseguire l'app in Android (dispositivi fisici o emulatori) o iOS (solo dispositivi fisici).

Per Android, è necessario disporre di:

  • Uno sviluppatore ha sbloccato un dispositivo fisico o un emulatore (che esegue l'API 26 e versioni successive con Google Play Services installato).

Per iOS, è necessario disporre di:

Nota

Il simulatore iOS non supporta le notifiche remote e pertanto è necessario un dispositivo fisico durante l'esplorazione di questo esempio in iOS. Tuttavia, non è necessario eseguire l'app sia in Android che in iOS per completare questa esercitazione.

È possibile seguire i passaggi descritti in questo esempio di primo principio senza esperienza precedente. Tuttavia, si trarrà vantaggio dalla familiarità con gli aspetti seguenti.

Importante

I passaggi forniti sono specifici per Visual Studio per Mac. È possibile seguire l'uso di Visual Studio 2019 , ma potrebbero esserci alcune differenze da riconciliare. Ad esempio, le descrizioni dell'interfaccia utente e dei flussi di lavoro, i nomi dei modelli, la configurazione dell'ambiente e così via.

Configurare Servizi di notifica push e Hub di notifica di Azure

In questa sezione viene configurato Firebase Cloud Messaging (FCM) e Apple Push Notification Services (APNS). È quindi possibile creare e configurare un hub di notifica per l'uso con tali servizi.

Creare un progetto Firebase e abilitare Firebase Cloud Messaging per Android

  1. Accedere alla console firebase. Creare un nuovo progetto Firebase immettendo PushDemo come nome progetto.

    Nota

    Verrà generato automaticamente un nome univoco. Per impostazione predefinita, si tratta di una variante minuscola del nome specificato più un numero generato separato da un trattino. È possibile modificare questa impostazione se si vuole che sia ancora univoca a livello globale.

  2. Dopo aver creato il progetto, selezionare Aggiungi Firebase all'app Android.

    Aggiungere Firebase all'app Android

  3. Nella pagina Aggiungi Firebase all'app Android seguire questa procedura.

    1. Per il nome del pacchetto Android immettere un nome per il pacchetto. Ad esempio: com.<organization_identifier>.<package_name>.

      Specificare il nome del pacchetto

    2. Selezionare Registra app.

    3. Selezionare Scarica google-services.json. Salvare quindi il file in una cartella locale per usarlo in un secondo momento e selezionare Avanti.

      Scaricare google-services.json

    4. Selezionare Avanti.

    5. Selezionare Continua nella console

      Nota

      Se il pulsante Continua alla console non è abilitato, a causa del controllo di installazione della verifica , scegliere Ignora questo passaggio.

  4. Nella console firebase selezionare l'ingranaggio per il progetto. Selezionare quindi Impostazioni progetto.

    Selezionare Impostazioni progetto

    Nota

    Se il file di google-services.json non è stato scaricato, è possibile scaricarlo in questa pagina.

  5. Passare alla scheda Messaggistica cloud nella parte superiore. Copiare e salvare la chiave server per usarla in un secondo momento. Questo valore viene usato per configurare l'hub di notifica.

    Copiare la chiave del server

Registrare l'app iOS per le notifiche push

Per inviare notifiche push a un'app iOS, registrare l'applicazione con Apple e registrare anche le notifiche push.

  1. Se l'app non è già stata registrata, passare al portale di provisioning iOS nel Centro per sviluppatori Apple. Accedere al portale con l'ID Apple, passare a Certificati, Identificatori & Profili e quindi selezionare Identificatori. Fare clic + per registrare una nuova app.

    Pagina ID app portale di provisioning iOS

  2. Nella schermata Registra un nuovo identificatore selezionare il pulsante di opzione ID app . Selezionare quindi Continua.

    Pagina Del portale di provisioning iOS per registrare un nuovo ID

  3. Aggiornare i tre valori seguenti per la nuova app e quindi selezionare 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 mobcat valore viene usato come identificatore dell'organizzazione e il valore PushDemo viene usato come nome del prodotto.

      Pagina dell'ID app di registrazione del portale di provisioning iOS

    • Notifiche push: selezionare l'opzione Notifiche push nella sezione Funzionalità .

      Modulo per registrare un nuovo ID app

      Questa azione genera l'ID app e le richieste che confermano le informazioni. Selezionare Continua e quindi Registra per confermare il nuovo ID app.

      Confermare il nuovo ID app

      Dopo aver selezionato Registra, il nuovo ID app viene visualizzato come voce nella pagina Certificati, Identificatori & Profili .

  4. Nella pagina Certificati, Identificatori & profili , in Identificatori individuare l'elemento della riga ID app creato. Selezionare quindi la riga per visualizzare la schermata Modifica configurazione ID app .

Creazione di un certificato per Hub di notifica

È necessario un certificato per consentire all'hub di notifica di usare Apple Push Notification Services (APNS) e può essere fornito in uno dei due modi seguenti:

  1. Creazione di un certificato push p12 che può essere caricato direttamente nell'hub di notifica (approccio originale)

  2. Creazione di un certificato p8 che può essere usato per l'autenticazione basata su token (approccio più recente e consigliato)

L'approccio più recente offre numerosi vantaggi, come documentato in Autenticazione basata su token (HTTP/2) per APNS. Sono necessari meno passaggi, ma viene richiesto anche per scenari specifici. Tuttavia, sono stati forniti passaggi per entrambi gli approcci, poiché entrambi funzioneranno ai fini di questa esercitazione.

OPZIONE 1: Creazione di un certificato push p12 che può essere caricato direttamente nell'hub di notifica
  1. Nel Mac eseguire lo strumento Accesso portachiavi. Può essere aperto dalla cartella Utilities o dalla cartella Other nella finestra di avvio.

  2. Selezionare Accesso portachiavi, espandere Assistente certificati e quindi selezionare Richiedi un certificato da un'autorità di certificazione.

    Usare l'accesso keychain per richiedere un nuovo certificato

    Nota

    Per impostazione predefinita, Accesso portachiavi seleziona il primo elemento nell'elenco. Questo può essere un problema se si è nella categoria Certificati e Apple Worldwide Developer Relations Certification Authority non è il primo elemento nell'elenco. Assicurarsi di avere un elemento non chiave o che sia selezionata la chiave apple Worldwide Developer Relations Certification Authority prima di generare la richiesta di firma del certificato (richiesta di firma del certificato).

  3. Selezionare l'indirizzo di Email utente, immettere il valore nome comune, assicurarsi di specificare Salvato su disco e quindi selezionare Continua. Lasciare vuoto l'Email indirizzo ca perché non è obbligatorio.

    Informazioni sul certificato previste

  4. Immettere un nome per il file csr (Certificate Signing Request) in Save As (Salva con nome), selezionare il percorso in Dove e quindi selezionare Salva.

    Scegliere un nome file per il certificato

    Questa azione salva il file CSR nel percorso selezionato. Il percorso predefinito è Desktop. Ricordare il percorso scelto per il file.

  5. 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.

    Pagina Modifica ID app

  6. Viene visualizzata la finestra Certificati TLS/SSL del servizio Notifica push Apple . Selezionare il pulsante Crea certificato nella sezione Sviluppo di certificati TLS/SSL .

    Pulsante Crea certificato per ID app

    Viene visualizzata la schermata Crea un nuovo certificato .

    Nota

    Questa esercitazione usa un certificato di sviluppo. Lo stesso processo viene usato durante la registrazione di un certificato di produzione. Assicurarsi di usare lo stesso tipo di certificato durante l'invio di notifiche.

  7. Selezionare 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.

  8. Dopo che il portale ha creato il certificato, selezionare il pulsante Scarica . Salvare il certificato e ricordare il percorso in cui viene salvato.

    Pagina di download del certificato generato

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

    Individuare il file del certificato nella cartella Download

    Nota

    Per impostazione predefinita, il certificato di sviluppo scaricato è denominato aps_development.cer.

  9. Fare doppio clic sul certificato push scaricato aps_development.cer. Questa azione installa il nuovo certificato nel Keychain, come illustrato nell'immagine seguente:

    Elenco dei certificati di accesso keychain che mostra il nuovo certificato

    Nota

    Anche se il nome nel certificato potrebbe essere diverso, il nome sarà preceduto da Apple Development iOS Push Services e avrà l'identificatore di bundle appropriato associato.

  10. In Accesso portachiavi fare + clic sul nuovo certificato push creato nella categoria Certificati . Selezionare Esporta, denominare il file, selezionare il formato p12 e quindi selezionare Salva.

    Esportare il certificato come formato p12

    È possibile scegliere di proteggere il certificato con una password, ma una password è facoltativa. Fare clic su OK se si vuole ignorare la creazione della password. Prendere nota del nome file e del percorso del certificato p12 esportato. Vengono usati per abilitare l'autenticazione con APN.

    Nota

    Il nome e il percorso del file p12 potrebbero essere diversi da quelli illustrati in questa esercitazione.

OPZIONE 2: Creazione di un certificato p8 che può essere usato per l'autenticazione basata su token
  1. Prendere nota dei dettagli seguenti:

    • Prefisso ID app (ID team)
    • Bundle ID
  2. Tornare a 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.

  3. Fare clic sul + pulsante (o sul pulsante Crea una chiave ) per creare una nuova chiave.

  4. Specificare un valore di Nome chiave appropriato, quindi selezionare l'opzione Apple Push Notifications Service (APNS) e quindi fare clic su Continua, seguita da Registra nella schermata successiva.

  5. Fare clic su Scarica e quindi spostare il file p8 (preceduto da AuthKey_) in una directory locale sicura, quindi fare clic su Fine.

    Nota

    Assicurarsi di mantenere il file p8 in una posizione sicura (e salvare un backup). Dopo aver scaricato la chiave, non può essere scaricato nuovamente perché la copia del server viene rimossa.

  6. In Chiavi fare clic sulla chiave creata o su una chiave esistente se si è scelto di usarla.

  7. Prendere nota del valore dell'ID chiave .

  8. Aprire il certificato p8 in un'applicazione appropriata a scelta, ad esempio Visual Studio Code. Prendere nota del valore della chiave (tra -----BEGIN PRIVATE KEY----- e -----END PRIVATE KEY-----).

    -----BEGIN PRIVATE KEY-----
    <key_value>
    -----END PRIVATE KEY-----

    Nota

    Si tratta del valore del token che verrà usato in un secondo momento per configurare Hub di notifica.

Al termine di questi passaggi, è necessario avere le informazioni seguenti per usarle più avanti in Configurare l'hub di notifica con informazioni APNS:

  • ID team (vedere il passaggio 1)
  • ID bundle (vedere il passaggio 1)
  • ID chiave (vedere il passaggio 7)
  • Valore del token (valore della chiave p8 ottenuto nel passaggio 8)

Creare un profilo di provisioning per l'app

  1. Tornare al portale di provisioning iOS, selezionare Certificati, Identificatori & Profili, selezionare Profili dal menu a sinistra e quindi selezionare + per creare un nuovo profilo. Viene visualizzata la schermata Registra un nuovo profilo di provisioning .

  2. Selezionare Sviluppo di app iOS in Sviluppo come tipo di profilo di provisioning e quindi selezionare Continua.

    Elenco dei profili di provisioning

  3. Selezionare quindi l'ID app creato dall'elenco a discesa ID app e selezionare Continua.

    Selezionare l'ID app

  4. Nella finestra Seleziona certificati selezionare il certificato di sviluppo usato per la firma del codice e selezionare Continua.

    Nota

    Questo certificato non è il certificato push creato nel passaggio precedente. Questo è il certificato di sviluppo. Se non esiste, è necessario crearlo perché si tratta di un prerequisito per questa esercitazione . I certificati per sviluppatori possono essere creati nel portale per sviluppatori Apple, tramite Xcode o in Visual Studio.

  5. Tornare alla pagina Certificati, Identificatori & Profili , selezionare Profili dal menu a sinistra e quindi selezionare + per creare un nuovo profilo. Viene visualizzata la schermata Registra un nuovo profilo di provisioning .

  6. Nella finestra Seleziona certificati selezionare il certificato di sviluppo creato. Selezionare quindi Continua.

  7. Selezionare quindi i dispositivi da usare per il test e selezionare Continua.

  8. Infine, scegliere un nome per il profilo in Nome profilo di provisioning e selezionare Genera.

    Scegliere un nome del profilo di provisioning

  9. Quando viene creato il nuovo profilo di provisioning, selezionare Scarica. Ricorda la posizione in cui viene salvata.

  10. 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 un'autenticazione basata su token. Se si vuole usare un hub di notifica già creato, è possibile passare al passaggio 5.

  1. Accedere ad Azure.

  2. Fare clic su Crea una risorsa, quindi cercare e scegliere Hub di notifica, quindi fare clic su Crea.

  3. Aggiornare i campi seguenti, quindi fare clic su Crea:

    DETTAGLI DI BASE

    Sottoscrizione: Scegliere la sottoscrizione di destinazione dall'elenco a discesa
    Gruppo di risorse: Creare un nuovo gruppo di risorse (o selezionarne uno esistente)

    DETTAGLI DELLO SPAZIO DEI NOMI

    Spazio dei nomi dell'hub di notifica: Immettere un nome univoco globale per lo spazio dei nomi dell'hub di notifica

    Nota

    Assicurarsi che per questo campo sia selezionata l'opzione Crea nuova .

    DETTAGLI DELL'HUB DI NOTIFICA

    Hub di notifica: Immettere un nome per l'hub di notifica
    Posizione: Scegliere una posizione appropriata dall'elenco a discesa
    Piano tariffario: Mantenere l'opzione Gratuita predefinita

    Nota

    A meno che non sia stato raggiunto il numero massimo di hub nel livello gratuito.

  4. Dopo il provisioning dell'hub di notifica , passare a tale risorsa.

  5. Passare al nuovo hub di notifica.

  6. Selezionare Criteri di accesso dall'elenco (in GESTISCI).

  7. Prendere nota dei valori di Nome criterio insieme ai valori corrispondenti della stringa di connessione .

Configurare l'hub di notifica con informazioni APNS

In 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

Usa produzione per la modalità applicazione solo se vuoi inviare notifiche push agli utenti che hanno acquistato l'app dallo Store.

OPZIONE 1: Uso di un certificato push con estensione p12

  1. Selezionare Certificato.

  2. Selezionare l'icona del file.

  3. Selezionare il file con estensione p12 esportato in precedenza e quindi selezionare Apri.

  4. Se necessario, specificare la password corretta.

  5. Selezionare Modalità sandbox .

  6. Selezionare Salva.

OPZIONE 2: Uso dell'autenticazione basata su token

  1. Selezionare Token.

  2. Immettere i valori seguenti acquisiti in precedenza:

    • ID chiave
    • Bundle ID
    • Team ID
    • Token
  3. Scegliere Sandbox.

  4. Selezionare Salva.

Configurare l'hub di notifica con le informazioni di FCM

  1. Selezionare Google (GCM/FCM) nella sezione Impostazioni del menu a sinistra.
  2. Immettere la chiave del server annotata da Google Firebase Console.
  3. Selezionare Salva sulla barra degli strumenti.

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 di notifiche all'app per dispositivi mobili Xamarin.Forms.

Creare un progetto Web

  1. In Visual Studio selezionare FileNew Solution (Nuova soluzione).>

  2. Selezionare App .NET Core>>ASP.NET Core>API>Avanti.

  3. Nella finestra di dialogo Configura la nuova API Web ASP.NET Core selezionare Framework di destinazione di .NET Core 3.1.

  4. Immettere PushDemoApi come Nome progetto e quindi selezionare Crea.

  5. Avviare il debug (comando + INVIO) per testare l'app modello.

    Nota

    L'app modello è configurata per l'uso di WeatherForecastController come launchUrl. Questa impostazione è impostata in Proprietà>launchSettings.json.

    Se viene visualizzato un messaggio Certificato di sviluppo non valido:

    1. Fare clic su per accettare di eseguire lo strumento "dotnet dev-certs https" per risolvere il problema. Lo strumento "dotnet dev-certs https" richiede quindi di immettere una password per il certificato e la password per il keychain.

    2. Fare clic su quando viene richiesto di installare e considerare attendibile il nuovo certificato, quindi immettere la password per il portachiavi.

  6. Espandere la cartella Controller , quindi eliminare WeatherForecastController.cs.

  7. Eliminare WeatherForecast.cs.

  8. Configurare i valori di configurazione locale usando lo strumento Secret Manager. La separazione dei segreti dalla soluzione garantisce che non finisano nel controllo del codice sorgente. Aprire 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 stringa di connessione. Sono stati annotato nella sezione Creare un hub di notifica . In caso contrario, è possibile cercarli in Azure.

    NotificationHub:Name:
    Vedere Name (Nome ) nel riepilogo Informazioni di base nella parte superiore di Panoramica.

    NotificationHub:ConnectionString:
    Vedere DefaultFullSharedAccessSignature nei criteri di accesso

    Nota

    Per gli scenari di produzione, è possibile esaminare le opzioni, ad esempio Azure KeyVault, per archiviare in modo sicuro le stringa di connessione. 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 token, ma sono sufficienti ai fini di questa esercitazione. Una chiave API può essere configurata facilmente tramite il middleware ASP.NET.

  1. Aggiungere la chiave API ai valori di configurazione locale.

    dotnet user-secrets set "Authentication:ApiKey" <value>
    

    Nota

    È necessario sostituire il valore segnaposto con il proprio e prendere nota di esso.

  2. Controllo + Fare clic sul progetto PushDemoApi , scegliere Nuova cartella dal menu Aggiungi , quindi fare clic su Aggiungi usando l'autenticazione come nome cartella.

  3. Controllo + Fare clic sulla cartella Autenticazione , quindi scegliere Nuovo file dal menu Aggiungi .

  4. Selezionare Generale>Classe vuota, immettere ApiKeyAuthOptions.cs per Nome, quindi fare clic su Nuovo aggiungendo 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; }
        }
    }
    
  5. Aggiungere un'altra classe vuota alla cartella Authentication 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.

  6. Aggiungere un'altra classe vuota alla cartella Authentication 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 middleware in Startup.cs rendendolo più leggibile e in genere più semplice da seguire.

  7. In Startup.cs aggiornare il metodo ConfigureServices per configurare l'autenticazione della chiave API sotto la chiamata ai servizi. Metodo 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);
    }
    
  8. Ancora in Startup.cs, aggiornare il metodo Configure per chiamare i metodi di estensione UseAuthentication e UseAuthorization nel IApplicationBuilder dell'app. Assicurarsi che questi metodi vengano chiamati dopo UseRouting e prima dell'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

    La chiamata a UseAuthentication registra il middleware che usa gli schemi di autenticazione registrati in precedenza (da ConfigureServices). Questa operazione deve essere chiamata prima di qualsiasi middleware che dipende dall'autenticazione degli utenti.

Aggiungere dipendenze e configurare i servizi

ASP.NET Core supporta il modello di progettazione software di inserimento delle dipendenze, che è una tecnica per ottenere l'inversione del controllo (IoC) 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 viene incapsulato all'interno di un servizio. Il servizio viene registrato e reso disponibile tramite un'astrazione appropriata.

  1. Controllo + Fare clic sulla cartella Dipendenze , quindi scegliere Gestisci pacchetti NuGet....

  2. Cercare Microsoft.Azure.NotificationHubs e assicurarsi che sia controllato.

  3. Fare clic su Aggiungi pacchetti e quindi su Accetta quando viene richiesto di accettare le condizioni di licenza.

  4. Controllo + Fare clic sul progetto PushDemoApi , scegliere Nuova cartella dal menu Aggiungi e quindi fare clic su Aggiungimodelli comenome cartella.

  5. Controllo + Fare clic sulla cartella Modelli , quindi scegliere Nuovo file... dal menu Aggiungi .

  6. SelezionareClasse vuotagenerale>, immettere PushTemplates.cs per il nome, quindi fare clic su Nuovo aggiungendo 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 tokenizzati per le notifiche generiche e invisibile all'utente richieste da questo scenario. I payload vengono definiti al di fuori dell'installazione per consentire la sperimentazione senza dover aggiornare le installazioni esistenti tramite il servizio. La gestione delle modifiche alle installazioni in questo modo è fuori ambito per questa esercitazione. Per la produzione, prendere in considerazione modelli personalizzati.

  7. Aggiungere un'altra classe vuota alla cartella Models 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>();
        }
    }
    
  8. Aggiungere un'altra classe vuota alla cartella Models 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; }
        }
    }
    
  9. Aggiungere un'altra classe vuota alla cartella Models 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; }
        }
    }
    
  10. Aggiungere una nuova cartella al progetto PushDemoApi denominato Services.

  11. Aggiungere un'interfaccia vuota alla cartella Services 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);
        }
    }
    
  12. Aggiungere una classe vuota alla cartella Services 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. È limitato a 6 per la maggior parte degli operatori, ma l'espressione contiene solo ORs (||) in questo caso. Se nella richiesta sono presenti più di 20 tag, devono essere suddivisi in più richieste. Per altre informazioni, vedere la documentazione di Routing e Espressioni tag .

  13. 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

  1. Controllo + Fare clic sulla cartella Controller e quindi scegliere Nuovo file dal menu Aggiungi .

  2. Selezionare ASP.NET Core>Classe controller APIWeb, immettere NotificationsController per il nome, quindi fare clic su Nuovo.

    Nota

    Se si segue con Visual Studio 2019, scegliere il controller API con il modello di azioni di lettura/scrittura .

  3. Aggiungere gli spazi dei nomi seguenti alla parte superiore 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;
    
  4. Aggiornare il controller modello in modo che deriva da ControllerBase e sia decorato con l'attributo ApiController .

    [ApiController]
    [Route("api/[controller]")]
    public class NotificationsController : ControllerBase
    {
        // Templated methods here
    }
    

    Nota

    La classe base Controller fornisce il supporto per le visualizzazioni, ma questo non è necessario in questo caso e quindi ControllerBase può essere usato invece. Se si segue con Visual Studio 2019, è possibile ignorare questo passaggio.

  5. Se si sceglie di completare i client autenticati usando una sezione Chiave API , è necessario decorare anche NotificationsController con l'attributo Authorize .

    [Authorize]
    
  6. Aggiornare il costruttore per accettare l'istanza registrata di INotificationService come argomento e assegnarlo a un membro readonly.

    readonly INotificationService _notificationService;
    
    public NotificationsController(INotificationService notificationService)
    {
        _notificationService = notificationService;
    }
    
  7. In launchSettings.json (all'interno della cartella Proprietà ), modificare launchUrl da weatherforecast a api/notifiche in modo che corrisponda all'URL specificato nell'attributo RegistrationsControllerRoute .

  8. Avviare il debug (Comando + INVIO) per verificare che l'app funzioni con il nuovo NotificationsController e restituisce uno stato non autorizzato 401 .

    Nota

    Visual Studio potrebbe non avviare automaticamente l'app nel browser. Si userà Postman per testare l'API da questo punto.

  9. In una nuova scheda Postman impostare la richiesta su GET. Immettere l'indirizzo seguente sostituendo l'applicazione segnapostoUrl< con l'applicazione httpsUrl> trovata in Proprietà>launchSettings.json.

    <applicationUrl>/api/notifications
    

    Nota

    L'applicazioneUrl deve essere 'https://localhost:5001' per il profilo predefinito. Se si usa IIS (impostazione predefinita in Visual Studio 2019 in Windows), è consigliabile usare l'applicazioneUrl specificata nell'elemento iisSettings . Si riceverà una risposta 404 se l'indirizzo non è corretto.

  10. Se si sceglie di completare i client autenticati usando una sezione Chiave API , assicurarsi di configurare le intestazioni della richiesta per includere il valore apikey .

    Chiave Valore
    apikey <your_api_key>
  11. Fare clic sul pulsante Invia .

    Nota

    Si dovrebbe ricevere uno stato 200 OK con un contenuto JSON .

    Se si riceve un avviso di verifica del certificato SSL, è possibile cambiare l'impostazione Postman di verifica del certificato SSL richiesta nelle impostazioni.

  12. Sostituire i metodi della classe 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

È ora possibile creare un'app per le API in Servizio app di Azure per ospitare il servizio back-end.

  1. Accedere alla portale di Azure.

  2. Fare clic su Crea una risorsa, quindi cercare e scegliere App per le API, quindi fare clic su Crea.

  3. Aggiornare i campi seguenti, quindi fare clic su Crea.

    Nome app:
    Immettere un nome univoco globale per l'app per le API

    Sottoscrizione:
    Scegliere la stessa sottoscrizione di destinazione in cui è stato creato l'hub di notifica.

    Gruppo di risorse:
    Scegliere lo stesso gruppo di risorse creato nell'hub di notifica.

    servizio app Piano/Posizione:
    Creare un nuovo piano di servizio app

    Nota

    Passare dall'opzione predefinita a un piano che include il supporto SSL . In caso contrario, è necessario eseguire i passaggi appropriati quando si usa l'app per dispositivi mobili per impedire che le richieste http vengano bloccate.

    Application Insights:
    Mantenere l'opzione suggerita (verrà creata una nuova risorsa usando tale nome) o selezionare una risorsa esistente.

  4. Dopo aver effettuato il provisioning dell'app per le API, passare a tale risorsa.

  5. Prendere nota della proprietà URL nel riepilogo Essentials nella parte superiore della 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 in precedenza, con il formato https://<app_name>.azurewebsites.net.

  6. Selezionare Configurazione dall'elenco (in Impostazioni).

  7. Per ognuna delle impostazioni seguenti, fare clic su Nuova impostazione dell'applicazione per immettere 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

    Queste sono le stesse impostazioni definite in precedenza nelle impostazioni utente. Dovresti essere in grado di copiare questi elementi. L'impostazione Authentication:ApiKey è necessaria solo se si sceglie di completare i client autenticati usando una sezione Chiave API . Per gli scenari di produzione, è possibile esaminare le opzioni, ad esempio Azure KeyVault. Questi sono stati aggiunti come impostazioni dell'applicazione per semplicità in questo caso.

  8. Dopo aver aggiunto tutte le impostazioni dell'applicazione, fare clic su Salva, quindi Continua.

Pubblicare il servizio back-end

Successivamente, si distribuisce l'app nell'app per le API per renderla accessibile da tutti i dispositivi.

Nota

La procedura seguente è specifica per Visual Studio per Mac. Se si segue con Visual Studio 2019 in Windows, il flusso di pubblicazione sarà diverso. Vedere Pubblica in Servizio app di Azure in Windows.

  1. Modificare la configurazione da Debug a Release se non è già stato fatto.

  2. Controllo + Fare clic sul progetto PushDemoApi e quindi scegliere Pubblica in Azure dal menu Pubblica .

  3. Se richiesto, seguire il flusso di autenticazione. Usare l'account usato nella sezione precedente creare l'app per le API .

  4. Selezionare l'app per le API Servizio app di Azure creata in precedenza dall'elenco come destinazione di pubblicazione e quindi fare clic su Pubblica.

Dopo aver completato la procedura guidata, pubblica l'app in Azure e quindi apre l'app. 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

  1. In Postman aprire una nuova scheda, impostare la richiesta su PUT e immettere l'indirizzo seguente. Sostituire il segnaposto con l'indirizzo di base preso nota nella sezione precedente pubblica il servizio back-end .

    https://<app_name>.azurewebsites.net/api/notifications/installations
    

    Nota

    L'indirizzo di base deve essere nel formato https://<app_name>.azurewebsites.net/

  2. Se si sceglie di completare i client autenticati usando una sezione Chiave API , assicurarsi di configurare le intestazioni della richiesta per includere il valore apikey .

    Chiave Valore
    apikey <your_api_key>
  3. Scegliere l'opzione non elaborata per Corpo, quindi scegliere JSON dall'elenco delle opzioni di formato e quindi includere un contenuto JSON segnaposto:

    {}
    
  4. Fare clic su Invia.

    Nota

    Si dovrebbe ricevere uno stato 422 UnprocessableEntity dal servizio.

  5. Eseguire nuovamente 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 i dati di richiesta validi perché richiederà informazioni specifiche della piattaforma dall'app per dispositivi mobili client.

Creare un'applicazione Xamarin.Forms multipiattaforma

In questa sezione viene creata un'applicazione per dispositivi mobili Xamarin.Forms che implementa le notifiche push in modo multipiattaforma.

Consente di registrare e annullare la registrazione da un hub di notifica tramite il servizio back-end creato.

Viene visualizzato un avviso quando viene specificata un'azione e l'app si trova in primo piano. In caso contrario, le notifiche vengono visualizzate nel centro notifiche.

Nota

In genere si eseguono le azioni di registrazione (e di registrazione) durante il punto appropriato nel ciclo di vita dell'applicazione (o nell'ambito dell'esperienza di prima esecuzione) senza input espliciti di registrazione/registrazione dell'utente. Tuttavia, questo esempio richiederà l'input esplicito dell'utente per consentire l'esplorazione e il test di questa funzionalità più facilmente.

Creare la soluzione Xamarin.Forms

  1. In Visual Studio creare una nuova soluzione Xamarin.Forms usando l'app Moduli vuoti come modello e immettendo PushDemo per il nome del progetto.

    Nota

    Nella finestra di dialogo Configura app Moduli vuoti verificare che l'identificatore dell'organizzazione corrisponda al valore usato in precedenza e che vengano controllate sia le destinazioni Android che iOS .

  2. Controllo + Fare clic sulla soluzione PushDemo , quindi scegliere Aggiorna pacchetti NuGet.

  3. Controllo + Fare clic sulla soluzione PushDemo, quindi scegliere Gestisci pacchetti NuGet...

  4. Cercare Newtonsoft.Json e assicurarsi che sia controllato.

  5. Fare clic su Aggiungi pacchetti e quindi su Accetta quando viene richiesto di accettare le condizioni di licenza.

  6. Compilare ed eseguire l'app in ogni piattaforma di destinazione (Comando + INVIO) per testare l'esecuzione dell'app modello nel dispositivo.

Implementare i componenti multipiattaforma

  1. Controllo + Fare clic sul progetto PushDemo , scegliere Nuova cartella dal menu Aggiungi , quindi fare clic su Aggiungi usando modelli come nome cartella.

  2. Controllo + Fare clic sulla cartella Modelli , quindi scegliere Nuovo file... dal menu Aggiungi .

  3. SelezionareClasse vuotagenerale>, 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>();
        }
    }
    
  4. Aggiungere un'enumerazione vuota alla cartella Models denominata PushDemoAction.cs con l'implementazione seguente.

    namespace PushDemo.Models
    {
        public enum PushDemoAction
        {
            ActionA,
            ActionB
        }
    }
    
  5. Aggiungere una nuova cartella al progetto PushDemo denominato Services e quindi aggiungere una classe vuota a tale cartella 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

    Si tratta di una versione tagliata della classe ServiceContainer dal repository XamCAT . Verrà usato come contenitore IoC (Inversion of Control) di peso leggero.

  6. Aggiungere un'interfaccia vuota alla cartella Services 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 da ogni destinazione in un secondo momento per fornire le funzionalità specifiche della piattaforma e le informazioni di Installazione del dispositivo richieste dal servizio back-end.

  7. Aggiungere un'altra interfaccia vuota alla cartella Services 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

    Questo gestirà l'interazione tra il client e il servizio back-end.

  8. Aggiungere un'altra interfaccia vuota alla cartella Services denominata INotificationActionService.cs, quindi aggiungere il codice seguente.

    namespace PushDemo.Services
    {
        public interface INotificationActionService
        {
            void TriggerAction(string action);
        }
    }
    

    Nota

    Questa operazione viene usata come meccanismo semplice per centralizzare la gestione delle azioni di notifica.

  9. Aggiungere un'interfaccia vuota alla cartella Services 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 attivata in modo fortemente tipizzato.

  10. Aggiungere una classe vuota alla cartella Services denominata NotificationRegistrationService.csimplementare 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 sceglie di completare i client autenticati usando una sezione Chiave API .

  11. Aggiungere una classe vuota alla cartella Services denominata PushDemoNotificationActionService.cs implementare 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);
            }
        }
    }
    
  12. Aggiungere una classe vuota al progetto PushDemo denominato 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

    Questo metodo viene usato come modo semplice per mantenere i segreti fuori dal controllo del codice sorgente. È possibile sostituire questi valori come parte di una compilazione automatica o eseguirne l'override usando una classe parziale locale. Questa operazione verrà eseguita nel passaggio successivo.

    Il campo ApiKey è obbligatorio solo se si sceglie di completare i client autenticati usando una sezione Chiave API .

  13. Aggiungere un'altra classe vuota al progetto PushDemo questa volta 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. Quando si è compilato il servizio back-end, è necessario prendere nota di questi elementi. L'URL dell'app per le API deve essere https://<api_app_name>.azurewebsites.net/. Ricordarsi di aggiungere *.local_secrets.* al file gitignore per evitare di eseguire il commit di questo file.

    Il campo ApiKey è obbligatorio solo se si sceglie di completare i client autenticati usando una sezione Chiave API .

  14. Aggiungere una classe vuota al progetto PushDemo denominato 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 verrà chiamato da ogni piattaforma quando l'app avvia un'implementazione specifica della piattaforma di IDeviceInstallationService.

    L'argomento del costruttore apiKeyNotificationRegistrationService è obbligatorio solo se si sceglie di completare i client autenticati usando una sezione Chiave API.

Implementare l'interfaccia utente multipiattaforma

  1. Nel progetto PushDemo aprire MainPage.xaml e sostituire il controllo StackLayout con quanto segue.

    <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>
    
  2. Ora in MainPage.xaml.cs aggiungere un campo di backup readonly per archiviare un riferimento all'implementazione INotificationRegistrationService .

    readonly INotificationRegistrationService _notificationRegistrationService;
    
  3. Nel costruttore MainPage risolvere l'implementazione INotificationRegistrationService usando ServiceContainer e assegnarla al campo di backup notificationRegistrationService .

    public MainPage()
    {
        InitializeComponent();
    
        _notificationRegistrationService =
            ServiceContainer.Resolve<INotificationRegistrationService>();
    }
    
  4. Implementare i gestori eventi per i pulsanti RegisterButton e DeregisterButtonClicked che chiamano i / metodi RegisterDeregister.

    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; }));
    
  5. In App.xaml.cs verificare che gli spazi dei nomi seguenti siano a cui si fa riferimento.

    using PushDemo.Models;
    using PushDemo.Services;
    using Xamarin.Essentials;
    using Xamarin.Forms;
    
  6. 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; }));
    
  7. 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

    Si tratta semplicemente di illustrare la ricezione e la propagazione delle azioni di notifica push. In genere, questi vengono gestiti in modo automatico, ad esempio passando a una visualizzazione specifica o aggiornando alcuni dati anziché visualizzare un avviso tramite la pagina radice, MainPage in questo caso.

Configurare il progetto Android nativo per le notifiche push

Convalidare il nome e le autorizzazioni del pacchetto

  1. In PushDemo.Android aprire le opzioni di progetto e quindi l'applicazione Android dalla sezione Compilazione .

  2. Verificare che il nome del pacchetto corrisponda al valore usato nel progetto PushDemo della console firebase. Il nome del pacchetto è stato nel formato com.<organization>.pushdemo.

  3. Impostare la versione minima di Android su Android 8.0 (livello API 26) e la versione Android di destinazione sul livello API più recente.

    Nota

    Solo i dispositivi che eseguono il livello API 26 e versioni successive sono supportati ai fini di questa esercitazione, tuttavia è possibile estenderlo per supportare i dispositivi che eseguono versioni precedenti.

  4. Verificare che le autorizzazioni INTERNET e READ_PHONE_STATE siano abilitate in Autorizzazioni necessarie.

  5. Fare clic su OK

Aggiungere i pacchetti Xamarin Google Play Services e Xamarin.Firebase.Messaging

  1. In PushDemo.Androidfare + clic sulla cartella Pacchetti e quindi scegliere Gestisci pacchetti NuGet....

  2. Cercare Xamarin.GooglePlayServices.Base (non Basement) e assicurarsi che sia controllato.

  3. Cercare Xamarin.Firebase.Messaging e assicurarsi che sia selezionato.

  4. Fare clic su Aggiungi pacchetti e quindi su Accetta quando viene richiesto di accettare le condizioni di licenza.

Aggiungere il file JSON di Servizi Google

  1. Controllo + Fare clic sul PushDemo.Android progetto, quindi scegliere File esistente dal menu Aggiungi .

  2. Scegliere il file google-services.json scaricato in precedenza quando si configura il progetto PushDemo nella console firebase e quindi fare clic su Apri.

  3. Quando richiesto, scegliere Di copiare il file nella directory.

  4. Controllo + Fare clic sul file google-services.json dall'interno del PushDemo.Android progetto, quindi assicurarsi che GoogleServicesJson sia impostato come azione di compilazione.

Gestire le notifiche push per Android

  1. Controllo + Fare clic sul PushDemo.Android progetto, scegliere Nuova cartella dal menu Aggiungi e quindi fare clic su Aggiungiservizi comenome cartella.

  2. Controllo + Fare clic sulla cartella Servizi , quindi scegliere Nuovo file... dal menu Aggiungi .

  3. SelezionareClasse vuotagenerale>, immettere DeviceInstallationService.cs per il nome, quindi fare clic su Nuovo aggiungendo 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 (usando Secure.AndroidId) come parte del payload di registrazione dell'hub di notifica.

  4. Aggiungere un'altra classe vuota alla cartella Services 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);
            }
        }
    }
    
  5. 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;
    
  6. In MainActivity.cs impostare LaunchMode su SingleTop in modo che MainActivity non venga nuovamente creato all'apertura.

    [Activity(
        Label = "PushDemo",
        LaunchMode = LaunchMode.SingleTop,
        Icon = "@mipmap/icon",
        Theme = "@style/MainTheme",
        MainLauncher = true,
        ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
    
  7. Aggiungere proprietà private e campi di backup corrispondenti per archiviare un riferimento alle implementazioni IPushNotificationActionService e IDeviceInstallationService .

    IPushDemoNotificationActionService _notificationActionService;
    IDeviceInstallationService _deviceInstallationService;
    
    IPushDemoNotificationActionService NotificationActionService
        => _notificationActionService ??
            (_notificationActionService =
            ServiceContainer.Resolve<IPushDemoNotificationActionService>());
    
    IDeviceInstallationService DeviceInstallationService
        => _deviceInstallationService ??
            (_deviceInstallationService =
            ServiceContainer.Resolve<IDeviceInstallationService>());
    
  8. 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();
    }
    
  9. Aggiungere un nuovo metodo denominato ProcessNotificationActions che verificherà se una determinata finalità ha un valore aggiuntivo denominato action. Attivare in modo condizionale tale azione 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);
        }
    }
    
  10. 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 l'attività è impostato su SingleTop, verrà inviato un intento all'istanza di Activity esistente tramite il metodo OnNewIntent anziché il metodo OnCreate e quindi è necessario gestire una finalità in ingresso nei metodi OnCreate e OnNewIntent.

  11. Aggiornare il metodo OnCreate per chiamare Bootstrap.Begin subito dopo la chiamata a base.OnCreate passare l'implementazione specifica della piattaforma di IDeviceInstallationService.

    Bootstrap.Begin(() => new DeviceInstallationService());
    
  12. 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);
    }
    
  13. Ancora in OnCreate chiamare ProcessNotificationActions immediatamente dopo la chiamata al LoadApplication passaggio della finalità corrente.

    ...
    
    LoadApplication(new App());
    
    ProcessNotificationActions(Intent);
    

Nota

È necessario registrare nuovamente l'app ogni volta che viene eseguita e arrestarla da una sessione di debug per continuare a ricevere notifiche push.

Configurare il progetto iOS nativo per le notifiche push

Configurare Info.plist e Entitlements.plist

  1. Assicurarsi di aver eseguito l'accesso all'account per sviluppatori Apple in Preferenze di Visual Studio>...>Pubblicazione>Gli account per sviluppatori Apple e il profilo di provisioning e certificato appropriati sono stati scaricati. È necessario creare questi asset come parte dei passaggi precedenti.

  2. 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 è stato nel formato com.<organization>.PushDemo.

  3. Nello stesso file impostare Versione minima del sistema su 13.0.

    Nota

    Solo i dispositivi che eseguono iOS 13.0 e versioni successive sono supportati ai fini di questa esercitazione, tuttavia è possibile estenderla per supportare i dispositivi che eseguono versioni precedenti.

  4. Aprire le opzioni di progetto per PushDemo.iOS (fare doppio clic sul progetto).

  5. In Opzioni progetto, in Compilazione > firma bundle iOS, assicurarsi che l'account sviluppatore sia selezionato in Team. Assicurarsi quindi che sia selezionata l'opzione "Gestione automatica della firma" e il certificato di firma e il profilo di provisioning vengano selezionati automaticamente.

    Nota

    Se il certificato di firma e il profilo di provisioning non sono stati selezionati automaticamente, scegliere Provisioning manuale, quindi fare clic su Opzioni di firma bundle. Assicurarsi che il team sia selezionato per l'identità di firma e che il profilo di provisioning specifico pushDemo sia selezionato per le configurazioni di debug e rilascio per garantire che iPhone sia selezionato per la piattaforma in entrambi i casi.

  6. In PushDemo.iOS aprire Entitlements.plist e assicurarsi che l'opzione Abilita notifiche push venga selezionata quando visualizzata nella scheda Diritti . Assicurarsi quindi che l'impostazione ambiente APS sia impostata sullo sviluppo quando visualizzata nella scheda Origine .

Gestire le notifiche push per iOS

  1. Controllo + Fare clic sul progetto PushDemo.iOS , scegliere Nuova cartella dal menu Aggiungi e quindi fare clic su Aggiungiservizi comenome cartella.

  2. Controllo + Fare clic sulla cartella Servizi , quindi scegliere Nuovo file... dal menu Aggiungi .

  3. SelezionareClasse vuotagenerale>, immettere DeviceInstallationService.cs per il nome, quindi fare clic su Nuovo aggiungendo 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 (usando il valore UIDevice.IdentifierForVendor ) e il payload di registrazione dell'hub di notifica.

  4. Aggiungere una nuova cartella al progetto PushDemo.iOS denominato Extensions e quindi aggiungere una classe vuota a tale cartella 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();
            }
        }
    }
    
  5. 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;
    
  6. Aggiungere proprietà private e i rispettivi campi di backup 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>());
    
  7. Aggiungere il metodo RegisterForRemoteNotifications per registrare le impostazioni di notifica 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();
        });
    }
    
  8. Aggiungere il metodo CompleteRegistrationAsync per impostare il valore della IDeviceInstallationService.Token proprietà. 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();
    }
    
  9. Aggiungere il metodo ProcessNotificationActions per l'elaborazione dei dati di notifica NSDictionary e la chiamata condizionale di 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);
        }
    }
    
  10. 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; });
    
  11. Eseguire l'override del metodo ReceivedRemoteNotification passando l'argomento userInfo al metodo ProcessNotificationActions .

    public override void ReceivedRemoteNotification(
        UIApplication application,
        NSDictionary userInfo)
        => ProcessNotificationActions(userInfo);
    
  12. Eseguire l'override del metodo FailedToRegisterForRemoteNotifications per registrare l'errore.

    public override void FailedToRegisterForRemoteNotifications(
        UIApplication application,
        NSError error)
        => Debug.WriteLine(error.Description);
    

    Nota

    Questo è molto un segnaposto. Si vuole implementare la registrazione e la gestione degli errori appropriati per gli scenari di produzione.

  13. Aggiornare il metodo FinishedLaunching per chiamare Bootstrap.Begin subito dopo la chiamata a Forms.Init passare l'implementazione specifica della piattaforma di IDeviceInstallationService.

    Bootstrap.Begin(() => new DeviceInstallationService());
    
  14. Nello stesso metodo richiedere in modo condizionale l'autorizzazione e registrare 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();
                });
    }
    
  15. Ancora in FinishedLaunching, chiamare ProcessNotificationActions immediatamente dopo la chiamata a LoadApplication se l'argomento opzioni contiene l'argomento UIApplication.LaunchOptionsRemoteNotificationKey passando nell'oggetto userInfo risultante.

    using (var userInfo = options?.ObjectForKey(
        UIApplication.LaunchOptionsRemoteNotificationKey) as NSDictionary)
            ProcessNotificationActions(userInfo);
    

Testare la soluzione

È ora possibile testare l'invio di notifiche tramite il servizio back-end.

Inviare una notifica di test

  1. Aprire una nuova scheda in Postman.

  2. Impostare la richiesta su POST e immettere l'indirizzo seguente:

    https://<app_name>.azurewebsites.net/api/notifications/requests
    
  3. Se si sceglie di completare i client autenticati usando una sezione Chiave API , assicurarsi di configurare le intestazioni della richiesta per includere il valore apikey .

    Chiave Valore
    apikey <your_api_key>
  4. Scegliere l'opzione non elaborata per Corpo, quindi scegliere JSON dall'elenco delle opzioni di formato e quindi includere un contenuto JSON segnaposto:

    {
        "text": "Message from Postman!",
        "action": "action_a"
    }
    
  5. Selezionare il pulsante Codice , che è sotto il pulsante Salva in alto a destra della finestra. La richiesta dovrebbe essere simile all'esempio seguente quando viene visualizzata per HTML (a seconda che sia 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"
    }
    
  6. 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 esecuzione in Debug o se l'app è stata distribuita eseguendo l'applicazione, forzare la chiusura dell'app e avviarla di nuovo dal launcher.

  7. Nell'app PushDemo toccare il pulsante Registra .

  8. Tornare a Postman, chiudere la finestra Genera frammenti di codice (se non è già stato fatto) e quindi fare clic sul pulsante Invia .

  9. Verificare di ottenere una risposta 200 OK in Postman e che l'avviso venga visualizzato nell'app che mostra l'azione ActionA ricevuta.

  10. Chiudere l'app PushDemo , quindi fare di nuovo clic sul pulsante Invia in Postman.

  11. Verificare di ottenere di nuovo una risposta 200 OK in Postman . Verificare che venga visualizzata una notifica nell'area di notifica per l'app PushDemo con il messaggio corretto.

  12. Toccare la notifica per confermare che si apre l'app e viene visualizzata l'azione ActionA ricevuta avviso.

  13. Tornare a Postman, modificare il corpo della richiesta precedente per inviare una notifica invisibile all'utente specificando action_b anziché action_a per il valore dell'azione .

    {
        "action": "action_b",
        "silent": true
    }
    
  14. Con l'app ancora aperta, fare clic sul pulsante Invia in Postman.

  15. Verificare di ottenere una risposta 200 OK in Postman e che l'avviso venga visualizzato nell'app che mostra l'azione ActionB ricevuta anziché l'azione ActionA ricevuta.

  16. Chiudere l'app PushDemo , quindi fare di nuovo clic sul pulsante Invia in Postman.

  17. Verificare che venga visualizzata una risposta 200 OK in Postman e che la notifica invisibile all'utente non venga visualizzata nell'area di notifica.

Risoluzione dei problemi relativi

Nessuna risposta dal servizio back-end

Quando si esegue il test in locale, assicurarsi che il servizio back-end sia in esecuzione e stia usando la porta corretta.

Se si esegue il test sull'app per le API di Azure, verificare che il servizio sia in esecuzione e sia stato distribuito e avviato senza errori.

Assicurarsi di aver specificato correttamente l'indirizzo di base in Postman o nella configurazione dell'app per dispositivi mobili durante il test tramite il client. L'indirizzo di base deve essere https://<api_name>.azurewebsites.net/ indicativamente o https://localhost:5001/ quando si esegue il test in locale.

Mancata ricezione di 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 . L'installazione dell'hub di notifica deve essere aggiornata anche.

Ricezione di un codice di stato 401 dal servizio back-end

Verificare che si stia impostando l'intestazione della richiesta apikey e questo valore corrisponda a quello configurato per il servizio back-end.

Se viene visualizzato questo errore durante il test in locale, verificare che il valore della chiave definito nella configurazione client corrisponda al valore authentication:ApiKey user-setting usato dall'API.

Se si esegue il 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 aver distribuito il servizio back-end, è necessario riavviare il servizio per renderla effettiva.

Se si sceglie di non completare la sezione Autenticare i client usando una chiave API , assicurarsi di non applicare l'attributo Authorize alla classe NotificationsController .

Ricezione di un codice di stato 404 dal servizio back-end

Verificare che l'endpoint e il metodo di richiesta HTTP siano corretti. Ad esempio, gli endpoint 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 quando si esegue il 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 https://<api_name>.azurewebsites.net/ indicativamente o https://localhost:5001/ quando si esegue il test in locale.

Impossibile registrare e viene visualizzato un messaggio di errore dell'hub di notifica

Verificare che il dispositivo di test disponga della 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 applicabile in base al codice di stato.

Impostare un punto di interruzione sulle righe che restituiscono questi codici di stato specifici per l'API corrispondente. 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 che non siano stati superati i passaggi. Verificare che i valori appropriati vengano risolti per installation id le variabili e token per la piattaforma appropriata.

Non è possibile risolvere un ID per il messaggio di errore del dispositivo

Esaminare le sezioni di configurazione specifiche della piattaforma per assicurarsi che non siano stati superati i passaggi.

Passaggi successivi

A questo momento dovrebbe essere disponibile un'app Xamarin.Forms di base connessa a un hub di notifica tramite un servizio back-end e può inviare e ricevere notifiche.

È probabile che sia necessario adattare l'esempio usato in questa esercitazione per adattarsi a uno scenario personalizzato. È anche consigliabile implementare una gestione degli errori più affidabile, logica di ripetizione dei tentativi e registrazione.

Visual Studio App Center può essere rapidamente incorporato nelle app per dispositivi mobili che forniscono analisi e diagnostica per facilitare la risoluzione dei problemi.