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

Download sample Scaricare l' di esempio

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

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

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

Questa esercitazione illustra i passaggi seguenti:

Prerequisiti

Per seguire la 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 installato anche Sviluppo per dispositivi mobili con .NET e ASP.NET e lo sviluppo Web carichi di lavoro.
  • Possibilità di eseguire l'app in Android (dispositivi fisici o emulatori) o iOS (solo dispositivi fisici).

Per Android, è necessario disporre di:

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

Per iOS, è necessario disporre di:

  • Un account per sviluppatore Apple attivo .
  • Un dispositivo iOS fisico registrato nell'account sviluppatore(che esegue iOS 13.0 e versioni successive).
  • Un certificato di sviluppo .p12installato nel keychain che consente di eseguire un'app in un dispositivo fisico.

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 in Android e 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 la procedura usando 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, questo è costituito da una variante minuscola del nome specificato più un numero generato separato da un trattino. È possibile modificare questa impostazione se si desidera 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 Registrare l'app.

    3. Selezionare Scaricare 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 nella console di non è abilitato, a causa del controllo verifica installazione , scegliere Ignora questo passaggio.

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

    Selezionare le impostazioni del 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 il chiave del server per un uso successivo. 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 & profilie quindi selezionare identificatori . Fare clic su + 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.

    portale di provisioning iOS registrare una nuova pagina 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 di app . Nello screenshot seguente il valore mobcat viene usato come identificatore dell'organizzazione e il valore PushDemo viene usato come nome del prodotto.

      pagina id app del portale di provisioning iOS

    • notifiche push: selezionare l'opzione notifiche push nella sezione Capabilities.

      modulo per registrare un nuovo ID app

      Questa azione genera l'ID app e richiede di confermare le informazioni. Selezionare Continuae quindi selezionare Registra per confermare il nuovo ID app.

      Confermare il nuovo ID app

      Dopo aver selezionato Registra, viene visualizzato il nuovo ID app 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 funzionare con 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 (l'approccio originale)

  2. Creazione di un certificato p8 che può essere usato per l'autenticazione basata su token (l'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 Di accesso Keychain. Può essere aperto dalla cartella utilità di o dalla cartella Altro nella finestra di avvio.

  2. Selezionare Keychain Access, espandere Assistente certificati e quindi selezionare Richiedere un certificato da un'autorità di certificazione.

    Usare l'accesso Keychain per richiedere un nuovo certificato

    Nota

    Per impostazione predefinita, Keychain Access seleziona il primo elemento nell'elenco. Questo può essere un problema se ci si trova 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 l''Autorità di certificazione Apple Worldwide Developer Relations chiave, prima di generare la richiesta di firma del certificato.

  3. Selezionare l'indirizzo di posta elettronica utente , immettere il valore nome comune , assicurarsi di specificare Salvato su discoe quindi selezionare Continua. Lasciare vuoto indirizzo di posta elettronica della CA perché non è obbligatorio.

    informazioni sui certificati previste

  4. Immettere un nome per il file CSR (Certificate Signing Request) in Salva con nome, selezionare il percorso in Wheree quindi selezionare Salva.

    Scegliere un nome di 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 del servizio Apple Push Notification Service TLS/SSL. Selezionare il pulsante Create Certificate (Crea certificato TLS/SSL) nella sezione Development TLS/SSL Certificate (Certificato TLS/SSL).

    pulsante Crea certificato per l'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 Scegliere 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 aver creato il certificato nel portale, selezionare il pulsante Scarica. Salvare il certificato e ricordare il percorso in cui è stato 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 a cui è associato l'identificatore del bundle appropriato.

  10. In Accesso portachiavi ControlloFare clic su sul nuovo certificato push creato nella categoria certificati . Selezionare Esporta, denominare il file, selezionare il formato p12 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 desidera 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)
    • ID bundle
  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 3 fino a 5.

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

  4. Specificare un valore nome chiave appropriato, quindi selezionare l'opzione Apple Push Notifications Service (APNS) e quindi fare clic su Continue, seguita da Register (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 un luogo sicuro (e salvare un backup). Dopo aver scaricato la chiave, non è possibile scaricarla nuovamente perché la copia del server viene rimossa.

  6. In Chiavifare clic sulla chiave creata oppure su una chiave esistente se si è scelto di usarla.

  7. Prendere nota del valore 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 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 di 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 Selezionare i 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 di Certificati, Identificatori & Profili, selezionare Profili dal menu a sinistra e quindi selezionare + per creare un nuovo profilo. Viene visualizzata schermata Registra un nuovo profilo di provisioning.

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

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

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

    Scegliere un nome di profilo di provisioning

  9. Quando viene creato il nuovo profilo di provisioning, selezionare Scarica. Ricordare 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 a 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 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

    Verificare che l'opzione Crea nuovo sia selezionata per questo campo.

    DETTAGLI DELL'HUB DI NOTIFICA

    Hub di notifica: Immettere un nome per l'hub di notifica
    Località: 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 aver effettuato il provisioning del hub di notifica, passare a tale risorsa.

  5. Passare al nuovo hub di notifica .

  6. Selezionare criteri di accesso dall'elenco (in MANAGE).

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

