Condividi tramite


Esercitazione: Inviare notifiche push alle app React Native usando Hub di notifica di Azure tramite un servizio back-end

Scaricare l'esempio Scaricare l'esempio

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

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

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

Questa esercitazione illustra i passaggi seguenti:

Prerequisiti

Per seguire questa procedura, è necessario:

  • Una sottoscrizione di Azure in cui è possibile creare e gestire le risorse.
  • Un Mac con Visual Studio per Mac installato (o un PC che esegue Visual Studio 2019 con il carico di lavoro Sviluppo per dispositivi mobili con .NET).
  • Possibilità di eseguire l'app in Android (dispositivi fisici o emulatori) o iOS (solo dispositivi fisici).

Per Android, è necessario disporre di:

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

Per iOS, è necessario disporre di:

Nota

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

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

I passaggi forniti sono per Visual Studio per Mac e Visual Studio Code, ma è possibile seguire questa procedura usando Visual Studio 2019.

Configurare Servizi di notifica push e Hub di notifica di Azure

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

Creare un progetto Firebase e abilitare Firebase Cloud Messaging per Android

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

    Nota

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

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

    Aggiungere Firebase all'app Android

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

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

      Specificare il nome del pacchetto

    2. Selezionare Registra app.

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

      Scaricare google-services.json

    4. Selezionare Avanti.

    5. Selezionare Continua nella console

      Nota

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

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

    Selezionare Impostazioni progetto

    Nota

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

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

    Copiare la chiave del server

Registrare l'app iOS per le notifiche push

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

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

    Pagina ID app portale di provisioning iOS

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

    Pagina Del portale di provisioning iOS per registrare un nuovo ID

  3. Aggiornare i tre valori seguenti per la nuova app e quindi selezionare Continua:

    • Descrizione: digitare un nome descrittivo per l'app.

    • ID bundle: immettere un ID bundle del modulo com.organization_identifier<>.<>product_name come indicato nella Guida alla distribuzione delle app. Nello screenshot seguente il mobcat valore viene usato come identificatore dell'organizzazione e il valore PushDemo viene usato come nome del prodotto.

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

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

      Modulo per registrare un nuovo ID app

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

      Confermare il nuovo ID app

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

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

Creazione di un certificato per Hub di notifica

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

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

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

L'approccio più recente offre numerosi vantaggi come documentato nell'autenticazione basata su token (HTTP/2) per APNS. Sono necessari meno passaggi, ma viene richiesto anche per scenari specifici. Tuttavia, i passaggi sono stati forniti per entrambi gli approcci, poiché entrambi funzioneranno a scopo 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 Keychain Access. Può essere aperto dalla cartella Utilità o dall'altra cartella nel Launchpad.

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

    Usare l'accesso keychain per richiedere un nuovo certificato

    Nota

    Per impostazione predefinita, Keychain Access seleziona il primo elemento nell'elenco. Questo può essere un problema se si è nella categoria Certificati e Apple Worldwide Developer Relations Authority non è il primo elemento nell'elenco. Assicurarsi di avere un elemento non chiave o che sia selezionata la chiave dell'autorità di certificazione per sviluppatori apple in tutto il mondo , prima di generare la richiesta di firma del certificato .

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

    Informazioni sul certificato previste

  4. Immettere un nome per il file Richiesta firma certificato (CSR) in Salva con nome, selezionare il percorso in Dove e quindi selezionare Salva.

    Scegliere un nome di file per il certificato

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

  5. Tornare alla pagina Certificati, Identificatori & profili nel portale di provisioning iOS, scorrere verso il basso fino all'opzione Notifiche push selezionata e quindi selezionare Configura per creare il certificato.

    Pagina Modifica ID app

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

    Pulsante Crea certificato per ID app

    Viene visualizzata la schermata Crea un nuovo certificato .

    Nota

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

  7. Selezionare Scegli file, passare al percorso in cui è stato salvato il file CSR e quindi fare doppio clic sul nome del certificato per caricarlo. Selezionare Continua.

  8. Dopo aver creato il certificato, 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 di 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 nella Keychain, come illustrato nell'immagine seguente:

    Elenco dei certificati di accesso della portachiavi che mostra il nuovo certificato

    Nota

    Anche se il nome nel certificato potrebbe essere diverso, il nome verrà prefisso con Apple Development iOS Push Services e avere l'identificatore di bundle appropriato associato.

  10. In Accesso keychain fare + clic sul nuovo certificato push creato nella categoria Certificati . Selezionare Esporta, assegnare un nome al file, selezionare il formato p12 e quindi selezionare Salva.

    Esportare il certificato come formato p12

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

    Nota

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

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

    • Prefisso ID app (ID team)
    • Bundle ID
  2. Tornare a Certificati, identificatori & profili, fare clic su Chiavi.

    Nota

    Se si dispone già di una chiave configurata per APNS, è possibile riutilizzare il certificato p8 scaricato subito dopo la creazione. In tal caso, è possibile ignorare i passaggi da 3 a 5.

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

  4. Specificare un valore di Nome chiave appropriato, quindi selezionare l'opzione Servizio notifiche push Apple (APNS) e quindi fare clic su Continua, quindi su 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 può essere scaricato nuovamente perché la copia del server viene rimossa.

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

  7. Prendere nota del valore ID chiave .

  8. Aprire il certificato p8 in un'applicazione adatta 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 l'hub di notifica.

Alla fine di questi passaggi, è necessario avere le informazioni seguenti da usare più avanti in Configurare l'hub di notifica con le informazioni di APNS:

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

