Självstudie: Skicka push-meddelanden till Xamarin.Forms-appar med hjälp av Azure Notification Hubs via en serverdelstjänst

Ladda ned exempel Ladda ned exemplet

I den här självstudien använder du Azure Notification Hubs för att skicka meddelanden till ett Xamarin.Forms-program som riktar sig till Android och iOS.

En ASP.NET Core webb-API-serverdel används för att hantera enhetsregistrering för klienten med den senaste och bästa installationsmetoden. Tjänsten skickar även push-meddelanden på ett plattformsoberoende sätt.

De här åtgärderna hanteras med Notification Hubs SDK för serverdelsåtgärder. Mer information om den övergripande metoden finns i dokumentationen om registrering från din appserverdel .

I den här självstudien går vi igenom följande steg:

Förutsättningar

Om du vill följa med behöver du:

För Android måste du ha:

  • En utvecklare har låst upp en fysisk enhet eller en emulator (som kör API 26 och senare med Google Play Services installerat).

För iOS måste du ha:

Observera

iOS-simulatorn stöder inte fjärrmeddelanden och därför krävs en fysisk enhet när du utforskar det här exemplet på iOS. Du behöver dock inte köra appen på både Android och iOS för att kunna slutföra den här självstudien.

Du kan följa stegen i det här exemplet med första principer utan tidigare erfarenhet. Du kommer dock att ha nytta av att känna till följande aspekter.

Viktigt

De angivna stegen är specifika för Visual Studio för Mac. Det går att följa med i Visual Studio 2019 , men det kan finnas vissa skillnader att stämma av. Till exempel beskrivningar av användargränssnitt och arbetsflöden, mallnamn, miljökonfiguration och så vidare.

Konfigurera Push Notification Services och Azure Notification Hub

I det här avsnittet konfigurerar du Firebase Cloud Messaging (FCM) och Apple Push Notification Services (APNS). Sedan skapar och konfigurerar du en meddelandehubb så att den fungerar med dessa tjänster.

Skapa ett Firebase-projekt och aktivera Firebase Cloud Messaging för Android

  1. Logga in på Firebase-konsolen. Skapa ett nytt Firebase-projekt som anger PushDemo som projektnamn.

    Observera

    Ett unikt namn genereras åt dig. Som standard består detta av en gemen variant av namnet du angav plus ett genererat tal avgränsat med ett bindestreck. Du kan ändra detta om du vill förutsatt att det fortfarande är globalt unikt.

  2. När du har skapat projektet väljer du Lägg till Firebase i din Android-app.

    Lägga till Firebase i din Android-app

  3. På sidan Lägg till Firebase i din Android-app utför du följande steg.

    1. Ange ett namn för ditt paket för Android-paketnamnet. Till exempel: com.<organization_identifier>.<package_name>.

      Ange paketnamnet

    2. Välj Registrera app.

    3. Välj Ladda ned google-services.json. Spara sedan filen i en lokal mapp för senare användning och välj Nästa.

      Ladda ned google-services.json

    4. Välj Nästa.

    5. Välj Fortsätt till konsolen

      Observera

      Om knappen Fortsätt till konsolen inte är aktiverad på grund av verifieringsinstallationskontrollen väljer du Hoppa över det här steget.

  4. I Firebase-konsolen väljer du kugghjulet för projektet. Välj sedan Projektinställningar.

    Välj Projektinställningar

    Observera

    Om du inte har laddat ned google-services.json-filen kan du ladda ned den på den här sidan.

  5. Växla till fliken Cloud Messaging längst upp. Kopiera och spara servernyckeln för senare användning. Du använder det här värdet för att konfigurera meddelandehubben.

    Kopiera servernyckel

Registrera din iOS-app för push-meddelanden

Om du vill skicka push-meddelanden till en iOS-app registrerar du ditt program med Apple och registrerar dig för push-meddelanden.

  1. Om du inte redan har registrerat din app bläddrar du till iOS-etableringsportalen i Apple Developer Center. Logga in på portalen med ditt Apple-ID, gå till Certifikat, Identifierare & profiler och välj sedan Identifierare. Klicka här om du + vill registrera en ny app.

    Sidan app-ID:n för iOS-etableringsportalen

  2. På skärmen Registrera en ny identifierare väljer du alternativknappen App-ID :n. Välj sedan Fortsätt.

    Registrera ny ID-sida i iOS-etableringsportalen

  3. Uppdatera följande tre värden för din nya app och välj sedan Fortsätt:

    • Beskrivning: Ange ett beskrivande namn för din app.

    • Paket-ID: Ange ett paket-ID för formuläret com.organization_identifier<>.<>product_name enligt beskrivningen i appdistributionsguiden. På följande skärmbild mobcat används värdet som organisationsidentifierare och PushDemo-värdet används som produktnamn.

      IOS-etableringsportalen registrerar app-ID-sida

    • Push-meddelanden: Kontrollera alternativet Push-meddelanden i avsnittet Funktioner .

      Formulär för att registrera ett nytt app-ID

      Den här åtgärden genererar ditt app-ID och begär att du bekräftar informationen. Välj Fortsätt och välj sedan Registrera för att bekräfta det nya app-ID:t.

      Bekräfta nytt app-ID

      När du har valt Registrera visas det nya app-ID:t som ett radobjekt på sidan Certifikat, Identifierare & Profiler .

  4. På sidan Certifikat letar identifierare & profiler , under Identifierare, upp det radobjekt för app-ID som du skapade. Välj sedan dess rad för att visa skärmen Redigera app-ID-konfiguration .

Skapa ett certifikat för Notification Hubs

Ett certifikat krävs för att meddelandehubben ska fungera med Apple Push Notification Services (APNS) och kan tillhandahållas på något av två sätt:

  1. Skapa ett p12-pushcertifikat som kan laddas upp direkt till Notification Hub (den ursprungliga metoden)

  2. Skapa ett p8-certifikat som kan användas för tokenbaserad autentisering (den nyare och rekommenderade metoden)

Den nyare metoden har ett antal fördelar som beskrivs i Tokenbaserad autentisering (HTTP/2) för APNS. Färre steg krävs men krävs även för specifika scenarier. Det finns dock steg för båda metoderna eftersom båda kommer att fungera i den här självstudien.