Configurare l'hub di notifica con informazioni APNS

In Notification Servicesselezionare 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 il di produzione per 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
    • ID bundle
    • ID team
    • token
  3. Scegliere Sandbox.

  4. Selezionare Salva.

Configurare l'hub di notifica con informazioni FCM

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

Creare un'applicazione back-end dell'API Web core ASP.NET

In questa sezione viene creato il back-end dell'API Web ASP.NET Core per gestire registrazione del dispositivo e l'invio di notifiche all'app per dispositivi mobili Xamarin.Forms.

Creare un progetto Web

  1. In Visual Studioselezionare File>Nuova soluzione.

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

  3. Nella finestra di dialogo Configure your new ASP.NET Core Web API selezionare Target Framework di .NET Core 3.1.

  4. Immettere PushDemoApi per nome progetto e quindi selezionare Crea.

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

    Nota

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

    Se viene visualizzato un certificato di sviluppo non valido trovato messaggio:

    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 Installa e considerare attendibile il nuovo certificato, quindi immettere la password per il Keychain.

  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 quindi passare alla directory del file di progetto ed eseguire i comandi seguenti:

    dotnet user-secrets init
    dotnet user-secrets set "NotificationHub:Name" <value>
    dotnet user-secrets set "NotificationHub:ConnectionString" <value>
    

    Sostituire i valori segnaposto con il nome dell'hub di notifica e i valori della stringa di connessione. Sono stati annoti nella sezione creare un hub di notifica. In caso contrario, è possibile cercarli in Azure.

    NotificationHub:Name:
    Vedere name nel riepilogo Essentials nella parte superiore di Panoramica.

    NotificationHub:ConnectionString:
    Vedere DefaultFullSharedAccessSignature in Criteri di accesso

    Nota

    Per gli scenari di produzione, è possibile esaminare opzioni come azure KeyVault per archiviare in modo sicuro la 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 i 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. ControlloFare clic su nel progetto PushDemoApi , scegliere Nuova cartella dal menu Aggiungi , quindi fare clic su Aggiungi utilizzando Authentication come Nome cartella.

  3. ControlloFare clic su nella 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 di autenticazione 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 di autenticazione denominata ApiKeyAuthenticationBuilderExtensions.cs, quindi aggiungere l'implementazione seguente.

    using System;
    using Microsoft.AspNetCore.Authentication;
    
    namespace PushDemoApi.Authentication
    {
        public static class AuthenticationBuilderExtensions
        {
            public static AuthenticationBuilder AddApiKeyAuth(
                this AuthenticationBuilder builder,
                Action<ApiKeyAuthOptions> configureOptions)
            {
                return builder
                    .AddScheme<ApiKeyAuthOptions, ApiKeyAuthHandler>(
                        ApiKeyAuthOptions.DefaultScheme,
                        configureOptions);
            }
        }
    }
    

    Nota

    Questo metodo di estensione semplifica il codice di configurazione del middleware in Startup.cs rendendolo più leggibile e generalmente più semplice da seguire.

  7. In Startup.csaggiornare il metodo di ConfigureServices per configurare l'autenticazione della chiave API sotto la chiamata ai servizi di . AddControllers metodo.

    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. Sempre in Startup.csaggiornare il metodo Configure per chiamare il UseAuthentication e i metodi di estensione UseAuthorization nel IApplicationBuilder dell'app. Assicurarsi che questi metodi vengano chiamati dopo UseRouting e prima di 'app. UseEndpoints.

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
    
        app.UseHttpsRedirection();
    
        app.UseRouting();
    
        app.UseAuthentication();
    
        app.UseAuthorization();
    
        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
        });
    }
    

    Nota

    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 lo schema di progettazione software di inserimento delle dipendenze, una tecnica per ottenere 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. ControlloFare clic su nella cartella Dipendenze , quindi scegliere Gestisci pacchetti NuGet....

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

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

  4. ControlloFare clic su nel progetto PushDemoApi Nuova cartella dal menu Aggiungi , quindi fare clic su Aggiungi utilizzando Modelli come Nome cartella.

  5. ControlloFare clic su nella cartella Modelli di , quindi scegliere Nuovo file... dal menu Aggiungi .

  6. Selezionare >Classe vuota, immettere PushTemplates.cs per 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 con token per le notifiche generiche e invisibile all'utente richieste da questo scenario. I payload vengono definiti al di fuori del 'installazione per consentire la sperimentazione senza dover aggiornare le installazioni esistenti tramite il servizio. La gestione delle modifiche alle installazioni in questo modo non rientra nell'ambito di questa esercitazione. Per l'ambiente di produzione, prendere in considerazione modelli personalizzati.

  7. Aggiungere un'altra classe vuota alla cartella modelli 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 altro classe vuota alla cartella modelli di 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 altro classe vuota alla cartella modelli di 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 di PushDemoApi denominato Services.

  11. Aggiungere un dell'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 un 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 di tag fornita per 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 altri dettagli, vedere la documentazione routing ed espressioni di tag.

  13. In Startup.csaggiornare il metodo ConfigureServices per aggiungere il 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 delle notifiche

  1. ControlloFare clic su nella cartella Controller , quindi scegliere Nuovo file... dal menu Aggiungi .

  2. Selezionare ASP.NET Core>Web API Controller Class, immettere NotificationsController per Name, quindi fare clic su New.

    Nota

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

  3. Aggiungere gli spazi dei nomi seguenti all'inizio del file.

    using System.ComponentModel.DataAnnotations;
    using System.Net;
    using System.Threading;
    using System.Threading.Tasks;
    using Microsoft.AspNetCore.Authorization;
    using Microsoft.AspNetCore.Mvc;
    using PushDemoApi.Models;
    using PushDemoApi.Services;
    
  4. Aggiornare il controller basato su modelli in modo che derivi da ControllerBase ed è 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 non è necessaria in questo caso e quindi è possibile usare ControllerBase. Se si segue con Visual Studio 2019, è possibile ignorare questo passaggio.

  5. Se si sceglie di completare la sezione Autenticare i client usando una chiave API, è necessario decorare anche l' 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 il launchUrl da a api/notifications in modo che corrisponda all'URL specificato nell'attributo RegistrationsControllerRoute.

  8. Avviare il debug (ComandoInvio) per verificare che l'app funzioni con il nuovo NotificationsController e restituisca uno stato di non autorizzato 401.

    Nota

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

  9. In una nuova scheda Postman impostare la richiesta su GET. Immettere l'indirizzo seguente sostituendo il segnaposto applicationUrl con il https applicationUrl trovato in Proprietà launchSettings.json.

    <applicationUrl>/api/notifications
    

    Nota

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

  10. Se si sceglie di completare la sezione Autenticare i client usando una 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

    Dovrebbe essere visualizzato uno stato 200 OK con alcuni contenuto JSON.

    Se viene visualizzato un avviso di verifica certificato SSL, è possibile cambiare la richiesta di verifica del certificato SSL postman disattivata nella Impostazioni.

  12. Sostituire i metodi della classe basato su modelli 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 del servizio app di Azure per l'hosting del servizio back-end.

  1. Accedere al 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 destinazione Sottoscrizione creato l'hub di notifica in .

    gruppo di risorse :
    Scegliere lo stesso gruppo di risorse in cui è stato creato l'hub di notifica.

    piano di servizio app/località:
    Creare un nuovo piano di servizio app

    Nota

    Passare dall'opzione predefinita a un piano che include supporto SSL. In caso contrario, sarà necessario eseguire i passaggi appropriati quando si lavora con l'app per dispositivi mobili per impedire che le richieste http 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 del 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 applicazione per immettere il Nome e un 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. Dovrebbe essere possibile copiarli. L'impostazione authentication:ApiKey è necessaria solo se si sceglie di completare la sezione Autenticare i client usando una chiave API. Per gli scenari di produzione, è possibile esaminare opzioni come Azure KeyVault. Queste impostazioni sono state aggiunte 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