Creare un profilo di provisioning per l'app

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

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

    Elenco dei profili di provisioning

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

    Selezionare l'ID app

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

    Nota

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

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

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

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

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

    Scegliere un nome del profilo di provisioning

  9. Quando viene creato il nuovo profilo di provisioning, selezionare Scarica. Tenere presente 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 si configura l'autenticazione con APNS. È possibile usare un certificato push p12 o l'autenticazione basata su token. Se si vuole usare un hub di notifica già creato, è possibile passare al passaggio 5.

  1. Accedere ad Azure.

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

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

    DETTAGLI DI BASE

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

    DETTAGLI DELLO SPAZIO DEI NOMI

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

    Nota

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

    DETTAGLI DELL'HUB DI NOTIFICA

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

    Nota

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

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

  5. Passare al nuovo hub di notifica.

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

  7. Prendere nota dei valori Nome criteri insieme ai relativi valori Stringa di connessione corrispondenti.

Configurare l'hub di notifica con le informazioni di APNS

In Servizi di notifica selezionare Apple e quindi seguire i passaggi appropriati in base all'approccio scelto in precedenza nella sezione Creazione di un certificato per hub di notifica .

Nota

Usare la modalità produzione per l'applicazione solo se si desidera 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 p12 esportato in precedenza e quindi selezionare Apri.

  4. Se necessario, specificare la password corretta.

  5. Selezionare Modalità sandbox .

  6. Selezionare Salva.

OPZIONE 2: Uso dell'autenticazione basata su token

  1. Selezionare Token.

  2. Immettere i valori seguenti acquisiti in precedenza:

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

  4. Selezionare Salva.

Configurare l'hub di notifica con le informazioni FCM

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

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

In questa sezione viene creato il back-end dell'API Web ASP.NET Core per gestire la registrazione dei dispositivi e l'invio di notifiche all'app per dispositivi mobili React Native.

Creare un progetto Web

  1. In Visual Studio selezionare Nuova>soluzione file.

  2. SelezionareApp>.NET Core>ASP.NET Core>API>Successiva.

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

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

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

    Nota

    L'app modello è configurata per usare WeatherForecastController come launchUrl. Questa opzione è impostata in Proprietà>launchSettings.json.

    Se viene richiesto un messaggio relativo a un certificato di sviluppo non valido :

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

    2. Fare clic su quando viene richiesto di installare e considerare attendibile il nuovo certificato, quindi immettere la password per la 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 disaccoppiamento dei segreti dalla soluzione garantisce che non finisca 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 proprio nome dell'hub di notifica e i valori stringa di connessione. È stata effettuata una nota nella sezione creare un hub di notifica . In caso contrario, è possibile cercarli in Azure.

    NotificationHub:Name:
    Vedere Nome nel riepilogo Essentials nella parte superiore della panoramica.

    NotificationHub:ConnectionString:
    Vedere DefaultFullSharedAccessSignature nei criteri di accesso

    Nota

    Per gli scenari di produzione, è possibile esaminare le opzioni, ad esempio Azure KeyVault, per archiviare in modo sicuro le stringa di connessione. Per semplicità, i segreti verranno aggiunti alle impostazioni dell'applicazione Servizio app di Azure.

Autenticare i client usando una chiave API (facoltativo)

Le chiavi API non sono sicure come token, ma saranno 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 locali.

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

    Nota

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

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

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

  4. SelezionareClasse vuotagenerale>, immettere ApiKeyAuthOptions.cs per il nome, quindi fare clic su Nuovo aggiungendo l'implementazione seguente.

    using Microsoft.AspNetCore.Authentication;
    
    namespace PushDemoApi.Authentication
    {
        public class ApiKeyAuthOptions : AuthenticationSchemeOptions
        {
            public const string DefaultScheme = "ApiKey";
            public string Scheme => DefaultScheme;
            public string ApiKey { get; set; }
        }
    }
    
  5. Aggiungere un'altra classe vuota alla cartella Authentication denominata ApiKeyAuthHandler.cs, quindi aggiungere l'implementazione seguente.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Security.Claims;
    using System.Text.Encodings.Web;
    using System.Threading.Tasks;
    using Microsoft.AspNetCore.Authentication;
    using Microsoft.Extensions.Logging;
    using Microsoft.Extensions.Options;
    
    namespace PushDemoApi.Authentication
    {
        public class ApiKeyAuthHandler : AuthenticationHandler<ApiKeyAuthOptions>
        {
            const string ApiKeyIdentifier = "apikey";
    
            public ApiKeyAuthHandler(
                IOptionsMonitor<ApiKeyAuthOptions> options,
                ILoggerFactory logger,
                UrlEncoder encoder,
                ISystemClock clock)
                : base(options, logger, encoder, clock) {}
    
            protected override Task<AuthenticateResult> HandleAuthenticateAsync()
            {
                string key = string.Empty;
    
                if (Request.Headers[ApiKeyIdentifier].Any())
                {
                    key = Request.Headers[ApiKeyIdentifier].FirstOrDefault();
                }
                else if (Request.Query.ContainsKey(ApiKeyIdentifier))
                {
                    if (Request.Query.TryGetValue(ApiKeyIdentifier, out var queryKey))
                        key = queryKey;
                }
    
                if (string.IsNullOrWhiteSpace(key))
                    return Task.FromResult(AuthenticateResult.Fail("No api key provided"));
    
                if (!string.Equals(key, Options.ApiKey, StringComparison.Ordinal))
                    return Task.FromResult(AuthenticateResult.Fail("Invalid api key."));
    
                var identities = new List<ClaimsIdentity> {
                    new ClaimsIdentity("ApiKeyIdentity")
                };
    
                var ticket = new AuthenticationTicket(
                    new ClaimsPrincipal(identities), Options.Scheme);
    
                return Task.FromResult(AuthenticateResult.Success(ticket));
            }
        }
    }
    

    Nota

    Un gestore di autenticazione è un tipo che implementa il comportamento di uno schema, in questo caso uno schema di chiave API personalizzato.

  6. Aggiungere un'altra classe vuota alla cartella Authentication denominata ApiKeyAuthenticationBuilderExtensions.cs, quindi aggiungere l'implementazione seguente.

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

    Nota

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

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

    using PushDemoApi.Authentication;
    using PushDemoApi.Models;
    using PushDemoApi.Services;
    
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllers();
    
        services.AddAuthentication(options =>
        {
            options.DefaultAuthenticateScheme = ApiKeyAuthOptions.DefaultScheme;
            options.DefaultChallengeScheme = ApiKeyAuthOptions.DefaultScheme;
        }).AddApiKeyAuth(Configuration.GetSection("Authentication").Bind);
    }
    
  8. Ancora in Startup.cs, aggiornare il metodo Configure per chiamare i metodi di estensione UseAuthentication e UseAuthorization nel IApplicationBuilder dell'app. Assicurarsi che questi metodi vengano chiamati dopo UseRouting e prima dell'app. UseEndpoints.

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

    Nota

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