ALTERNATIV 1: Skapa ett p12-pushcertifikat som kan laddas upp direkt till Notification Hub
  1. På din Mac kör du nyckelringsåtkomstverktyget. Den kan öppnas från mappen Verktyg eller mappen Övrigt på Launchpad.

  2. Välj Nyckelringsåtkomst, expandera Certifikatassistenten och välj sedan Begär ett certifikat från en certifikatutfärdare.

    Använda nyckelringsåtkomst för att begära ett nytt certifikat

    Observera

    Som standard väljer Nyckelringsåtkomst det första objektet i listan. Detta kan vara ett problem om du är i kategorin Certifikat och Apple Worldwide Developer Relations Certification Authority inte är det första objektet i listan. Kontrollera att du har ett icke-nyckelobjekt, eller att nyckeln för Apple Worldwide Developer Relations Certification Authority är vald innan du genererar CSR (certifikatsigneringsbegäran).

  3. Välj din användare Email adress, ange ditt eget namn, se till att du anger Sparad till disk och välj sedan Fortsätt. Lämna CA-Email adress tom eftersom det inte krävs.

    Förväntad certifikatinformation

  4. Ange ett namn för csr-filen (Certificate Signing Request) i Spara som, välj platsen i Var och välj sedan Spara.

    Välj ett filnamn för certifikatet

    Den här åtgärden sparar CSR-filen på den valda platsen. Standardplatsen är Desktop. Kom ihåg den plats som valts för filen.

  5. Tillbaka på sidan Certifikat, identifierare & profiler i iOS-etableringsportalen rullar du ned till det markerade alternativet Push-meddelanden och väljer sedan Konfigurera för att skapa certifikatet.

    Sidan Redigera app-ID

  6. Fönstret TLS/SSL-certifikat för Apple Push Notification Service visas. Välj knappen Skapa certifikat under avsnittet TLS/SSL-certifikat för utveckling .

    Knappen Skapa certifikat för app-ID

    Skärmen Skapa ett nytt certifikat visas.

    Observera

    I den här självstudien används ett utvecklingscertifikat. Samma process används när du registrerar ett produktionscertifikat. Se bara till att du använder samma certifikattyp när du skickar meddelanden.

  7. Välj Välj fil, bläddra till den plats där du sparade CSR-filen och dubbelklicka sedan på certifikatnamnet för att läsa in den. Välj sedan Fortsätt.

  8. När portalen har skapat certifikatet väljer du knappen Ladda ned . Spara certifikatet och kom ihåg den plats där det sparas.

    Nedladdningssida för genererat certifikat

    Certifikatet laddas ned och sparas på datorn i mappen Hämtade filer .

    Leta upp certifikatfilen i mappen Hämtade filer

    Observera

    Som standard heter det nedladdade utvecklingscertifikatet aps_development.cer.

  9. Dubbelklicka på det nedladdade push-certifikatet aps_development.cer. Den här åtgärden installerar det nya certifikatet i nyckelringen enligt följande bild:

    Lista över certifikat för nyckelringsåtkomst som visar nytt certifikat

    Observera

    Även om namnet i certifikatet kan vara annorlunda kommer namnet att föregås av Apple Development iOS Push Services och ha rätt paketidentifierare associerad med det.

  10. I Nyckelringsåtkomstkontrollerar + du Klicka på det nya push-certifikatet som du skapade i kategorin Certifikat. Välj Exportera, namnge filen, välj formatet p12 och välj sedan Spara.

    Exportera certifikat som p12-format

    Du kan välja att skydda certifikatet med ett lösenord, men ett lösenord är valfritt. Klicka på OK om du vill kringgå skapandet av lösenord. Anteckna filnamnet och platsen för det exporterade p12-certifikatet. De används för att aktivera autentisering med APNs.

    Observera

    Namnet på p12-filen och platsen kan skilja sig från vad som visas i den här självstudien.

ALTERNATIV 2: Skapa ett p8-certifikat som kan användas för tokenbaserad autentisering
  1. Anteckna följande information:

    • App-ID-prefix (team-ID)
    • Paket-ID
  2. När du är tillbaka i Certifikat, Identifierare & profiler klickar du på Nycklar.

    Observera

    Om du redan har en nyckel konfigurerad för APNS kan du återanvända p8-certifikatet som du laddade ned direkt efter att det skapades. I så fall kan du ignorera steg 3 till 5.

  3. + Klicka på knappen (eller knappen Skapa en nyckel) för att skapa en ny nyckel.

  4. Ange ett lämpligt nyckelnamnsvärde , kontrollera sedan alternativet Apple Push Notifications Service (APNS) och klicka sedan på Fortsätt följt av Registrera på nästa skärm.

  5. Klicka på Ladda ned och flytta sedan p8-filen (prefixet med AuthKey_) till en säker lokal katalog och klicka sedan på Klar.

    Observera

    Se till att hålla p8-filen på en säker plats (och spara en säkerhetskopia). När du har laddat ned nyckeln kan den inte laddas ned igen eftersom serverkopian tas bort.

  6. Nycklar klickar du på den nyckel som du skapade (eller en befintlig nyckel om du har valt att använda den i stället).

  7. Anteckna nyckel-ID-värdet .

  8. Öppna ditt p8-certifikat i ett lämpligt program, till exempel Visual Studio Code. Anteckna nyckelvärdet (mellan -----BEGIN PRIVATE KEY----- och -----END PRIVATE KEY-----).

    -----BEGIN PRIVAT NYCKEL-----
    <key_value>
    -----END PRIVAT NYCKEL-----

    Observera

    Det här är det tokenvärde som ska användas senare för att konfigurera Notification Hub.

I slutet av de här stegen bör du ha följande information för senare användning i Konfigurera din meddelandehubb med APNS-information:

  • Team-ID (se steg 1)
  • Paket-ID (se steg 1)
  • Nyckel-ID (se steg 7)
  • Tokenvärde (p8-nyckelvärde som hämtades i steg 8)

Skapa en etableringsprofil för appen

  1. Gå tillbaka till iOS-etableringsportalen, välj Certifikat, Identifierare & profiler, välj Profiler på den vänstra menyn och välj + sedan för att skapa en ny profil. Skärmen Registrera en ny etableringsprofil visas.

  2. Välj iOS-apputveckling under Utveckling som etableringsprofiltyp och välj sedan Fortsätt.

    Etableringsprofillista

  3. Välj sedan det app-ID som du skapade i listrutan App-ID och välj Fortsätt.

    Välj app-ID

  4. I fönstret Välj certifikat väljer du det utvecklingscertifikat som du använder för kodsignering och väljer Fortsätt.

    Observera

    Det här certifikatet är inte det push-certifikat som du skapade i föregående steg. Det här är ditt utvecklingscertifikat. Om det inte finns någon måste du skapa den eftersom det är en förutsättning för den här självstudien. Utvecklarcertifikat kan skapas i Apple Developer Portal, via Xcode eller i Visual Studio.

  5. Gå tillbaka till sidan Certifikat, Identifierare & profiler , välj Profiler på den vänstra menyn och välj + sedan för att skapa en ny profil. Skärmen Registrera en ny etableringsprofil visas.

  6. I fönstret Välj certifikat väljer du det utvecklingscertifikat som du skapade. Välj sedan Fortsätt.

  7. Välj sedan de enheter som ska användas för testning och välj Fortsätt.

  8. Välj slutligen ett namn för profilen i Etableringsprofilnamn och välj Generera.

    Välj ett etableringsprofilnamn

  9. När den nya etableringsprofilen har skapats väljer du Ladda ned. Kom ihåg den plats där den sparas.

  10. Bläddra till platsen för etableringsprofilen och dubbelklicka sedan på den för att installera den på utvecklingsdatorn.

Skapa en meddelandehubb