Distribuire quindi l'app nell'app per le API per renderla accessibile da tutti i dispositivi.

Nota

I passaggi seguenti sono specifici per Visual Studio per Mac. Se si segue con Visual Studio 2019 in Windows, il flusso di pubblicazione sarà diverso. Vedere Pubblicare nel servizio app di Azure in Windows.

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

  2. ControlloFare clic su il 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 la 'app per le API del 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 annotato nella sezione precedente pubblicare 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 la sezione Autenticare i client usando una 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 Body, quindi scegliere JSON dall'elenco di opzioni di formato e quindi includere alcuni segnaposto contenuto JSON:

    {}
    
  4. Fare clic su Invia.

    Nota

    Si dovrebbe ricevere uno stato 422 UnprocessableEntity dal servizio.

  5. Eseguire di nuovo i passaggi da 1 a 4, ma questa volta specificando l'endpoint delle richieste per convalidare la ricezione di una 400 richiesta non valida risposta.

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

Nota

Non è ancora possibile testare l'API usando dati di richiesta validi, perché saranno necessarie informazioni specifiche della piattaforma dall'app per dispositivi mobili client.

Creare un'applicazione Xamarin.Forms multipiattaforma

In questa sezione viene creata una Xamarin.Forms'applicazione per dispositivi mobili 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.