Aggiungere dipendenze e configurare i servizi

ASP.NET Core supporta il modello di progettazione software di inserimento delle dipendenze, che è una tecnica per ottenere l'inversione del controllo (IoC) tra le classi e le relative dipendenze.

L'uso dell'hub di notifica e dell'SDK di Hub di notifica per le operazioni back-end viene incapsulato all'interno di un servizio. Il servizio viene registrato e reso disponibile tramite un'astrazione appropriata.

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

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

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

  4. Controllo + Fare clic sul progetto PushDemoApi , scegliere Nuova cartella dal menu Aggiungi , quindi fare clic su Aggiungi usando modelli come Nome cartella.

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

  6. Selezionare Generale>Classe vuota, immettere PushTemplates.cs per Nome e 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 all'esterno dell'installazione per consentire la sperimentazione senza dover aggiornare le installazioni esistenti tramite il servizio. La gestione delle modifiche apportate alle installazioni in questo modo non rientra nell'ambito di questa esercitazione. Per la produzione, prendere in considerazione i modelli personalizzati.

  7. Aggiungere un'altra classe vuota alla cartella Models denominata DeviceInstallation.cs, quindi aggiungere l'implementazione seguente.

    using System.Collections.Generic;
    using System.ComponentModel.DataAnnotations;
    
    namespace PushDemoApi.Models
    {
        public class DeviceInstallation
        {
            [Required]
            public string InstallationId { get; set; }
    
            [Required]
            public string Platform { get; set; }
    
            [Required]
            public string PushChannel { get; set; }
    
            public IList<string> Tags { get; set; } = Array.Empty<string>();
        }
    }
    
  8. Aggiungere un'altra classe vuota alla cartella Models denominata NotificationRequest.cs, quindi aggiungere l'implementazione seguente.

    using System;
    
    namespace PushDemoApi.Models
    {
        public class NotificationRequest
        {
            public string Text { get; set; }
            public string Action { get; set; }
            public string[] Tags { get; set; } = Array.Empty<string>();
            public bool Silent { get; set; }
        }
    }
    
  9. Aggiungere un'altra classe vuota alla cartella Models denominataNotificationHubOptions.cs, quindi aggiungere l'implementazione seguente.

    using System.ComponentModel.DataAnnotations;
    
    namespace PushDemoApi.Models
    {
        public class NotificationHubOptions
        {
            [Required]
            public string Name { get; set; }
    
            [Required]
            public string ConnectionString { get; set; }
        }
    }
    
  10. Aggiungere una nuova cartella al progetto PushDemoApi denominato Services.

  11. Aggiungere un'interfaccia vuota alla cartella Services denominata INotificationService.cs, quindi aggiungere l'implementazione seguente.

    using System.Threading;
    using System.Threading.Tasks;
    using PushDemoApi.Models;
    
    namespace PushDemoApi.Services
    {
        public interface INotificationService
        {
            Task<bool> CreateOrUpdateInstallationAsync(DeviceInstallation deviceInstallation, CancellationToken token);
            Task<bool> DeleteInstallationByIdAsync(string installationId, CancellationToken token);
            Task<bool> RequestNotificationAsync(NotificationRequest notificationRequest, CancellationToken token);
        }
    }
    
  12. Aggiungere una classe vuota alla cartella Services denominata NotificationHubsService.cs, quindi aggiungere il codice seguente per implementare l'interfaccia INotificationService :

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading;
    using System.Threading.Tasks;
    using Microsoft.Azure.NotificationHubs;
    using Microsoft.Extensions.Logging;
    using Microsoft.Extensions.Options;
    using PushDemoApi.Models;
    
    namespace PushDemoApi.Services
    {
        public class NotificationHubService : INotificationService
        {
            readonly NotificationHubClient _hub;
            readonly Dictionary<string, NotificationPlatform> _installationPlatform;
            readonly ILogger<NotificationHubService> _logger;
    
            public NotificationHubService(IOptions<NotificationHubOptions> options, ILogger<NotificationHubService> logger)
            {
                _logger = logger;
                _hub = NotificationHubClient.CreateClientFromConnectionString(
                    options.Value.ConnectionString,
                    options.Value.Name);
    
                _installationPlatform = new Dictionary<string, NotificationPlatform>
                {
                    { nameof(NotificationPlatform.Apns).ToLower(), NotificationPlatform.Apns },
                    { nameof(NotificationPlatform.Fcm).ToLower(), NotificationPlatform.Fcm }
                };
            }
    
            public async Task<bool> CreateOrUpdateInstallationAsync(DeviceInstallation deviceInstallation, CancellationToken token)
            {
                if (string.IsNullOrWhiteSpace(deviceInstallation?.InstallationId) ||
                    string.IsNullOrWhiteSpace(deviceInstallation?.Platform) ||
                    string.IsNullOrWhiteSpace(deviceInstallation?.PushChannel))
                    return false;
    
                var installation = new Installation()
                {
                    InstallationId = deviceInstallation.InstallationId,
                    PushChannel = deviceInstallation.PushChannel,
                    Tags = deviceInstallation.Tags
                };
    
                if (_installationPlatform.TryGetValue(deviceInstallation.Platform, out var platform))
                    installation.Platform = platform;
                else
                    return false;
    
                try
                {
                    await _hub.CreateOrUpdateInstallationAsync(installation, token);
                }
                catch
                {
                    return false;
                }
    
                return true;
            }
    
            public async Task<bool> DeleteInstallationByIdAsync(string installationId, CancellationToken token)
            {
                if (string.IsNullOrWhiteSpace(installationId))
                    return false;
    
                try
                {
                    await _hub.DeleteInstallationAsync(installationId, token);
                }
                catch
                {
                    return false;
                }
    
                return true;
            }
    
            public async Task<bool> RequestNotificationAsync(NotificationRequest notificationRequest, CancellationToken token)
            {
                if ((notificationRequest.Silent &&
                    string.IsNullOrWhiteSpace(notificationRequest?.Action)) ||
                    (!notificationRequest.Silent &&
                    (string.IsNullOrWhiteSpace(notificationRequest?.Text)) ||
                    string.IsNullOrWhiteSpace(notificationRequest?.Action)))
                    return false;
    
                var androidPushTemplate = notificationRequest.Silent ?
                    PushTemplates.Silent.Android :
                    PushTemplates.Generic.Android;
    
                var iOSPushTemplate = notificationRequest.Silent ?
                    PushTemplates.Silent.iOS :
                    PushTemplates.Generic.iOS;
    
                var androidPayload = PrepareNotificationPayload(
                    androidPushTemplate,
                    notificationRequest.Text,
                    notificationRequest.Action);
    
                var iOSPayload = PrepareNotificationPayload(
                    iOSPushTemplate,
                    notificationRequest.Text,
                    notificationRequest.Action);
    
                try
                {
                    if (notificationRequest.Tags.Length == 0)
                    {
                        // This will broadcast to all users registered in the notification hub
                        await SendPlatformNotificationsAsync(androidPayload, iOSPayload, token);
                    }
                    else if (notificationRequest.Tags.Length <= 20)
                    {
                        await SendPlatformNotificationsAsync(androidPayload, iOSPayload, notificationRequest.Tags, token);
                    }
                    else
                    {
                        var notificationTasks = notificationRequest.Tags
                            .Select((value, index) => (value, index))
                            .GroupBy(g => g.index / 20, i => i.value)
                            .Select(tags => SendPlatformNotificationsAsync(androidPayload, iOSPayload, tags, token));
    
                        await Task.WhenAll(notificationTasks);
                    }
    
                    return true;
                }
                catch (Exception e)
                {
                    _logger.LogError(e, "Unexpected error sending notification");
                    return false;
                }
            }
    
            string PrepareNotificationPayload(string template, string text, string action) => template
                .Replace("$(alertMessage)", text, StringComparison.InvariantCulture)
                .Replace("$(alertAction)", action, StringComparison.InvariantCulture);
    
            Task SendPlatformNotificationsAsync(string androidPayload, string iOSPayload, CancellationToken token)
            {
                var sendTasks = new Task[]
                {
                    _hub.SendFcmNativeNotificationAsync(androidPayload, token),
                    _hub.SendAppleNativeNotificationAsync(iOSPayload, token)
                };
    
                return Task.WhenAll(sendTasks);
            }
    
            Task SendPlatformNotificationsAsync(string androidPayload, string iOSPayload, IEnumerable<string> tags, CancellationToken token)
            {
                var sendTasks = new Task[]
                {
                    _hub.SendFcmNativeNotificationAsync(androidPayload, tags, token),
                    _hub.SendAppleNativeNotificationAsync(iOSPayload, tags, token)
                };
    
                return Task.WhenAll(sendTasks);
            }
        }
    }
    

    Nota

    L'espressione tag fornita a SendTemplateNotificationAsync è limitata a 20 tag. È limitato a 6 per la maggior parte degli operatori, ma l'espressione contiene solo ORs (||) in questo caso. Se nella richiesta sono presenti più di 20 tag, devono essere suddivisi in più richieste. Per altri dettagli, vedere la documentazione relativa alle espressioni di routing e tag .

  13. In Startup.cs aggiornare il metodo ConfigureServices per aggiungere NotificationHubsService come implementazione singleton di INotificationService.

    
    using PushDemoApi.Models;
    using PushDemoApi.Services;
    
    public void ConfigureServices(IServiceCollection services)
    {
        ...
    
        services.AddSingleton<INotificationService, NotificationHubService>();
    
        services.AddOptions<NotificationHubOptions>()
            .Configure(Configuration.GetSection("NotificationHub").Bind)
            .ValidateDataAnnotations();
    }
    