I det här avsnittet skapar du en meddelandehubb och konfigurerar autentisering med APNS. Du kan använda ett p12-pushcertifikat eller tokenbaserad autentisering. Om du vill använda en meddelandehubb som du redan har skapat kan du gå vidare till steg 5.

  1. Logga in på Azure.

  2. Klicka på Skapa en resurs, sök efter och välj Meddelandehubb och klicka sedan på Skapa.

  3. Uppdatera följande fält och klicka sedan på Skapa:

    GRUNDLÄGGANDE INFORMATION

    Prenumeration: Välj målprenumerationen i listrutan
    Resursgrupp: Skapa en ny resursgrupp (eller välj en befintlig)

    NAMNOMRÅDESINFORMATION

    Namnområde för notification hub: Ange ett globalt unikt namn för Notification Hub-namnområdet

    Observera

    Kontrollera att alternativet Skapa ny är markerat för det här fältet.

    INFORMATION OM MEDDELANDEHUBBEN

    Notification Hub: Ange ett namn för Notification Hub
    Plats: Välj en lämplig plats i listrutan
    Prisnivå: Behåll standardalternativet Kostnadsfri

    Observera

    Såvida du inte har nått det maximala antalet hubbar på den kostnadsfria nivån.

  4. När meddelandehubben har etablerats går du till den resursen.

  5. Gå till den nya meddelandehubben.

  6. Välj Åtkomstprinciper i listan (under HANTERA).

  7. Anteckna värdena för Principnamn tillsammans med deras motsvarande anslutningssträngvärden .

Konfigurera meddelandehubben med APNS-information

Under Notification Services väljer du Apple och följer sedan lämpliga steg baserat på den metod som du valde tidigare i avsnittet Skapa ett certifikat för Notification Hubs .

Observera

Använd endast produktion för programläge om du vill skicka push-meddelanden till användare som har köpt din app från butiken.

ALTERNATIV 1: Använda ett .p12-pushcertifikat

  1. Välj Certifikat.

  2. Välj filikonen.

  3. Välj den .p12-fil som du exporterade tidigare och välj sedan Öppna.

  4. Ange rätt lösenord om det behövs.

  5. Välj Sandbox-läge .

  6. Välj Spara.

ALTERNATIV 2: Använda tokenbaserad autentisering

  1. Välj Token.

  2. Ange följande värden som du hämtade tidigare:

    • Nyckel-ID
    • Paket-ID
    • Team-ID
    • Token
  3. Välj Sandbox.

  4. Välj Spara.

Konfigurera din meddelandehubb med FCM-information

  1. Välj Google (GCM/FCM) i avsnittet Inställningar på den vänstra menyn.
  2. Ange servernyckeln som du antecknade i Google Firebase-konsolen.
  3. Välj Spara i verktygsfältet.

Skapa ett ASP.NET Core webb-API-serverdelsprogram

I det här avsnittet skapar du ASP.NET Core webb-API-serverdelen för att hantera enhetsregistrering och skicka meddelanden till mobilappen Xamarin.Forms.

Skapa ett webbprojekt

  1. I Visual Studio väljer du Arkiv>Ny lösning.

  2. Välj .NET Core>App>ASP.NET Core>API>Nästa.

  3. I dialogrutan Konfigurera din nya ASP.NET Core Webb-API väljer du Målramverk för .NET Core 3.1.

  4. Ange PushDemoApi som projektnamn och välj sedan Skapa.

  5. Börja felsöka (Kommando + retur) för att testa den mallade appen.

    Observera

    Den mallade appen är konfigurerad för att använda WeatherForecastController som launchUrl. Detta anges i Egenskaper>launchSettings.json.

    Om du uppmanas att ange ett meddelande om ogiltigt utvecklingscertifikat:

    1. Klicka på Ja för att godkänna att du kör verktyget "dotnet dev-certs https" för att åtgärda detta. Verktyget "dotnet dev-certs https" uppmanar dig sedan att ange ett lösenord för certifikatet och lösenordet för nyckelringen.

    2. Klicka på Ja när du uppmanas att installera och lita på det nya certifikatet och ange sedan lösenordet för nyckelringen.

  6. Expandera mappen Controllers och ta sedan bort WeatherForecastController.cs.

  7. Ta bort WeatherForecast.cs.

  8. Konfigurera lokala konfigurationsvärden med verktyget Secret Manager. Att ta bort hemligheterna från lösningen säkerställer att de inte hamnar i källkontrollen. Öppna Terminal och gå sedan till katalogen för projektfilen och kör följande kommandon:

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

    Ersätt platshållarvärdena med ditt eget namn på meddelandehubben och anslutningssträng värden. Du antecknar dem i avsnittet Skapa en meddelandehubb . Annars kan du söka efter dem i Azure.

    NotificationHub:Name:
    Se Namn i Essentials-sammanfattningen överst i Översikt.

    NotificationHub:ConnectionString:
    Se DefaultFullSharedAccessSignature i Åtkomstprinciper

    Observera

    I produktionsscenarier kan du titta på alternativ som Azure KeyVault för att lagra anslutningssträng på ett säkert sätt. För enkelhetens skull läggs hemligheterna till i Azure App Service programinställningar.

Autentisera klienter med hjälp av en API-nyckel (valfritt)

API-nycklar är inte lika säkra som token, men räcker för den här självstudien. En API-nyckel kan enkelt konfigureras via ASP.NET Middleware.

  1. Lägg till API-nyckeln till de lokala konfigurationsvärdena.

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

    Observera

    Du bör ersätta platshållarvärdet med ditt eget och anteckna det.

  2. Kontroll + KlickaPushDemoApi-projektet , välj Ny mapp på menyn Lägg till och klicka sedan på Lägg till med autentisering som mappnamn.

  3. Kontroll + Klicka på mappen Autentisering och välj sedan Ny fil... på menyn Lägg till .

  4. Välj Allmän>tom klass, ange ApiKeyAuthOptions.cs som Namn och klicka sedan på Nytt och lägg till följande implementering.

    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. Lägg till ytterligare en tom klass i mappen Autentisering med namnet ApiKeyAuthHandler.cs och lägg sedan till följande implementering.

    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));
            }
        }
    }
    

    Observera

    En autentiseringshanterare är en typ som implementerar beteendet för ett schema, i det här fallet ett anpassat API-nyckelschema.

  6. Lägg till ytterligare en tom klass i mappen Autentisering med namnet ApiKeyAuthenticationBuilderExtensions.cs och lägg sedan till följande implementering.

    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);
            }
        }
    }
    

    Observera

    Den här tilläggsmetoden förenklar konfigurationskoden för mellanprogram i Startup.cs vilket gör den mer läsbar och allmänt enklare att följa.

  7. I Startup.cs uppdaterar du metoden ConfigureServices för att konfigurera API-nyckelautentiseringen under anropet till tjänsterna. AddControllers-metod .

    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. I Startup.cs uppdaterar du metoden Configure (Konfigurera ) för att anropa metoderna UseAuthentication och UseAuthorization-tillägget på appens IApplicationBuilder. Se till att dessa metoder anropas efter UseRouting och före appen. 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();
        });
    }
    

    Observera

    Anropa UseAuthentication registrerar mellanprogrammet som använder tidigare registrerade autentiseringsscheman (från ConfigureServices). Detta måste anropas innan mellanprogram som är beroende av att användare autentiseras.

Lägga till beroenden och konfigurera tjänster

ASP.NET Core stöder mönstret för beroendeinmatning (DI), vilket är en teknik för att uppnå inversion av kontroll (IoC) mellan klasser och deras beroenden.