Un avviso viene visualizzato quando viene specificata un'azione e l'app è in primo piano. In caso contrario, le notifiche vengono visualizzate nel Centro notifiche.

Nota

In genere si eseguono le azioni di registrazione (e annullamento della registrazione) durante il punto appropriato nel ciclo di vita dell'applicazione (o come parte dell'esperienza di prima esecuzione) senza input espliciti di registrazione/annullamento della 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 Studiocreare una nuova soluzione Xamarin.Forms usando app Forms vuota come modello e immettendo PushDemo per il nome del progetto .

    Nota

    Nella finestra di dialogo Configura app Forms vuota verificare che la identificatore organizzazione corrisponda al valore usato in precedenza e che vengano controllate sia Android che destinazioni iOS.

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

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

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

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

  6. Compilare ed eseguire l'app in ogni piattaforma di destinazione ( comando + Immettere) per testare l'esecuzione dell'app modello nei dispositivi.

Implementare i componenti multipiattaforma

  1. ControlloFare clic su nel progetto PushDemo , scegliere Nuova cartella dal menu Aggiungi , quindi fare clic su Aggiungi utilizzando Modelli come Nome cartella.

  2. ControlloFare clic su nella cartella Modelli di , quindi scegliere Nuovo file... dal menu Aggiungi .

  3. Selezionare Generale>Classe vuota, immettere DeviceInstallation.cse 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 di enumerazione vuota alla cartella modelli 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 quindi aggiungere un 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 di XamCAT . Verrà usato come contenitore IoC (Inversion of Control) 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 funzionalità specifiche della piattaforma e DeviceInstallation informazioni 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

    Questa operazione 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

    Viene usato come meccanismo semplice per centralizzare la gestione delle azioni di notifica.

  9. Aggiungere un empty Interface alla cartella Services denominata IPushDemoNotificationActionService.cs che deriva dalla 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 un classe vuota alla cartella Services denominata NotificationRegistrationService.cs implementare il 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 è necessario solo se si sceglie di completare la sezione Autenticare i client usando una chiave API.

  11. Aggiungere un classe vuota alla cartella Services denominata PushDemoNotificationActionService.cs l'implementazione del 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 un 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 automatizzata o eseguirne l'override usando una classe parziale locale. Questa operazione verrà eseguita nel passaggio successivo.

    Il campo ApiKey è necessario solo se si sceglie di completare la sezione Autenticare i client usando una chiave API.

  13. Aggiungere un'altra classe vuota al Config.local_secrets.cs progetto PushDemo 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 è stato creato il servizio back-end, è necessario prendere nota di questi elementi. L'URL dell'app per le API deve essere . Ricordarsi di aggiungere *.local_secrets.* al file gitignore per evitare il commit di questo file.

    Il campo ApiKey è necessario solo se si sceglie di completare la sezione Autenticare i client usando una chiave API.

  14. Aggiungere un classe vuota al progetto di 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 viene avviata passando un'implementazione specifica della piattaforma di IDeviceInstallationService.

    L'argomento del costruttore NotificationRegistrationServiceapiKey è necessario solo se si sceglie di completare la sezione Autenticare i client usando una chiave API.