Creare l'API delle notifiche

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

  2. Selezionare ASP.NET Core>Classe controller APIWeb, immettere NotificationsController come Nome e quindi fare clic su Nuovo.

    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 e sia decorato con l'attributo ApiController .

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

    Nota

    La classe base Controller fornisce il supporto per le visualizzazioni, ma non è necessario in questo caso e quindi ControllerBase può essere usato. 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 NotificationsController anche con l'attributo Authorize .

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

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

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

    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 valore applicationUrl> https trovato in Proprietà>launchSettings.json.

    <applicationUrl>/api/notifications
    

    Nota

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

  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 contenuto JSON .

    Se viene visualizzato un avviso di verifica del certificato SSL , è possibile disattivare l'impostazione Postman di verifica del certificato SSL nelle 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 Servizio app di Azure per ospitare il 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 sottoscrizione di destinazione in cui è stato creato l'hub di notifica.

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

    servizio app Piano/Località:
    Creare un nuovo piano di servizio app

    Nota

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

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

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

  5. Prendere nota della proprietà URL nel riepilogo Informazioni di base nella parte superiore della panoramica. Questo URL è l'endpoint back-end che verrà usato più avanti in questa esercitazione.

    Nota

    L'URL usa il nome dell'app per le API specificato in precedenza, con il formato https://<app_name>.azurewebsites.net.

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

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

    Si tratta delle stesse impostazioni definite in precedenza nelle impostazioni utente. Dovrebbe essere possibile copiarli. L'impostazione Authentication:ApiKey è necessaria solo se si è scelto 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 su Continua.