Användning av meddelandehubben och Notification Hubs SDK för serverdelsåtgärder kapslas in i en tjänst. Tjänsten registreras och görs tillgänglig genom en lämplig abstraktion.

  1. Kontroll + Klicka på mappen Beroenden och välj sedan Hantera NuGet-paket....

  2. Sök efter Microsoft.Azure.NotificationHubs och kontrollera att det är markerat.

  3. Klicka på Lägg till paket och klicka sedan på Acceptera när du uppmanas att godkänna licensvillkoren.

  4. Kontroll + KlickaPushDemoApi-projektet , välj Ny mapp på Menyn Lägg till och klicka sedan på Lägg till med modeller som mappnamn.

  5. Kontroll + Klicka på mappen Modeller och välj sedan Ny fil... på menyn Lägg till .

  6. Välj Allmän>tom klass, ange PushTemplates.cs som Namn och klicka sedan på Nytt och lägg till följande implementering.

    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)\" }";
            }
        }
    }
    

    Observera

    Den här klassen innehåller de tokeniserade meddelandenyttolasterna för de allmänna och tysta meddelanden som krävs i det här scenariot. Nyttolaster definieras utanför installationen för att tillåta experimentering utan att behöva uppdatera befintliga installationer via tjänsten. Hantering av ändringar av installationer på det här sättet ligger utanför omfånget för den här självstudien. Överväg anpassade mallar för produktion.

  7. Lägg till ytterligare en tom klass i mappen Modeller med namnet DeviceInstallation.cs och lägg sedan till följande implementering.

    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. Lägg till ytterligare en tom klass i mappen Modeller med namnet NotificationRequest.cs och lägg sedan till följande implementering.

    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. Lägg till ytterligare en tom klass i mappen Modeller med namnet NotificationHubOptions.cs och lägg sedan till följande implementering.

    using System.ComponentModel.DataAnnotations;
    
    namespace PushDemoApi.Models
    {
        public class NotificationHubOptions
        {
            [Required]
            public string Name { get; set; }
    
            [Required]
            public string ConnectionString { get; set; }
        }
    }
    
  10. Lägg till en ny mapp i PushDemoApi-projektetmed namnet Tjänster.

  11. Lägg till ett tomt gränssnitt i mappen Tjänster med namnet INotificationService.cs och lägg sedan till följande implementering.

    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. Lägg till en tom klass i mappen Tjänster med namnet NotificationHubsService.cs och lägg sedan till följande kod för att implementera gränssnittet 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);
            }
        }
    }
    

    Observera

    Tagguttrycket som anges i SendTemplateNotificationAsync är begränsat till 20 taggar. Det är begränsat till 6 för de flesta operatorer, men uttrycket innehåller endast OR (||) i det här fallet. Om det finns fler än 20 taggar i begäran måste de delas upp i flera begäranden. Mer information finns i dokumentationen om routnings- och tagguttryck .

  13. I Startup.cs uppdaterar du metoden ConfigureServices för att lägga till NotificationHubsService som en singleton-implementering av 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();
    }
    

Skapa meddelande-API:et

  1. Kontroll + Klicka på mappen Controllers och välj sedan Ny fil... på menyn Lägg till .

  2. Välj ASP.NET Core>Webb-API-kontrollantklass, ange NotificationsController som Namn och klicka sedan på Ny.

    Observera

    Om du följer med Visual Studio 2019 väljer du mallen API-kontrollant med läs-/skrivåtgärder .

  3. Lägg till följande namnområden överst i filen.

    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. Uppdatera den mallade kontrollanten så att den härleds från ControllerBase och är dekorerad med attributet ApiController .

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

    Observera

    Basklassen Controller har stöd för vyer, men detta behövs inte i det här fallet, så ControllerBase kan användas i stället. Om du följer med Visual Studio 2019 kan du hoppa över det här steget.

  5. Om du väljer att slutföra avsnittet Autentisera klienter med hjälp av en API-nyckel bör du även dekorera NotificationsController med attributet Auktorisera .

    [Authorize]
    
  6. Uppdatera konstruktorn för att acceptera den registrerade instansen av INotificationService som ett argument och tilldela den till en skrivskyddad medlem.

    readonly INotificationService _notificationService;
    
    public NotificationsController(INotificationService notificationService)
    {
        _notificationService = notificationService;
    }
    
  7. I launchSettings.json (i mappen Egenskaper ) ändrar du launchUrl från weatherforecast till api/notifications så att den matchar url:en som anges i attributet RegistrationsControllerRoute .

  8. Börja felsöka (Kommando + retur) för att verifiera att appen fungerar med den nya NotificationsController och returnerar statusen 401 Obehörig .

    Observera

    Visual Studio kanske inte startar appen automatiskt i webbläsaren. Du kommer att använda Postman för att testa API:et från och med nu.

  9. På en ny Postman-flik ställer du in begäran på GET. Ange adressen nedan och ersätt platshållarprogrammetUrl <> med https applicationUrl som finns i Egenskaper>launchSettings.json.

    <applicationUrl>/api/notifications
    

    Observera

    ApplicationUrl ska vara förhttps://localhost:5001 standardprofilen. Om du använder IIS (standard i Visual Studio 2019 i Windows) bör du använda applicationUrl som anges i iisSettings-objektet i stället. Du får ett 404-svar om adressen är felaktig.

  10. Om du väljer att slutföra avsnittet Autentisera klienter med hjälp av en API-nyckel måste du konfigurera begärandehuvudena så att de inkluderar apikey-värdet .

    Nyckel Värde
    apikey <your_api_key>
  11. Klicka på knappen Skicka .

    Observera

    Du bör få statusen 200 OK med lite JSON-innehåll .

    Om du får en SSL-certifikatverifieringsvarning kan du ändra inställningen för SSL-certifikatverifiering för begäran Postman i inställningarna.

  12. Ersätt de mallade klassmetoderna i NotificationsController.cs med följande kod.

    [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();
    }
    

Skapa API-appen

Nu skapar du en API-app i Azure App Service för att vara värd för serverdelstjänsten.

  1. Logga in på Azure Portal.

  2. Klicka på Skapa en resurs, sök efter och välj API-app och klicka sedan på Skapa.

  3. Uppdatera följande fält och klicka sedan på Skapa.

    Appnamn:
    Ange ett globalt unikt namn för API-appen

    Prenumeration:
    Välj samma målprenumeration som du skapade meddelandehubben i.

    Resursgrupp:
    Välj samma resursgrupp som du skapade meddelandehubben i.

    App Service plan/plats:
    Skapa en ny App Service plan

    Observera

    Ändra från standardalternativet till en plan som innehåller SSL-stöd . Annars måste du vidta lämpliga åtgärder när du arbetar med mobilappen för att förhindra att HTTP-begäranden blockeras.

    Application Insights:
    Behåll det föreslagna alternativet (en ny resurs skapas med det namnet) eller välj en befintlig resurs.

  4. När API-appen har etablerats går du till den resursen.

  5. Anteckna URL-egenskapen i Essentials-sammanfattningen överst i översikten. Den här URL:en är din serverdelsslutpunkt som kommer att användas senare i den här självstudien.

    Observera

    URL:en använder API-appnamnet som du angav tidigare, med formatet https://<app_name>.azurewebsites.net.

  6. Välj Konfiguration i listan (under Inställningar).

  7. För var och en av inställningarna nedan klickar du på Ny programinställning för att ange Namn och ett värde och klickar sedan på OK.

    Namn Värde
    Authentication:ApiKey <api_key_value>
    NotificationHub:Name <hub_name_value>
    NotificationHub:ConnectionString <hub_connection_string_value>

    Observera

    Det här är samma inställningar som du definierade tidigare i användarinställningarna. Du bör kunna kopiera över dessa. Inställningen Authentication:ApiKey krävs bara om du väljer att slutföra avsnittet Autentisera klienter med hjälp av en API-nyckel . I produktionsscenarier kan du titta på alternativ som Azure KeyVault. Dessa har lagts till som programinställningar för enkelhetens skull i det här fallet.

  8. När alla programinställningar har lagts till klickar du på Spara och sedan på Fortsätt.