Implementare l'interfaccia utente multipiattaforma

  1. Nel progetto PushDemo aprire MainPage.xaml e sostituire il controllo StackLayout con il codice seguente.

    <StackLayout VerticalOptions="EndAndExpand"  
                 HorizontalOptions="FillAndExpand"
                 Padding="20,40">
        <Button x:Name="RegisterButton"
                Text="Register"
                Clicked="RegisterButtonClicked" />
        <Button x:Name="DeregisterButton"
                Text="Deregister"
                Clicked="DeregisterButtonClicked" />
    </StackLayout>
    
  2. A questo punto, in MainPage.xaml.csaggiungere un campo sottostante per archiviare un riferimento all'implementazione INotificationRegistrationService di .

    readonly INotificationRegistrationService _notificationRegistrationService;
    
  3. Nel costruttore MainPage risolvere l'implementazione INotificationRegistrationService usando il ServiceContainer e assegnarlo al campo sottostante notificationRegistrationService.

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

    void RegisterButtonClicked(object sender, EventArgs e)
        => _notificationRegistrationService.RegisterDeviceAsync().ContinueWith((task)
            => { ShowAlert(task.IsFaulted ?
                    task.Exception.Message :
                    $"Device registered"); });
    
    void DeregisterButtonClicked(object sender, EventArgs e)
        => _notificationRegistrationService.DeregisterDeviceAsync().ContinueWith((task)
            => { ShowAlert(task.IsFaulted ?
                    task.Exception.Message :
                    $"Device deregistered"); });
    
    void ShowAlert(string message)
        => MainThread.BeginInvokeOnMainThread(()
            => DisplayAlert("PushDemo", message, "OK").ContinueWith((task)
                => { if (task.IsFaulted) throw task.Exception; }));
    
  5. In App.xaml.csverificare che venga fatto riferimento agli spazi dei nomi seguenti.

    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 del IPushNotificationActionService usando l' ServiceContainer e sottoscrivere l'evento IPushDemoNotificationActionServiceActionTriggered.

    public App()
    {
        InitializeComponent();
    
        ServiceContainer.Resolve<IPushDemoNotificationActionService>()
            .ActionTriggered += NotificationActionTriggered;
    
        MainPage = new MainPage();
    }
    

    Nota

    Questo è semplicemente per dimostrare la ricezione e la propagazione delle azioni di notifica push. In genere, questi vengono gestiti automaticamente, ad esempio passando a una visualizzazione specifica o aggiornando alcuni dati anziché visualizzare un avviso tramite la radice Pagina, mainPage in questo caso.

Configurare il progetto Android nativo per le notifiche push

Convalidare il nome e le autorizzazioni del pacchetto

  1. In PushDemo.Androidaprire la opzioni progetto e quindi applicazione Android dalla sezione Compilazione.

  2. Verificare che il nome del pacchetto corrisponda al valore usato nel progetto console Firebase Firebase. Il nome del pacchetto era nel formato com.<organization>.pushdemo.

  3. Impostare il versione minima di Android su Android 8.0 (livello API 26) e Versione di Android di destinazione allalivello api più recente.

    Nota

    Solo i dispositivi che eseguono livello API 26 e versioni successive sono supportati ai fini di questa esercitazione, ma è 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.Android, ControlloFare clic su nella cartella pacchetti , quindi scegliere Gestisci pacchetti NuGet....

  2. Cercare Xamarin.GooglePlayServices.Base (non Seminterrato) e assicurarsi che sia selezionato.

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

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