Pubblicare il servizio back-end

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

Nota

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

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

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

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

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

Dopo aver completato la procedura guidata, pubblica l'app in Azure e quindi apre l'app. Prendere nota dell'URL se non è già stato fatto. Questo URL è l'endpoint back-end usato più avanti in questa esercitazione.

Convalida dell'API pubblicata

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

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

    Nota

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

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

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

    {}
    
  4. Fare clic su Invia.

    Nota

    Si dovrebbe ricevere uno stato 422 UnprocessableEntity dal servizio.

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

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

Nota

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

Creare un'applicazione di React Native multipiattaforma

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

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

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

Nota

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

Creare la soluzione React Native

  1. In Terminalaggiornare gli strumenti di ambiente necessari per usare React Native usando i comandi seguenti:

    # install node
    brew install node
    # or update
    brew update node
    # install watchman
    brew install watchman
    # or update
    brew upgrade watchman
    # install cocoapods
    sudo gem install cocoapods
    
  2. In Terminaleseguire il comando seguente, se è React Native installata l'interfaccia della riga di comando per disinstallarla. Usare npx per accedere automaticamente alla versione più recente dell'interfaccia della riga di comando React Native disponibile:

    npm uninstall -g react-native-cli
    

    Nota

    React Native ha un'interfaccia della riga di comando predefinita. Anziché installare e gestire una versione specifica dell'interfaccia della riga di comando a livello globale, è consigliabile accedere alla versione corrente in fase di esecuzione usando npx, che viene fornito con Node.js. Con npx react-native <command>, la versione stabile corrente dell'interfaccia della riga di comando verrà scaricata ed eseguita al momento dell'esecuzione del comando.

  3. Passare alla cartella dei progetti in cui si vuole creare la nuova applicazione. Usare il modello basato su Typescript specificando il --template parametro:

    # init new project with npx
    npx react-native init PushDemo --template react-native-template-typescript
    
  4. Eseguire il server metro, che compila bundle JavaScript e monitora eventuali aggiornamenti del codice per aggiornare i bundle in tempo reale:

    cd PushDemo
    npx react-native start
    
  5. Eseguire l'app iOS per verificare la configurazione. Assicurarsi di avviare un simulatore iOS o di connettersi a un dispositivo iOS, prima di eseguire il comando seguente:

    npx react-native run-ios
    
  6. Eseguire l'app Android per verificare la configurazione. Richiede alcuni passaggi aggiuntivi per configurare un emulatore o un dispositivo Android per poter accedere al server React Native metro. I comandi seguenti generano il bundle JavaScript iniziale per Android e lo inserisce nella cartella asset.

    # create assets folder for the bundle
    mkdir android/app/scr/main/assets
    # build the bundle
    npx react-native bundle --platform android --dev true --entry-file index.js --bundle-output android/app/src/main/assets/index.android.bundle --assets-dest android/app/src/main/res
    # enable ability for sim to access the localhost
    adb reverse tcp:8081 tcp:8081
    

    Questo script verrà pre-distribuito con la versione iniziale dell'app. Dopo la distribuzione, configurare l'emulatore o il dispositivo per accedere al server metro specificando l'indirizzo IP del server. Eseguire il comando seguente per compilare ed eseguire l'applicazione Android:

    npx react-native run-android
    

    Una volta nell'app, premere CMD+M (emulatore) o scuotere il dispositivo per popolare le impostazioni dello sviluppatore, passare aChange Bundle LocationSettings> e specificare l'indirizzo IP del server metro con la porta predefinita: . <metro-server-ip-address>:8081

  7. App.tsx Nel file applicare qualsiasi modifica al layout della pagina, salvarla e apportare la modifica viene riflessa automaticamente nelle app iOS e Android.

    Nota

    Guida dettagliata alla configurazione dell'ambiente di sviluppo è disponibile nella documentazione ufficiale

Installare i pacchetti necessari

Per il funzionamento di questo esempio sono necessari i tre pacchetti seguenti:

  1. React Native Notifiche push di iOS - Project GitHub

    Questo pacchetto è stato creato quando pushNotificationIOS è stato suddiviso dal core di React Native. Il pacchetto implementa in modo nativo le notifiche push per iOS e fornisce React Native interfaccia per accedervi. Eseguire il comando seguente per installare il pacchetto:

    yarn add @react-native-community/push-notification-ios
    
  2. React Native notifiche push multipiattaforma

    Questo pacchetto implementa notifiche locali e remote in iOS e Android in modo multipiattaforma. Eseguire il comando seguente per installare il pacchetto:

    yarn add react-native-push-notification
    
  3. Pacchetto informazioni dispositivo Il pacchetto fornisce informazioni su un dispositivo in fase di esecuzione. Usarlo per definire un identificatore del dispositivo, usato per registrare la notifica push. Eseguire il comando seguente per installare il pacchetto:

    yarn add react-native-device-info
    