Publicera serverdelstjänsten

Därefter distribuerar du appen till API-appen för att göra den tillgänglig från alla enheter.

Observera

Följande steg är specifika för Visual Studio för Mac. Om du följer visual studio 2019 i Windows är publiceringsflödet annorlunda. Se Publicera till Azure App Service i Windows.

  1. Ändra konfigurationen från Debug till Release om du inte redan har gjort det.

  2. Kontroll + Klicka påPushDemoApi-projektet och välj sedan Publicera till Azure... på menyn Publicera .

  3. Följ autentiseringsflödet om du uppmanas att göra det. Använd det konto som du använde i föregående skapa AVSNITTET API-app .

  4. Välj den Azure App Service API-app som du skapade tidigare i listan som publiceringsmål och klicka sedan på Publicera.

När du har slutfört guiden publiceras appen till Azure och appen öppnas. Anteckna URL: en om du inte redan har gjort det. Den här URL:en är din serverdelsslutpunkt som används senare i den här självstudien.

Verifiera det publicerade API:et

  1. I Postman öppnar du en ny flik, ställer in begäran på PUT och anger adressen nedan. Ersätt platshållaren med den basadress som du antecknar i föregående publicera avsnittet serverdelstjänst .

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

    Observera

    Basadressen ska vara i formatet https://<app_name>.azurewebsites.net/

  2. Om du väljer att slutföra avsnittet Autentisera klienter med hjälp av en API-nyckel måste du konfigurera begärandehuvudena så att de inkluderar apikey-värdet .

    Nyckel Värde
    apikey <your_api_key>
  3. Välj raw-alternativet för brödtexten och välj sedan JSON i listan med formatalternativ och ta sedan med lite platshållar-JSON-innehåll :

    {}
    
  4. Klicka på Skicka.

    Observera

    Du bör få statusen 422 UnprocessableEntity från tjänsten.

  5. Utför steg 1–4 igen, men den här gången anger du slutpunkten för begäranden för att verifiera att du får svaret 400 Felaktig begäran .

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

Observera

Det går ännu inte att testa API:et med giltiga begärandedata eftersom det kräver plattformsspecifik information från klientmobilappen.

Skapa ett plattformsoberoende Xamarin.Forms-program

I det här avsnittet skapar du ett Xamarin.Forms-mobilprogram som implementerar push-meddelanden på ett plattformsoberoende sätt.

Det gör att du kan registrera och avregistrera från en meddelandehubb via serverdelstjänsten som du skapade.

En avisering visas när en åtgärd anges och appen är i förgrunden. Annars visas meddelanden i meddelandecentret.

Observera

Du utför vanligtvis registreringsåtgärderna (och avregistreringen) under lämplig punkt i programmets livscykel (eller som en del av din första körningsupplevelse kanske) utan explicita indata från användarregistret/avregistrerar. Det här exemplet kräver dock explicita användarindata så att den här funktionen kan utforskas och testas enklare.

Skapa Xamarin.Forms-lösningen

  1. I Visual Studio skapar du en ny Xamarin.Forms-lösning med tom formulärapp som mall och anger PushDemo som projektnamn.

    Observera

    I dialogrutan Konfigurera din tomma formulärapp kontrollerar du att organisationsidentifieraren matchar det värde som du använde tidigare och att både Android - och iOS-målen är markerade.

  2. Kontroll + KlickaPushDemo-lösningen och välj sedan Uppdatera NuGet-paket.

  3. Kontroll + KlickaPushDemo-lösningen och välj sedan Hantera NuGet-paket...

  4. Sök efter Newtonsoft.Json och kontrollera att det är markerat.

  5. Klicka på Lägg till paket och klicka sedan på Acceptera när du uppmanas att godkänna licensvillkoren.

  6. Skapa och kör appen på varje målplattform (Kommando + retur) för att testa att den mallade appen körs på dina enheter.

Implementera plattformsoberoende komponenter

  1. Kontroll + KlickaPushDemo-projektet , välj Ny mappMenyn Lägg till och klicka sedan på Lägg till med modeller som mappnamn.

  2. Kontroll + Klicka på mappen Modeller och välj sedan Ny fil... på menyn Lägg till .

  3. Välj Allmän>tom klass, ange DeviceInstallation.cs och lägg sedan till följande implementering.

    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. Lägg till en tom uppräkning i mappen Modeller med namnet PushDemoAction.cs med följande implementering.

    namespace PushDemo.Models
    {
        public enum PushDemoAction
        {
            ActionA,
            ActionB
        }
    }
    
  5. Lägg till en ny mapp i PushDemo-projektetmed namnet Tjänster och lägg sedan till en tom klass i mappen ServiceContainer.cs med följande implementering.

    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}'");
               }
           }
       }
    }
    

    Observera

    Det här är en trimmad version av klassen ServiceContainer från XamCAT-lagringsplatsen . Den används som en lätt IoC-container (inversion av kontroll).

  6. Lägg till ett tomt gränssnitt i mappen Tjänster med namnet IDeviceInstallationService.cs och lägg sedan till följande kod.

    using PushDemo.Models;
    
    namespace PushDemo.Services
    {
        public interface IDeviceInstallationService
        {
            string Token { get; set; }
            bool NotificationsSupported { get; }
            string GetDeviceId();
            DeviceInstallation GetDeviceInstallation(params string[] tags);
        }
    }
    

    Observera

    Det här gränssnittet implementeras och startas av varje mål senare för att tillhandahålla plattformsspecifika funktioner och enhetinstallationsinformation som krävs av serverdelstjänsten.

  7. Lägg till ytterligare ett tomt gränssnitt i mappen Tjänster med namnet INotificationRegistrationService.cs och lägg sedan till följande kod.

    using System.Threading.Tasks;
    
    namespace PushDemo.Services
    {
        public interface INotificationRegistrationService
        {
            Task DeregisterDeviceAsync();
            Task RegisterDeviceAsync(params string[] tags);
            Task RefreshRegistrationAsync();
        }
    }
    

    Observera

    Detta hanterar interaktionen mellan klienten och serverdelstjänsten.

  8. Lägg till ytterligare ett tomt gränssnitt i mappen Tjänster med namnet INotificationActionService.cs och lägg sedan till följande kod.

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

    Observera

    Detta används som en enkel mekanism för att centralisera hanteringen av meddelandeåtgärder.

  9. Lägg till ett tomt gränssnitt i mappen Tjänster med namnet IPushDemoNotificationActionService.cs som härleds från INotificationActionService med följande implementering.

    using System;
    using PushDemo.Models;
    
    namespace PushDemo.Services
    {
        public interface IPushDemoNotificationActionService : INotificationActionService
        {
            event EventHandler<PushDemoAction> ActionTriggered;
        }
    }
    

    Observera

    Den här typen är specifik för PushDemo-programmet och använder PushDemoAction-uppräkningen för att identifiera den åtgärd som utlöses på ett starkt skrivet sätt.

  10. Lägg till en tom klass i mappen Tjänster med namnet NotificationRegistrationService.cs implementera INotificationRegistrationService med följande kod.

    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();
            }
        }
    }
    

    Observera

    Argumentet apiKey krävs bara om du väljer att slutföra avsnittet Autentisera klienter med hjälp av en API-nyckel .

  11. Lägg till en tom klass i mappen Tjänster med namnet PushDemoNotificationActionService.cs implementera IPushDemoNotificationActionService med följande kod.

    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. Lägg till en tom klass i PushDemo-projektetmed namnet Config.cs med följande implementering.

    namespace PushDemo
    {
        public static partial class Config
        {
            public static string ApiKey = "API_KEY";
            public static string BackendServiceEndpoint = "BACKEND_SERVICE_ENDPOINT";
        }
    }
    

    Observera

    Detta används som ett enkelt sätt att hålla hemligheter utom källkontroll. Du kan ersätta dessa värden som en del av en automatiserad version eller åsidosätta dem med hjälp av en lokal partiell klass. Du kommer att göra detta i nästa steg.

    Fältet ApiKey krävs bara om du väljer att slutföra avsnittet Autentisera klienter med hjälp av en API-nyckel .

  13. Lägg till ytterligare en tom klass i PushDemo-projektet den här gången med namnet Config.local_secrets.cs med följande implementering.

    namespace PushDemo
    {
        public static partial class Config
        {
            static Config()
            {
                ApiKey = "<your_api_key>";
                BackendServiceEndpoint = "<your_api_app_url>";
            }
        }
    }
    

    Observera

    Ersätt platshållarvärdena med dina egna. Du bör ha antecknat dessa när du skapade serverdelstjänsten. URL:en för API-appen ska vara https://<api_app_name>.azurewebsites.net/. Kom ihåg att lägga *.local_secrets.* till i gitignore-filen för att undvika att checka in den här filen.

    Fältet ApiKey krävs bara om du väljer att slutföra avsnittet Autentisera klienter med hjälp av en API-nyckel .

  14. Lägg till en tom klass i PushDemo-projektetmed namnet Bootstrap.cs med följande implementering.

    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));
            }
        }
    }
    

    Observera

    Metoden Begin anropas av varje plattform när appen startar och skickar in en plattformsspecifik implementering av IDeviceInstallationService.

    Konstruktorargumentet NotificationRegistrationServiceapiKey krävs bara om du väljer att slutföra avsnittet Authenticate clients using an API Key (Autentisera klienter med hjälp av en API-nyckel ).