Aggiungere il file JSON di Google Services

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

  2. Scegliere il file di google-services.json scaricato in precedenza quando si configura il progetto di PushDemo nel Firebase Console e quindi fare clic su Apri.

  3. Quando richiesto, scegliere di Copiare il file nella directory.

  4. Controllo + Fare clic su nel file di google-services.json dal progetto di PushDemo.Android, quindi assicurarsi che GoogleServicesJson sia impostato come Azione di compilazione.

Gestire le notifiche push per Android

  1. ControlloFare clic su nel progetto di , scegliere Nuova cartella dal menu Aggiungi , quindi fare clic su Aggiungi utilizzando Services come nome cartella .

  2. ControlloFare clic su nella cartella servizi , quindi scegliere Nuovo file... dal menu Aggiungi .

  3. Selezionare Generale>Classe vuota, immettere DeviceInstallationService.cs per 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 altro 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.csverificare 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.csimpostare il 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 di 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 di 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 un determinato Intent ha un valore aggiuntivo denominato 'azione. Attivare in modo condizionale l'azione usando l'implementazione 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 metodo ProcessNotificationActions.

    protected override void OnNewIntent(Intent intent)
    {
        base.OnNewIntent(intent);
        ProcessNotificationActions(intent);
    }
    

    Nota

    Poiché l' LaunchMode per Activity è impostato su SingleTop, un finalità verrà inviato all'istanza attività esistente tramite l'istanza di metodo OnNewIntent anziché il metodo onCreate e quindi è necessario gestire una finalità in ingresso sia in OnCreate che nei metodi onNewIntent.

  11. Aggiornare il metodo onCreate per chiamare subito dopo la chiamata a passando 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 , aggiungendo MainActivity come IOnSuccessListener.

    if (DeviceInstallationService.NotificationsSupported)
    {
        FirebaseInstanceId.GetInstance(Firebase.FirebaseApp.Instance)
            .GetInstanceId()
            .AddOnSuccessListener(this);
    }
    
  13. Sempre in OnCreate, chiamare ProcessNotificationActions immediatamente dopo la chiamata a LoadApplication passando la 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 al account per sviluppatore Apple in Preferenze di Visual Studio...Pubblicazione account per sviluppatori Apple e il certificato appropriato e profilo di provisioning è stato scaricato. È necessario aver creato questi asset come parte dei passaggi precedenti.

  2. In PushDemo.iOSaprire Info.plist e assicurarsi che il BundleIdentifier corrisponda al valore usato per il rispettivo profilo di provisioning nel Portale per sviluppatori Apple. Il BundleIdentifier era nel formato .

  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, ma è possibile estenderlo per supportare i dispositivi che eseguono versioni precedenti.

  4. Aprire il Opzioni progetto per PushDemo.iOS (fare doppio clic sul progetto).

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

    Nota

    Se il certificato di firma e profilo di provisioning non sono stati selezionati automaticamente, scegliere provisioning manuale, quindi fare clic su Opzioni di firma del bundle . Assicurarsi che l' team di sia selezionata per di identità di firma e che sia selezionato il profilo di provisioning specifico PushDemo per profilo di provisioning sia per debug che per le configurazioni Versione assicurando che iPhone sia selezionato per Platform in entrambi i casi.

  6. In PushDemo.iOSaprire Entitlements.plist e assicurarsi che Abilita notifiche push sia selezionato quando visualizzato nella scheda Entitlement . Assicurarsi quindi che l'impostazione ambiente APS sia impostata su di sviluppo quando viene visualizzata nella scheda origine .