Implementare i componenti multipiattaforma

  1. Creare e implementare DemoNotificationHandler:

    import PushNotification from 'react-native-push-notification';
    
    class DemoNotificationHandler {
      private _onRegister: any;
      private _onNotification: any;
    
      onNotification(notification: any) {
        console.log('NotificationHandler:', notification);
    
        if (typeof this._onNotification === 'function') {
          this._onNotification(notification);
        }
      }
    
      onRegister(token: any) {
        console.log('NotificationHandler:', token);
    
        if (typeof this._onRegister === 'function') {
          this._onRegister(token);
        }
      }
    
      attachTokenReceived(handler: any) {
        this._onRegister = handler;
      }
    
      attachNotificationReceived(handler: any) {
        this._onNotification = handler;
      }
    }
    
    const handler = new DemoNotificationHandler();
    
    PushNotification.configure({
      onRegister: handler.onRegister.bind(handler),
      onNotification: handler.onNotification.bind(handler),
      permissions: {
        alert: true,
        badge: true,
        sound: true,
      },
      popInitialNotification: true,
      requestPermissions: true,
    });
    
    export default handler;
    
  2. Creare e implementare DemoNotificationService:

    import PushNotification from 'react-native-push-notification';
    import DemoNotificationHandler from './DemoNotificationHandler';
    
    export default class DemoNotificationService {
      constructor(onTokenReceived: any, onNotificationReceived: any) {
        DemoNotificationHandler.attachTokenReceived(onTokenReceived);
        DemoNotificationHandler.attachNotificationReceived(onNotificationReceived);
        PushNotification.getApplicationIconBadgeNumber(function(number: number) {
          if(number > 0) {
            PushNotification.setApplicationIconBadgeNumber(0);
          }
        });
      }
    
      checkPermissions(cbk: any) {
        return PushNotification.checkPermissions(cbk);
      }
    
      requestPermissions() {
        return PushNotification.requestPermissions();
      }
    
      cancelNotifications() {
        PushNotification.cancelLocalNotifications();
      }
    
      cancelAll() {
        PushNotification.cancelAllLocalNotifications();
      }
    
      abandonPermissions() {
        PushNotification.abandonPermissions();
      }
    }
    
  3. Creare e implementare DemoNotificationRegistrationService:

    export default class DemoNotificationService {
        constructor(
            readonly apiUrl: string,
            readonly apiKey: string) {
        }
    
    async registerAsync(request: any): Promise<Response> {
            const method = 'PUT';
            const registerApiUrl = `${this.apiUrl}/notifications/installations`;
            const result = await fetch(registerApiUrl, {
                method: method,
                headers: {
                    Accept: 'application/json',
                    'Content-Type': 'application/json',
                    'apiKey': this.apiKey
                },
                body: JSON.stringify(request)
            });
    
            this.validateResponse(registerApiUrl, method, request, result);
            return result;
        }
    
        async deregisterAsync(deviceId: string): Promise<Response> {
            const method = 'DELETE';
            const deregisterApiUrl = `${this.apiUrl}/notifications/installations/${deviceId}`;
            const result = await fetch(deregisterApiUrl, {
                method: method,
                headers: {
                    Accept: 'application/json',
                    'Content-Type': 'application/json',
                    'apiKey': this.apiKey
                }
            });
    
            this.validateResponse(deregisterApiUrl, method, null, result);
            return result;
        }
    
        private validateResponse(requestUrl: string, method: string, requestPayload: any, response: Response) {
            console.log(`Request: ${method} ${requestUrl} => ${JSON.stringify(requestPayload)}\nResponse: ${response.status}`);
            if (!response || response.status != 200) {
                throw `HTTP error ${response.status}: ${response.statusText}`;
            }
        }
    }
    
  4. Configurare l'app. Aprire package.json e aggiungere la definizione di script seguente:

    "configure": "cp .app.config.tsx src/config/AppConfig.tsx"
    

    Eseguire quindi questo script, che copia la configurazione predefinita nella config cartella.

    yarn configure
    

    Il passaggio finale consiste nell'aggiornare il file di configurazione copiato nel passaggio precedente con le informazioni di accesso alle API. Specificare apiKey e apiUrl parametri:

    module.exports = {
        appName: "PushDemo",
        env: "production",
        apiUrl: "https://<azure-push-notifications-api-url>/api/",
        apiKey: "<api-auth-key>",
    };
    