Implementera det plattformsoberoende användargränssnittet

  1. I PushDemo-projektet öppnar du MainPage.xaml och ersätter StackLayout-kontrollen med följande.

    <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. Nu i MainPage.xaml.cs lägger du till ett skrivskyddat säkerhetskopieringsfält för att lagra en referens till implementeringen av INotificationRegistrationService .

    readonly INotificationRegistrationService _notificationRegistrationService;
    
  3. I MainPage-konstruktorn löser du implementeringen av INotificationRegistrationService med hjälp av ServiceContainer och tilldelar den till backfältet notificationRegistrationService .

    public MainPage()
    {
        InitializeComponent();
    
        _notificationRegistrationService =
            ServiceContainer.Resolve<INotificationRegistrationService>();
    }
    
  4. Implementera händelsehanterarna för knapparna RegisterButton och DeregisterButtonKlickade på händelser som anropar motsvarande registerderegistermetoder/.

    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. I App.xaml.cs kontrollerar du att följande namnrymder refereras till.

    using PushDemo.Models;
    using PushDemo.Services;
    using Xamarin.Essentials;
    using Xamarin.Forms;
    
  6. Implementera händelsehanteraren för händelsen 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. I appkonstruktorn löser du IPushNotificationActionService-implementeringen med hjälp av ServiceContainer och prenumererar på händelsen IPushDemoNotificationActionServiceActionTriggered.

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

    Observera

    Detta är helt enkelt för att demonstrera mottagandet och spridningen av push-meddelandeåtgärder. Normalt hanteras dessa tyst, till exempel genom att navigera till en viss vy eller uppdatera vissa data i stället för att visa en avisering via rotsidan, MainPage i det här fallet.

Konfigurera det interna Android-projektet för push-meddelanden

Verifiera paketnamn och behörigheter

  1. I PushDemo.Android öppnar du Projektalternativ och Sedan Android-program från avsnittet Skapa .

  2. Kontrollera att paketnamnet matchar det värde som du använde i Firebase ConsolePushDemo-projektet . Paketnamnet var i formatet com.<organization>.pushdemo.

  3. Ange Lägsta Android-version till Android 8.0 (API-nivå 26) och Android-målversionen till den senaste API-nivån.

    Observera

    Endast de enheter som kör API-nivå 26 och senare stöds i den här självstudien, men du kan utöka den för att stödja enheter som kör äldre versioner.

  4. Kontrollera att Internet - och READ_PHONE_STATE-behörigheter är aktiverade under Nödvändiga behörigheter.

  5. Klicka på OK

Lägg till Xamarin Google Play Services-bas- och Xamarin.Firebase.Messaging-paket

  1. I PushDemo.Androidkontrollerar + du Klicka på mappen Paket och välj sedan Hantera NuGet-paket....

  2. Sök efter Xamarin.GooglePlayServices.Base (inte Källare) och kontrollera att den är markerad.

  3. Sök efter Xamarin.Firebase.Messaging och kontrollera att det är markerat.

  4. Klicka på Lägg till paket och klicka sedan på Acceptera när du uppmanas att godkänna licensvillkoren.

Lägg till Google Services JSON-filen

  1. Kontroll + KlickaPushDemo.Android projektet och välj sedan Befintlig fil... på menyn Lägg till .

  2. Välj den google-services.json fil som du laddade ned tidigare när du konfigurerade PushDemo-projektet i Firebase-konsolen och klicka sedan på Öppna.

  3. När du uppmanas till det väljer du att Kopiera filen till katalogen.

  4. Kontroll + Klickafilen google-services.json inifrån PushDemo.Android projektet och kontrollera sedan att GoogleServicesJson har angetts som byggåtgärd.