Gestire le notifiche push per iOS

  1. ControlloFare clic su nel progetto di PushDemo.iOS, scegliere Nuova cartella dal menu aggiungi , quindi fare clic su Aggiungi utilizzando Services comeNome cartella .

  2. ControlloFare clic su nella cartella servizi , quindi scegliere Nuovo file... dal menu Aggiungi .

  3. Selezionare Generale>Classe vuota, immettere DeviceInstallationService.cs per 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 di PushDemo.iOS denominato Extensions quindi aggiungere un 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.csverificare 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 implementazioniIPushDemoNotificationActionService, INotificationRegistrationServicee 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 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 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. È necessario 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 passando l'implementazione specifica della piattaforma di IDeviceInstallationService.

    Bootstrap.Begin(() => new DeviceInstallationService());
    
  14. Nello stesso metodo richiedere in modo condizionale l'autorizzazione e registrarsi per le notifiche remote immediatamente dopo Bootstrap.Begin.

    if (DeviceInstallationService.NotificationsSupported)
    {
        UNUserNotificationCenter.Current.RequestAuthorization(
                UNAuthorizationOptions.Alert |
                UNAuthorizationOptions.Badge |
                UNAuthorizationOptions.Sound,
                (approvalGranted, error) =>
                {
                    if (approvalGranted && error == null)
                        RegisterForRemoteNotifications();
                });
    }
    
  15. Sempre in FinishedLaunching, chiamare ProcessNotificationActions immediatamente dopo la chiamata a se l'argomento opzioni di contiene l'argomento UIApplication.LaunchOptionsRemoteNotification Key passando l'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 POSTe immettere l'indirizzo seguente:

    https://<app_name>.azurewebsites.net/api/notifications/requests
    
  3. Se si sceglie di completare la sezione Autenticare i client usando una 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 Body, quindi scegliere JSON dall'elenco di opzioni di formato e quindi includere alcuni segnaposto contenuto JSON:

    {
        "text": "Message from Postman!",
        "action": "action_a"
    }
    
  5. Selezionare il pulsante Codice , che si trova 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 stata inclusa un'intestazione apikey ):

    POST /api/notifications/requests HTTP/1.1
    Host: https://<app_name>.azurewebsites.net
    apikey: <your_api_key>
    Content-Type: application/json
    
    {
        "text": "Message from backend service",
        "action": "action_a"
    }
    
  6. Eseguire l'applicazione PushDemo di 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 Debugo se l'app è stata distribuita eseguendo l'applicazione, forzare la chiusura dell'app e avviarla nuovamente dall'utilità di avvio.

  7. Nell'app PushDemo toccare il pulsante Registra.

  8. Tornare in Postman, chiudere la finestra Genera frammenti di codice (se non è già stato fatto) 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 'azione ActionA ricevuta.

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

  11. Verificare di ottenere una risposta di 200 OK in Postman di nuovo. 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 visualizzare l'azione actionA ricevuta avviso.

  13. Tornare in 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 'azione ActionB ricevuta anziché 'azione ActionA ricevuta.

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

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

Risoluzione dei problemi

Nessuna risposta dal servizio back-end

Quando si esegue il test in locale, assicurarsi che il servizio back-end sia in esecuzione e che usi 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 indicativamente https://<api_name>.azurewebsites.net/ o https://localhost:5001/ durante il test in locale.

Non ricevere 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 genererà la generazione di un nuovo token Firebase . Anche l'installazione dell'hub di notifica deve essere aggiornata.

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 questo errore viene visualizzato durante il test in locale, verificare che il valore della chiave definito nella configurazione client corrisponda al valore di autenticazione :ApiKey valore dell'impostazione utente usato dall'API .

Se si esegue il test con un'app per le API , assicurarsi che il valore della chiave nel file di configurazione del 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 è scelto di non completare la sezione Autenticare i client usando una chiave API, assicurarsi di non aver applicato l'attributo Authorize alla classe NotificationsController .

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

Verificare che 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 un /. L'indirizzo di base deve essere indicativamente https://<api_name>.azurewebsites.net/ o https://localhost:5001/ durante 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 nel HttpResponse.

Esaminare i suggerimenti precedenti per la risoluzione dei problemi, se applicabili in base al codice di stato.

Impostare un punto di interruzione nelle 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 eseguiti passaggi. Verificare che i valori appropriati vengano risolti per le variabili installation id e token per la piattaforma appropriata.

Impossibile risolvere un ID per il messaggio di errore del dispositivo

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

Passaggi successivi

È ora necessario avere un'app Xamarin.Forms di base connessa a un hub di notifica tramite un servizio back-end e può inviare e ricevere notifiche.

Probabilmente sarà necessario adattare l'esempio usato in questa esercitazione per adattarsi al proprio scenario. È consigliabile implementare una gestione degli errori più affidabile, logica di ripetizione dei tentativi e registrazione.

i di Visual Studio App Center possono essere incorporati rapidamente nelle app per dispositivi mobili che forniscono di analisi e diagnostica per facilitare la risoluzione dei problemi.