Implementare l'interfaccia utente multipiattaforma

  1. Definire il layout di pagina

    <View style={styles.container}>
      {this.state.isBusy &&
        <ActivityIndicator></ActivityIndicator>
      }
      <View style={styles.button}>
        <Button title="Register" onPress={this.onRegisterButtonPress.bind(this)} disabled={this.state.isBusy} />
      </View>
      <View style={styles.button}>
        <Button title="Deregister" onPress={this.onDeregisterButtonPress.bind(this)} disabled={this.state.isBusy} />
      </View>
    </View>
    
  2. Applicare stili

    const styles = StyleSheet.create({
      container: {
        flex: 1,
        alignItems: "center",
        justifyContent: 'flex-end',
        margin: 50,
      },
      button: {
        margin: 5,
        width: "100%",
      }
    });
    
  3. Inizializzare il componente di pagina

      state: IState;
      notificationService: DemoNotificationService;
      notificationRegistrationService: DemoNotificationRegistrationService;
      deviceId: string;
    
      constructor(props: any) {
        super(props);
        this.deviceId = DeviceInfo.getUniqueId();
        this.state = {
          status: "Push notifications registration status is unknown",
          registeredOS: "",
          registeredToken: "",
          isRegistered: false,
          isBusy: false,
        };
    
        this.notificationService = new DemoNotificationService(
          this.onTokenReceived.bind(this),
          this.onNotificationReceived.bind(this),
        );
    
        this.notificationRegistrationService = new DemoNotificationRegistrationService(
          Config.apiUrl,
          Config.apiKey,
        );
      }
    
  4. Definire gestori di clic del pulsante

      async onRegisterButtonPress() {
        if (!this.state.registeredToken || !this.state.registeredOS) {
          Alert.alert("The push notifications token wasn't received.");
          return;
        }
    
        let status: string = "Registering...";
        let isRegistered = this.state.isRegistered;
        try {
          this.setState({ isBusy: true, status });
          const pnPlatform = this.state.registeredOS == "ios" ? "apns" : "fcm";
          const pnToken = this.state.registeredToken;
          const request = {
            installationId: this.deviceId,
            platform: pnPlatform,
            pushChannel: pnToken,
            tags: []
          };
          const response = await this.notificationRegistrationService.registerAsync(request);
          status = `Registered for ${this.state.registeredOS} push notifications`;
          isRegistered = true;
        } catch (e) {
          status = `Registration failed: ${e}`;
        }
        finally {
          this.setState({ isBusy: false, status, isRegistered });
        }
      }
    
      async onDeregisterButtonPress() {
        if (!this.notificationService)
          return;
    
        let status: string = "Deregistering...";
        let isRegistered = this.state.isRegistered;
        try {
          this.setState({ isBusy: true, status });
          await this.notificationRegistrationService.deregisterAsync(this.deviceId);
          status = "Deregistered from push notifications";
          isRegistered = false;
        } catch (e) {
          status = `Deregistration failed: ${e}`;
        }
        finally {
          this.setState({ isBusy: false, status, isRegistered });
        }
      }
    
  5. Gestire le registrazioni dei token ricevuti e le notifiche push

      onTokenReceived(token: any) {
        console.log(`Received a notification token on ${token.os}`);
        this.setState({ registeredToken: token.token, registeredOS: token.os, status: `The push notifications token has been received.` });
    
        if (this.state.isRegistered && this.state.registeredToken && this.state.registeredOS) {
          this.onRegisterButtonPress();
        }
      }
    
      onNotificationReceived(notification: any) {
        console.log(`Received a push notification on ${this.state.registeredOS}`);
        this.setState({ status: `Received a push notification...` });
    
        if (notification.data.message) {
          Alert.alert(AppConfig.appName, `${notification.data.action} action received`);
        }
      }
    };
    

Configurare il progetto Android nativo per le notifiche push

Configurare i pacchetti Android necessari

Il pacchetto viene collegato automaticamente durante la compilazione dell'app. Sono disponibili alcuni passaggi aggiuntivi seguenti per completare il processo di configurazione.

Configurare il manifesto Android

Nella pagina "android/app/src/main/AndroidManifest.xml", verificare il nome del pacchetto, le autorizzazioni e i servizi necessari. Assicurarsi di registrare RNPushNotificationPublisher e RNPushNotificationBootEventReceiver ricevitori e registrare il RNPushNotificationListenerService servizio. I metadati delle notifiche possono essere usati per personalizzare l'aspetto delle notifiche push.

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="YOUR_PACKAGE_NAME">

      <uses-permission android:name="android.permission.INTERNET" />
      <uses-permission android:name="android.permission.WAKE_LOCK" />
      <uses-permission android:name="android.permission.VIBRATE" />
      <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>

      <application
        android:name=".MainApplication"
        android:label="@string/app_name"
        android:usesCleartextTraffic="true"
        android:icon="@mipmap/ic_launcher"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:allowBackup="false"
        android:theme="@style/AppTheme">

        <meta-data  android:name="com.dieam.reactnativepushnotification.notification_channel_name"
                    android:value="PushDemo Channel"/>
        <meta-data  android:name="com.dieam.reactnativepushnotification.notification_channel_description"
                    android:value="PushDemo Channel Description"/>
        <meta-data  android:name="com.dieam.reactnativepushnotification.notification_foreground"
                    android:value="true"/>
        <meta-data  android:name="com.dieam.reactnativepushnotification.notification_color"
                    android:resource="@android:color/white"/>

        <receiver android:name="com.dieam.reactnativepushnotification.modules.RNPushNotificationPublisher" />
        <receiver android:name="com.dieam.reactnativepushnotification.modules.RNPushNotificationBootEventReceiver">
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
            </intent-filter>
        </receiver>

        <service
            android:name="com.dieam.reactnativepushnotification.modules.RNPushNotificationListenerService"
            android:exported="false" >
            <intent-filter>
                <action android:name="com.google.firebase.MESSAGING_EVENT" />
            </intent-filter>
        </service>

        <activity
          android:name=".MainActivity"
          android:label="@string/app_name"
          android:configChanges="keyboard|keyboardHidden|orientation|screenSize|uiMode"
          android:launchMode="singleTask"
          android:windowSoftInputMode="adjustResize">
          <intent-filter>
              <action android:name="android.intent.action.MAIN" />
              <category android:name="android.intent.category.LAUNCHER" />
          </intent-filter>
        </activity>
        <activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
      </application>

</manifest>

Configurare i servizi Google

In "android/app/build.gradle" registrare Google Services:

dependencies {
  ...
  implementation 'com.google.firebase:firebase-analytics:17.3.0'
  ...
}

apply plugin: 'com.google.gms.google-services'

Copiare il file "google-services.json" scaricato durante l'installazione di FCM nella cartella di progetto "android/app/".

Gestire le notifiche push per Android

Il servizio esistente RNPushNotificationListenerService è stato configurato per gestire le notifiche push Android in ingresso. Questo servizio è stato registrato in precedenza nel manifesto dell'applicazione. Elabora le notifiche in ingresso e li proxy alla parte React Native multipiattaforma. Non sono necessari passaggi aggiuntivi.

Configurare il progetto iOS nativo per le notifiche push

Configurare i pacchetti iOS necessari

Il pacchetto viene collegato automaticamente durante la compilazione dell'app. È sufficiente installare i pod nativi:

npx pod-install