Hantera push-meddelanden för Android

  1. Kontroll + KlickaPushDemo.Android projektet, välj Ny mapp på menyn Lägg till och klicka sedan på Lägg till med tjänster som mappnamn.

  2. Kontroll + Klicka på mappen Tjänster och välj sedan Ny fil... på menyn Lägg till .

  3. Välj Allmän>tom klass, ange DeviceInstallationService.cs som Namn och klicka sedan på Nytt och lägg till följande implementering.

    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";
            }
        }
    }
    

    Observera

    Den här klassen tillhandahåller ett unikt ID (med Secure.AndroidId) som en del av registreringsnyttolasten för meddelandehubben.

  4. Lägg till ytterligare en tom klass i mappen Tjänster med namnet PushNotificationFirebaseMessagingService.cs och lägg sedan till följande implementering.

    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. I MainActivity.cs kontrollerar du att följande namnområden har lagts till överst i filen.

    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. I MainActivity.cs anger du LaunchMode till SingleTop så att MainActivity inte skapas igen när den öppnas.

    [Activity(
        Label = "PushDemo",
        LaunchMode = LaunchMode.SingleTop,
        Icon = "@mipmap/icon",
        Theme = "@style/MainTheme",
        MainLauncher = true,
        ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
    
  7. Lägg till privata egenskaper och motsvarande bakgrundsfält för att lagra en referens till implementeringarna IPushNotificationActionService och IDeviceInstallationService .

    IPushDemoNotificationActionService _notificationActionService;
    IDeviceInstallationService _deviceInstallationService;
    
    IPushDemoNotificationActionService NotificationActionService
        => _notificationActionService ??
            (_notificationActionService =
            ServiceContainer.Resolve<IPushDemoNotificationActionService>());
    
    IDeviceInstallationService DeviceInstallationService
        => _deviceInstallationService ??
            (_deviceInstallationService =
            ServiceContainer.Resolve<IDeviceInstallationService>());
    
  8. Implementera gränssnittet IOnSuccessListener för att hämta och lagra Firebase-token .

    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. Lägg till en ny metod med namnet ProcessNotificationActions som kontrollerar om en viss avsikt har ett extra värde med namnet action. Villkorlig utlösa den åtgärden med IPushDemoNotificationActionService-implementeringen .

    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. Åsidosätt metoden OnNewIntent för att anropa metoden ProcessNotificationActions .

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

    Observera

    Eftersom LaunchMode för aktiviteten har angetts till SingleTop skickas en avsikt till den befintliga aktivitetsinstansen via metoden OnNewIntent i stället för metoden OnCreate . Därför måste du hantera en inkommande avsikt i både OnCreate - och OnNewIntent-metoderna .

  11. Uppdatera oncreate-metoden för att anropa Bootstrap.Begin direkt efter anropet till att base.OnCreate skicka den plattformsspecifika implementeringen av IDeviceInstallationService.

    Bootstrap.Begin(() => new DeviceInstallationService());
    
  12. I samma metod anropar du villkorligt GetInstanceIdFirebaseApp-instansen direkt efter anropet till och lägger till Bootstrap.BeginMainActivity som IOnSuccessListener.

    if (DeviceInstallationService.NotificationsSupported)
    {
        FirebaseInstanceId.GetInstance(Firebase.FirebaseApp.Instance)
            .GetInstanceId()
            .AddOnSuccessListener(this);
    }
    
  13. Fortfarande i OnCreateanropar du ProcessNotificationActions omedelbart efter anropet till att LoadApplication skicka den aktuella avsikten.

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

Observera

Du måste registrera om appen varje gång du kör den och stoppa den från en felsökningssession för att fortsätta ta emot push-meddelanden.

Konfigurera det interna iOS-projektet för push-meddelanden

Konfigurera Info.plist och Entitlements.plist

  1. Se till att du har loggat in på ditt Apple Developer-konto i Visual Studio-inställningar>...>Publicerande>Apple Developer-konton och lämplig certifikat- och etableringsprofil har laddats ned. Du bör ha skapat dessa tillgångar som en del av föregående steg.

  2. Öppna Info.plist i PushDemo.iOS och se till att BundleIdentifier matchar värdet som användes för respektive etableringsprofil i Apple Developer Portal. BundleIdentifier hade formatet com.<organization>.PushDemo.

  3. I samma fil anger du Lägsta systemversion till 13.0.

    Observera

    Endast de enheter som kör iOS 13.0 och senare stöds i den här självstudien, men du kan utöka den för att stödja enheter som kör äldre versioner.

  4. Öppna projektalternativen för PushDemo.iOS (dubbelklicka på projektet).

  5. Under Skapa > iOS-paketsignering i Projektalternativ kontrollerar du att ditt utvecklarkonto har valts under Team. Kontrollera sedan att "Hantera signering automatiskt" är markerat och att signeringscertifikatet och etableringsprofilen väljs automatiskt.

    Observera

    Om signeringscertifikatet och etableringsprofilen inte har valts automatiskt väljer du Manuell etablering och klickar sedan på Alternativ för paketsignering. Se till att ditt team har valts för signeringsidentitet och att din PushDemo-specifika etableringsprofil har valts för Etableringsprofil för både felsöknings- och versionskonfigurationer som säkerställer att iPhone har valts för plattformen i båda fallen.

  6. Öppna Entitlements.plist i PushDemo.iOS och kontrollera att Aktivera push-meddelanden är markerat när det visas på fliken Berättiganden. Kontrollera sedan att INSTÄLLNINGEN APS-miljö är inställd på utveckling när den visas på fliken Källa.

Hantera push-meddelanden för iOS

  1. Kontroll + KlickaPushDemo.iOS-projektet , välj Ny mapp på menyn Lägg till och klicka sedan på Lägg till med tjänster som mappnamn.

  2. Kontroll + Klicka på mappen Tjänster och välj sedan Ny fil... på menyn Lägg till .

  3. Välj Allmän>tom klass, ange DeviceInstallationService.cs som Namn och klicka sedan på Nytt och lägg till följande implementering.

    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";
            }
        }
    }
    

    Observera

    Den här klassen ger ett unikt ID (med värdet UIDevice.IdentifierForVendor ) och registreringsnyttolasten för meddelandehubben.

  4. Lägg till en ny mapp i PushDemo.iOS-projektetmed namnet Extensions och lägg sedan till en tom klass i mappen som heter NSDataExtensions.cs med följande implementering.

    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. I AppDelegate.cs kontrollerar du att följande namnområden har lagts till överst i filen.

    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. Lägg till privata egenskaper och deras respektive bakgrundsfält för att lagra en referens till implementeringarna IPushDemoNotificationActionService, INotificationRegistrationService och 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. Lägg till metoden RegisterForRemoteNotifications för att registrera inställningar för användarmeddelanden och sedan för fjärrmeddelanden med 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. Lägg till metoden CompleteRegistrationAsync för att ange egenskapsvärdet IDeviceInstallationService.Token . Uppdatera registreringen och cachelagras enhetstoken om den har uppdaterats sedan den senast lagrades.

    Task CompleteRegistrationAsync(NSData deviceToken)
    {
        DeviceInstallationService.Token = deviceToken.ToHexString();
        return NotificationRegistrationService.RefreshRegistrationAsync();
    }
    
  9. Lägg till metoden ProcessNotificationActions för bearbetning av NSDictionary-meddelandedata och anropa NotificationActionService.TriggerAction villkorligt.

    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. Åsidosätt metoden RegisteredForRemoteNotifications som skickar argumentet deviceToken till metoden CompleteRegistrationAsync .

    public override void RegisteredForRemoteNotifications(
        UIApplication application,
        NSData deviceToken)
        => CompleteRegistrationAsync(deviceToken).ContinueWith((task)
            => { if (task.IsFaulted) throw task.Exception; });
    
  11. Åsidosätt metoden ReceivedRemoteNotification som skickar userInfo-argumentet till metoden ProcessNotificationActions .

    public override void ReceivedRemoteNotification(
        UIApplication application,
        NSDictionary userInfo)
        => ProcessNotificationActions(userInfo);
    
  12. Åsidosätt metoden FailedToRegisterForRemoteNotifications för att logga felet.

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

    Observera

    Det här är i högsta grad en platshållare. Du vill implementera korrekt loggning och felhantering för produktionsscenarier.

  13. Uppdatera metoden FinishedLaunching för att anropa Bootstrap.Begin direkt efter anropet till att Forms.Init skicka den plattformsspecifika implementeringen av IDeviceInstallationService.

    Bootstrap.Begin(() => new DeviceInstallationService());
    
  14. I samma metod begär du auktorisering villkorligt och registrerar dig för fjärrmeddelanden omedelbart efter Bootstrap.Begin.

    if (DeviceInstallationService.NotificationsSupported)
    {
        UNUserNotificationCenter.Current.RequestAuthorization(
                UNAuthorizationOptions.Alert |
                UNAuthorizationOptions.Badge |
                UNAuthorizationOptions.Sound,
                (approvalGranted, error) =>
                {
                    if (approvalGranted && error == null)
                        RegisterForRemoteNotifications();
                });
    }
    
  15. Fortfarande i FinishedLaunchinganropar du ProcessNotificationActions omedelbart efter anropet till LoadApplication om alternativargumentet innehåller UIApplication.LaunchOptionsRemoteNotificationKey som skickar det resulterande userInfo-objektet .

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