Configurare Info.plist e Entitlements.plist

  1. Passare alla cartella "PushDemo/ios" e aprire l'area di lavoro "PushDemo.xcworkspace", selezionare il progetto principale "PushDemo" e selezionare la scheda "Firma & Funzionalità".

  2. Aggiornare l'identificatore del bundle in modo che corrisponda al valore usato nel profilo di provisioning.

  3. Aggiungere due nuove funzionalità usando il pulsante - "+":

    • Funzionalità modalità in background e tick Notifiche remote.
    • Funzionalità notifiche push

Gestire le notifiche push per iOS

  1. Aprire "AppDelegate.h" e aggiungere l'importazione seguente:

    #import <UserNotifications/UNUserNotificationCenter.h>
    
  2. Aggiornare l'elenco di protocolli, supportato da "AppDelegate", aggiungendo UNUserNotificationCenterDelegate:

    @interface AppDelegate : UIResponder <UIApplicationDelegate, RCTBridgeDelegate, UNUserNotificationCenterDelegate>
    
  3. Aprire "AppDelegate.m" e configurare tutti i callback iOS necessari:

    #import <UserNotifications/UserNotifications.h>
    #import <RNCPushNotificationIOS.h>
    
    ...
    
    // Required to register for notifications
    - (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings
    {
     [RNCPushNotificationIOS didRegisterUserNotificationSettings:notificationSettings];
    }
    
    // Required for the register event.
    - (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
    {
     [RNCPushNotificationIOS didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
    }
    
    // Required for the notification event. You must call the completion handler after handling the remote notification.
    - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
    fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
    {
      [RNCPushNotificationIOS didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler];
    }
    
    // Required for the registrationError event.
    - (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
    {
     [RNCPushNotificationIOS didFailToRegisterForRemoteNotificationsWithError:error];
    }
    
    // IOS 10+ Required for localNotification event
    - (void)userNotificationCenter:(UNUserNotificationCenter *)center
    didReceiveNotificationResponse:(UNNotificationResponse *)response
             withCompletionHandler:(void (^)(void))completionHandler
    {
      [RNCPushNotificationIOS didReceiveNotificationResponse:response];
      completionHandler();
    }
    
    // IOS 4-10 Required for the localNotification event.
    - (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
    {
     [RNCPushNotificationIOS didReceiveLocalNotification:notification];
    }
    
    //Called when a notification is delivered to a foreground app.
    -(void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler
    {
      completionHandler(UNAuthorizationOptionSound | UNAuthorizationOptionAlert | UNAuthorizationOptionBadge);
    }
    

Testare la soluzione

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

Inviare una notifica di test

  1. Aprire una nuova scheda in Postman.

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

    https://<app_name>.azurewebsites.net/api/notifications/requests
    
  3. Se si sceglie di completare 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 raw per Body (Corpo), quindi scegliere JSON dall'elenco di opzioni di formato e quindi includere il contenuto JSON segnaposto:

    {
        "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 visualizzato 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 in una o entrambe le piattaforme di destinazione (Android e iOS).

    Nota

    Se si esegue il test in Android , assicurarsi di non essere in esecuzione in Debug o se l'app è stata distribuita eseguendo l'applicazione, forzare la chiusura dell'app e avviarla nuovamente dall'utilità di avvio.

  7. Nell'app PushDemo toccare il pulsante Registra .

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

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

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

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

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

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

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

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

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

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

Risoluzione dei problemi relativi

Nessuna risposta dal servizio back-end

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

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

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

Mancata ricezione di notifiche in Android dopo l'avvio o l'arresto di una sessione di debug

Assicurarsi di eseguire di nuovo la registrazione dopo l'avvio o l'arresto di una sessione di debug. Il debugger causerà la generazione di un nuovo token Firebase . L'installazione dell'hub di notifica deve essere aggiornata anche.

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

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

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

Se si esegue il test con un'app per le API, assicurarsi che il valore della chiave nel file di configurazione client corrisponda all'impostazione dell'applicazione Authentication:ApiKey usata nell'app per le API.

Nota

Se questa impostazione è stata creata o modificata dopo aver distribuito il servizio back-end, è necessario riavviare il servizio per renderla effettiva.

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

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

Verificare che l'endpoint e il metodo di richiesta HTTP siano corretti. Ad esempio, gli endpoint devono essere indicativamente:

  • [PUT]https://<api_name>.azurewebsites.net/api/notifications/installations
  • [DELETE]https://<api_name>.azurewebsites.net/api/notifications/installations/<installation_id>
  • [POST]https://<api_name>.azurewebsites.net/api/notifications/requests

Oppure quando si esegue il test in locale:

  • [PUT]https://localhost:5001/api/notifications/installations
  • [DELETE]https://localhost:5001/api/notifications/installations/<installation_id>
  • [POST]https://localhost:5001/api/notifications/requests

Quando si specifica l'indirizzo di base nell'app client, assicurarsi che termini con ./ L'indirizzo di base deve essere https://<api_name>.azurewebsites.net/ indicativamente o https://localhost:5001/ quando si esegue il test in locale.

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

Verificare che il dispositivo di test disponga della connettività di rete. Determinare quindi il codice di stato della risposta Http impostando un punto di interruzione per esaminare il valore della proprietà StatusCode in HttpResponse.

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

Impostare un punto di interruzione sulle righe che restituiscono questi codici di stato specifici per l'API corrispondente. Provare quindi a chiamare il servizio back-end durante il debug in locale.

Verificare che il servizio back-end funzioni come previsto tramite Postman usando il payload appropriato. Usare il payload effettivo creato dal codice client per la piattaforma in questione.

Esaminare le sezioni di configurazione specifiche della piattaforma per assicurarsi che non siano stati superati i passaggi. Verificare che i valori appropriati vengano risolti per installation id le variabili e token per la piattaforma appropriata.

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

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

Passaggi successivi

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

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

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