Testa lösningen

Nu kan du testa att skicka meddelanden via serverdelstjänsten.

Skicka ett testmeddelande

  1. Öppna en ny flik i Postman.

  2. Ställ in begäran på POST och ange följande adress:

    https://<app_name>.azurewebsites.net/api/notifications/requests
    
  3. Om du väljer att slutföra avsnittet Autentisera klienter med hjälp av en API-nyckel måste du konfigurera begärandehuvudena så att de inkluderar apikey-värdet .

    Nyckel Värde
    apikey <your_api_key>
  4. Välj alternativet raw för Brödtext, välj sedan JSON i listan med formatalternativ och ta sedan med något platshållar-JSON-innehåll :

    {
        "text": "Message from Postman!",
        "action": "action_a"
    }
    
  5. Välj knappen Kod , som finns under knappen Spara längst upp till höger i fönstret. Begäran bör se ut ungefär som i följande exempel när den visas för HTML (beroende på om du har inkluderat ett apikey-huvud ):

    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. Kör PushDemo-programmet på en eller båda målplattformarna (Android och iOS).

    Observera

    Om du testar på Android kontrollerar du att du inte kör i Felsökning, eller om appen har distribuerats genom att köra programmet, framtvinga stängning av appen och starta den igen från startprogrammet.

  7. Tryck på knappen Registrera i PushDemo-appen.

  8. I Postman stänger du fönstret Generera kodfragment (om du inte redan har gjort det) och klickar sedan på knappen Skicka .

  9. Kontrollera att du får ett 200 OK-svar i Postman och att aviseringen visas i appen som visar åtgärden ActionA mottagen.

  10. Stäng PushDemo-appen och klicka sedan på knappen Skicka igen i Postman.

  11. Kontrollera att du får ett 200 OK-svar i Postman igen. Kontrollera att ett meddelande visas i meddelandefältet för PushDemo-appen med rätt meddelande.

  12. Tryck på meddelandet för att bekräfta att appen öppnas och att åtgärden ActionA har tagit emot aviseringen.

  13. Gå tillbaka till Postman och ändra föregående begärandetext för att skicka ett tyst meddelande som anger action_b i stället för action_a för åtgärdsvärdet .

    {
        "action": "action_b",
        "silent": true
    }
    
  14. När appen fortfarande är öppen klickar du på knappen Skicka i Postman.

  15. Kontrollera att du får ett 200 OK-svar i Postman och att aviseringen visas i appen som visar åtgärdB-åtgärden som tagits emot i stället för den ÅtgärdA-åtgärd som tagits emot.

  16. Stäng PushDemo-appen och klicka sedan på knappen Skicka igen i Postman.

  17. Kontrollera att du får ett 200 OK-svar i Postman och att det tysta meddelandet inte visas i meddelandefältet.

Felsökning

Inget svar från serverdelstjänsten

När du testar lokalt kontrollerar du att serverdelstjänsten körs och använder rätt port.

Om du testar mot Azure API-appen kontrollerar du att tjänsten körs och har distribuerats och har startats utan fel.

Kontrollera att du har angett basadressen korrekt i Postman eller i mobilappkonfigurationen vid testning via klienten. Basadressen bör vara https://<api_name>.azurewebsites.net/ eller https://localhost:5001/ när den testas lokalt.

Får inte meddelanden på Android när du har startat eller stoppat en felsökningssession

Kontrollera att du registrerar dig igen när du har startat eller stoppat en felsökningssession. Felsökningsprogrammet gör att en ny Firebase-token genereras. Installationen av meddelandehubben måste också uppdateras.

Ta emot en 401-statuskod från serverdelstjänsten

Kontrollera att du ställer in apikey-begärandehuvudet och att det här värdet matchar det som du har konfigurerat för serverdelstjänsten.

Om du får det här felet när du testar lokalt kontrollerar du det nyckelvärde som du definierade i klientkonfigurationen och matchar användarinställningsvärdet Authentication:ApiKey som används av API:et.

Om du testar med en API-app kontrollerar du att nyckelvärdet i klientkonfigurationsfilen matchar den autentisering:ApiKey-programinställning som du använder i API-appen.

Observera

Om du har skapat eller ändrat den här inställningen när du har distribuerat serverdelstjänsten måste du starta om tjänsten för att den ska börja gälla.

Om du väljer att inte slutföra avsnittet Autentisera klienter med hjälp av en API-nyckel kontrollerar du att du inte har tillämpat attributet Auktorisera på klassen NotificationsController .

Ta emot en 404-statuskod från serverdelstjänsten

Kontrollera att metoden för slutpunkts- och HTTP-begäran är korrekt. Slutpunkterna bör till exempel vara:

  • [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

Eller när du testar lokalt:

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

När du anger basadressen i klientappen ska du se till att den slutar med en /. Basadressen bör vara https://<api_name>.azurewebsites.net/ eller https://localhost:5001/ när den testas lokalt.

Det går inte att registrera och felmeddelandet för meddelandehubben visas

Kontrollera att testenheten har nätverksanslutning. Bestäm sedan statuskoden för Http-svar genom att ange en brytpunkt för att granska egenskapsvärdet StatusCode i HttpResponse.

Granska föregående felsökningsförslag där det är tillämpligt baserat på statuskoden.

Ange en brytpunkt på de rader som returnerar dessa specifika statuskoder för respektive API. Försök sedan anropa serverdelstjänsten när du felsöker lokalt.

Kontrollera att serverdelstjänsten fungerar som förväntat via Postman med lämplig nyttolast. Använd den faktiska nyttolasten som skapats av klientkoden för plattformen i fråga.

Granska de plattformsspecifika konfigurationsavsnitten för att se till att inga steg har missats. Kontrollera att lämpliga värden matchas för installation id och token variabler för lämplig plattform.

Det går inte att lösa ett ID för enhetens felmeddelande visas

Granska de plattformsspecifika konfigurationsavsnitten för att se till att inga steg har missats.

Nästa steg

Nu bör du ha en grundläggande Xamarin.Forms-app ansluten till en meddelandehubb via en serverdelstjänst och kan skicka och ta emot meddelanden.

Du behöver förmodligen anpassa exemplet som används i den här självstudien för att passa ditt eget scenario. Vi rekommenderar också att du implementerar mer robust felhantering, omprövningslogik och loggning.

Visual Studio App Center kan snabbt införlivas i mobilappar som tillhandahåller analys och diagnostik för felsökning.