Kurz: Odesílání nabízených oznámení konkrétním uživatelům pomocí Azure Notification Hubs

V tomto kurzu se dozvíte, jak pomocí služby Azure Notification Hubs posílat nabízená oznámení konkrétním uživatelům aplikace na konkrétním zařízení. Back-end ASP.NET WebAPI se používá k ověřování klientů a generování oznámení, jak je znázorněno v tématu s pokyny k registraci z back-endu vaší aplikace.

V tomto kurzu provedete následující kroky:

  • Vytvoření projektu WebAPI
  • Ověřování klientů v back-endu WebAPI
  • Registrace k oznámením pomocí back-endu WebAPI
  • Odesílání oznámení z back-endu WebAPI
  • Publikování nového back-endu WebAPI
  • Úprava aplikace pro iOS
  • Testování aplikace

Požadavky

V tomto kurzu se předpokládá, že jste vytvořili a nakonfigurovali centrum oznámení, jak je popsáno v tématu Odesílání nabízených oznámení do aplikací pro iOS pomocí Azure Notification Hubs. Tento kurz je také předpokladem pro kurz zabezpečeného nabízení (iOS). Pokud chcete jako back-endovou službu používat Mobile Apps, přečtěte si článek Začínáme s nabízenou oznámením Mobile Apps.

Vytvoření projektu WebAPI

Následující části popisují vytvoření nového back-endu ASP.NET WebAPI. Tento proces má tři hlavní účely:

  • Ověřování klientů: Přidáte popisovač zprávy, který bude ověřovat požadavky klientů a přiřazovat uživatele k požadavkům.
  • Registrace k oznámením pomocí back-endu WebAPI: Přidáte kontroler, který bude zpracovávat nové registrace klientských zařízení k přijímání oznámení. Ověřené uživatelské jméno se automaticky přidá do registrace jako značka.
  • Odesílání oznámení klientům: Přidáte kontroler, který uživatelům umožní aktivovat zabezpečené nabízení do zařízení a klientů přidružených ke značce.

Pomocí následujících akcí vytvořte nový back-end webového rozhraní API ASP.NET Core 6.0:

Pokud to chcete zkontrolovat, spusťte sadu Visual Studio. V nabídce Nástroje vyberte Rozšíření a aktualizace. Vyhledejte Správce balíčků NuGet ve vaší verzi sady Visual Studio a ujistěte se, že máte nejnovější verzi. Pokud vaše verze není nejnovější verzí, odinstalujte ji a pak znovu nainstalujte Správce balíčků NuGet.

Snímek obrazovky s dialogovým oknem Rozšíření a Aktualizace se zvýrazněnou možností Spravovat balíček NuGet pro balíček Visual Studios

Poznámka

Ujistěte se, že jste nainstalovali sadu Visual Studio Azure SDK pro nasazení webu.

  1. Spusťte sadu Visual Studio nebo Visual Studio Express.

  2. Vyberte Průzkumník serveru a přihlaste se ke svému účtu Azure. Abyste mohli vytvářet prostředky webu, musíte být přihlášení.

  3. V nabídce Soubor sady Visual Studio vyberte Nový>projekt.

  4. Do vyhledávacího pole zadejte Webové rozhraní API .

  5. Vyberte šablonu projektu webového rozhraní API ASP.NET Core a vyberte Další.

  6. V dialogovém okně Konfigurace nového projektu pojmenujte projekt AppBackend a vyberte Další.

  7. V dialogovém okně Další informace :

    • Ověřte, že je rozhraní .NET 6.0 (dlouhodobá podpora).
    • Ověřte, že je zaškrtnuté políčko Používat kontrolery (zrušte zaškrtnutí, pokud chcete použít minimální počet rozhraní API).
    • Zrušte zaškrtnutí políčka Povolit podporu OpenAPI.
    • Vyberte Vytvořit.

Odebrání souborů šablon WeatherForecast

  1. Z nového projektu AppBackend odeberte ukázkové soubory WeatherForecast.cs a Controllers/WeatherForecastController.cs.
  2. Otevřete Properties\launchSettings.json.
  3. Změňte vlastnosti launchUrl z weatherforcast na appbackend.

V okně Konfigurovat webovou aplikaci Microsoft Azure vyberte předplatné a pak v seznamu Plán služby App Service proveďte jednu z následujících akcí:

  • Vyberte plán Azure App Service, který jste už vytvořili.
  • Vyberte možnost Vytvořit nový plán App Service a pak tento plán vytvořte.

Pro účely tohoto kurzu nepotřebujete databázi. Jakmile vyberete plán služby App Service, výběrem OK vytvořte projekt.

Okno Konfigurovat webovou aplikaci Microsoft Azure

Pokud tuto stránku pro konfiguraci plánu služby App Service nevidíte, pokračujte kurzem. Můžete ho nakonfigurovat při pozdějším publikování aplikace.

Ověřování klientů v back-endu WebAPI

V této části vytvoříte pro nový back-end novou třídu popisovače zprávy AuthenticationTestHandler. Tato třída je odvozená od třídy DelegatingHandler a přidaná jako popisovač zprávy, aby mohla zpracovávat všechny požadavky přicházející na back-end.

  1. V Průzkumníku řešení klikněte pravým tlačítkem na projekt AppBackend, vyberte Přidat a pak vyberte Třída.

  2. Pojmenujte novou třídu AuthenticationTestHandler.cs a pak ji výběrem Přidat vygenerujte. Tato třída bude pro zjednodušení ověřovat uživatele pomocí základního ověřování. Vaše aplikace může používat jakékoli schéma ověřování.

  3. V souboru AuthenticationTestHandler.cs přidejte následující příkazy using:

    using System.Net.Http;
    using System.Threading;
    using System.Security.Principal;
    using System.Net;
    using System.Text;
    using System.Threading.Tasks;
    
  4. V souboru AuthenticationTestHandler.cs nahraďte definici třídy AuthenticationTestHandler následujícím kódem:

    Tato obslužná rutina ověří požadavek při splnění těchto tří podmínek:

    • Požadavek obsahuje autorizační hlavičku.
    • Požadavek používá základní ověřování.
    • Řetězce uživatelského jména a hesla jsou stejné.

    Jinak bude požadavek zamítnut. Při tomto ověření se nepoužívá správný přístup k ověřování a autorizaci. Je to jenom jednoduchý příklad pro účely tohoto kurzu.

    Pokud třída AuthenticationTestHandler ověří a autorizuje zprávu požadavku, uživatel základního ověřování se připojí k aktuálnímu požadavku v objektu HttpContext. Informace o uživateli v objektu HttpContext později použije jiný kontroler (RegisterController) pro přidání značky do požadavku na registraci oznámení.

    public class AuthenticationTestHandler : DelegatingHandler
    {
        protected override Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, CancellationToken cancellationToken)
        {
            var authorizationHeader = request.Headers.GetValues("Authorization").First();
    
            if (authorizationHeader != null && authorizationHeader
                .StartsWith("Basic ", StringComparison.InvariantCultureIgnoreCase))
            {
                string authorizationUserAndPwdBase64 =
                    authorizationHeader.Substring("Basic ".Length);
                string authorizationUserAndPwd = Encoding.Default
                    .GetString(Convert.FromBase64String(authorizationUserAndPwdBase64));
                string user = authorizationUserAndPwd.Split(':')[0];
                string password = authorizationUserAndPwd.Split(':')[1];
    
                if (VerifyUserAndPwd(user, password))
                {
                    // Attach the new principal object to the current HttpContext object
                    HttpContext.Current.User =
                        new GenericPrincipal(new GenericIdentity(user), new string[0]);
                    System.Threading.Thread.CurrentPrincipal =
                        System.Web.HttpContext.Current.User;
                }
                else return Unauthorized();
            }
            else return Unauthorized();
    
            return base.SendAsync(request, cancellationToken);
        }
    
        private bool VerifyUserAndPwd(string user, string password)
        {
            // This is not a real authentication scheme.
            return user == password;
        }
    
        private Task<HttpResponseMessage> Unauthorized()
        {
            var response = new HttpResponseMessage(HttpStatusCode.Forbidden);
            var tsc = new TaskCompletionSource<HttpResponseMessage>();
            tsc.SetResult(response);
            return tsc.Task;
        }
    }
    

    Poznámka

    Poznámka k zabezpečení: Třída AuthenticationTestHandler nezajišťuje skutečné ověřování. Používá se pouze k napodobení základního ověřování a není bezpečná. Ve svých produkčních aplikacích a službách musíte implementovat mechanismus zabezpečeného ověřování.

  5. Pokud chcete zaregistrovat obslužnou rutinu zprávy, přidejte do souboru Program.cs na konec Register metody následující kód:

    config.MessageHandlers.Add(new AuthenticationTestHandler());
    
  6. Uložte provedené změny.

Registrace k oznámením pomocí back-endu WebAPI

V této části přidáte do back-endu WebAPI nový kontroler, který bude zpracovávat požadavky na registraci uživatele a zařízení k oznámením pomocí klientské knihovny pro centra oznámení. Kontroler přidá značku uživatele pro uživatele, který byl ověřen a připojen k objektu HttpContext třídou AuthenticationTestHandler. Značka bude mít formát řetězce "username:<actual username>".

  1. V Průzkumníku řešení klikněte pravým tlačítkem na projekt AppBackend a pak vyberte Spravovat balíčky NuGet.

  2. V levém podokně vyberte Online a pak do pole Hledat zadejte Microsoft.Azure.NotificationHubs.

  3. V seznamu výsledků vyberte Microsoft Azure Notification Hubs a pak vyberte Nainstalovat. Dokončete instalaci a pak zavřete okno Správce balíčků NuGet.

    Tato akce přidá referenci na sadu SDK služby Azure Notification Hubs pomocí balíčku NuGet Microsoft.Azure.Notification Hubs.

  4. Vytvořte nový soubor třídy, která představuje propojení s centrem událostí sloužícím k odesílání oznámení. V Průzkumníku řešení klikněte pravým tlačítkem na složku Modely, vyberte Přidat a pak vyberte Třída. Pojmenujte novou třídu Notifications.cs a pak ji výběrem Přidat vygenerujte.

    Okno Přidat novou položku

  5. Na začátek souboru Notifications.cs přidejte následující příkaz using:

    using Microsoft.Azure.NotificationHubs;
    
  6. Nahraďte definici třídy Notifications následujícím kódem a nahraďte dva zástupné symboly připojovacím řetězcem (pro úplný přístup) vašeho centra událostí a názvem centra (najdete ho na webu Azure Portal):

    public class Notifications
    {
        public static Notifications Instance = new Notifications();
    
        public NotificationHubClient Hub { get; set; }
    
        private Notifications() {
            Hub = NotificationHubClient.CreateClientFromConnectionString("<your hub's DefaultFullSharedAccessSignature>",
                                                                            "<hub name>");
        }
    }
    

    Důležité

    Než budete pokračovat, zadejte název a defaultFullSharedAccessSignature vašeho centra.

  7. Dále vytvořte nový kontroler RegisterController. V Průzkumníku řešení klikněte pravým tlačítkem na složku Kontrolery, vyberte Přidat a pak vyberte Kontroler.

  8. Vyberte Kontroler rozhraní API – prázdný a pak vyberte Přidat.

  9. Do pole Název kontroleru zadejte RegisterController a pojmenujte tak novou třídu, pak vyberte Přidat.

    Okno Přidat kontroler

  10. V souboru RegisterController.cs přidejte následující příkazy using:

    using Microsoft.Azure.NotificationHubs;
    using Microsoft.Azure.NotificationHubs.Messaging;
    using AppBackend.Models;
    using System.Threading.Tasks;
    using System.Web;
    
  11. Do definice třídy RegisterController přidejte následující kód. V tomto kódu přidáváte značku uživatele pro uživatele, který je připojený k objektu HttpContext. Tento uživatel byl ověřen a připojen k objektu HttpContext filtrem zpráv AuthenticationTestHandler, který jste vytvořili. Můžete také přidat volitelné kontroly pro ověření, že uživatel má práva pro registraci k požadovaným značkám.

    private NotificationHubClient hub;
    
    public RegisterController()
    {
        hub = Notifications.Instance.Hub;
    }
    
    public class DeviceRegistration
    {
        public string Platform { get; set; }
        public string Handle { get; set; }
        public string[] Tags { get; set; }
    }
    
    // POST api/register
    // This creates a registration id
    public async Task<string> Post(string handle = null)
    {
        string newRegistrationId = null;
    
        // make sure there are no existing registrations for this push handle (used for iOS and Android)
        if (handle != null)
        {
            var registrations = await hub.GetRegistrationsByChannelAsync(handle, 100);
    
            foreach (RegistrationDescription registration in registrations)
            {
                if (newRegistrationId == null)
                {
                    newRegistrationId = registration.RegistrationId;
                }
                else
                {
                    await hub.DeleteRegistrationAsync(registration);
                }
            }
        }
    
        if (newRegistrationId == null) 
            newRegistrationId = await hub.CreateRegistrationIdAsync();
    
        return newRegistrationId;
    }
    
    // PUT api/register/5
    // This creates or updates a registration (with provided channelURI) at the specified id
    public async Task<HttpResponseMessage> Put(string id, DeviceRegistration deviceUpdate)
    {
        RegistrationDescription registration = null;
        switch (deviceUpdate.Platform)
        {
            case "mpns":
                registration = new MpnsRegistrationDescription(deviceUpdate.Handle);
                break;
            case "wns":
                registration = new WindowsRegistrationDescription(deviceUpdate.Handle);
                break;
            case "apns":
                registration = new AppleRegistrationDescription(deviceUpdate.Handle);
                break;
            case "fcm":
                registration = new FcmRegistrationDescription(deviceUpdate.Handle);
                break;
            default:
                throw new HttpResponseException(HttpStatusCode.BadRequest);
        }
    
        registration.RegistrationId = id;
        var username = HttpContext.Current.User.Identity.Name;
    
        // add check if user is allowed to add these tags
        registration.Tags = new HashSet<string>(deviceUpdate.Tags);
        registration.Tags.Add("username:" + username);
    
        try
        {
            await hub.CreateOrUpdateRegistrationAsync(registration);
        }
        catch (MessagingException e)
        {
            ReturnGoneIfHubResponseIsGone(e);
        }
    
        return Request.CreateResponse(HttpStatusCode.OK);
    }
    
    // DELETE api/register/5
    public async Task<HttpResponseMessage> Delete(string id)
    {
        await hub.DeleteRegistrationAsync(id);
        return Request.CreateResponse(HttpStatusCode.OK);
    }
    
    private static void ReturnGoneIfHubResponseIsGone(MessagingException e)
    {
        var webex = e.InnerException as WebException;
        if (webex.Status == WebExceptionStatus.ProtocolError)
        {
            var response = (HttpWebResponse)webex.Response;
            if (response.StatusCode == HttpStatusCode.Gone)
                throw new HttpRequestException(HttpStatusCode.Gone.ToString());
        }
    }
    
  12. Uložte provedené změny.

Odesílání oznámení z back-endu WebAPI

V této části přidáte nový kontroler, který zveřejňuje způsob odesílání oznámení klientskými zařízeními. Oznámení je založené na značce uživatelského jména, kterou používá knihovna .NET Notification Hubs v back-endu ASP.NET WebAPI.

  1. Vytvořte další nový kontroler NotificationsController stejným způsobem, kterým jste v předchozí části vytvořili RegisterController.

  2. V souboru NotificationsController.cs přidejte následující příkazy using:

    using AppBackend.Models;
    using System.Threading.Tasks;
    using System.Web;
    
  3. Do třídy NotificationsController přidejte následující metodu:

    Tento kód odesílá typ oznámení na základě parametru pns systému oznámení platformy. Hodnota to_tag slouží k nastavení značky username (uživatelské jméno) pro zprávu. Tato značka musí odpovídat značce uživatelského jména aktivní registrace k centru událostí. Zpráva oznámení se přetáhne z textu požadavku POST a naformátuje se pro cílový systém oznámení platformy.

    V závislosti na systému oznámení platformy, který vaše zařízení používá k přijímání oznámení, jsou podporována oznámení v různých formátech. Například na zařízeních s Windows byste mohli použít informační zprávu pomocí Služby nabízených oznámení Windows, kterou ostatní systémy oznámení platformy přímo nepodporují. V takovém případě musí váš back-end formátovat oznámení na podporované oznámení pro systémy oznámení platformy zařízení, která chcete podporovat. Ve třídě NotificationHubClient pak použijte vhodné rozhraní API pro odesílání.

    public async Task<HttpResponseMessage> Post(string pns, [FromBody]string message, string to_tag)
    {
        var user = HttpContext.Current.User.Identity.Name;
        string[] userTag = new string[2];
        userTag[0] = "username:" + to_tag;
        userTag[1] = "from:" + user;
    
        Microsoft.Azure.NotificationHubs.NotificationOutcome outcome = null;
        HttpStatusCode ret = HttpStatusCode.InternalServerError;
    
        switch (pns.ToLower())
        {
            case "wns":
                // Windows 8.1 / Windows Phone 8.1
                var toast = @"<toast><visual><binding template=""ToastText01""><text id=""1"">" + 
                            "From " + user + ": " + message + "</text></binding></visual></toast>";
                outcome = await Notifications.Instance.Hub.SendWindowsNativeNotificationAsync(toast, userTag);
                break;
            case "apns":
                // iOS
                var alert = "{\"aps\":{\"alert\":\"" + "From " + user + ": " + message + "\"}}";
                outcome = await Notifications.Instance.Hub.SendAppleNativeNotificationAsync(alert, userTag);
                break;
            case "fcm":
                // Android
                var notif = "{ \"data\" : {\"message\":\"" + "From " + user + ": " + message + "\"}}";
                outcome = await Notifications.Instance.Hub.SendFcmNativeNotificationAsync(notif, userTag);
                break;
        }
    
        if (outcome != null)
        {
            if (!((outcome.State == Microsoft.Azure.NotificationHubs.NotificationOutcomeState.Abandoned) ||
                (outcome.State == Microsoft.Azure.NotificationHubs.NotificationOutcomeState.Unknown)))
            {
                ret = HttpStatusCode.OK;
            }
        }
    
        return Request.CreateResponse(ret);
    }
    
  4. Stisknutím klávesy F5 aplikaci spusťte a ověřte, že jste zatím postupovali správně. Aplikace otevře webový prohlížeč a zobrazí se na domovské stránce ASP.NET.

Publikování nového back-endu WebAPI

Dále tuto aplikaci nasadíte na web Azure, aby byla přístupná ze všech zařízení.

  1. Klikněte pravým tlačítkem na projekt AppBackend a vyberte Publikovat.

  2. Jako cíl publikování vyberte Microsoft Azure App Service a pak vyberte \*\*Publikovat. Otevře se okno Vytvořit plán App Service. Tady můžete vytvořit všechny prostředky Azure, které jsou potřeba ke spuštění webové aplikace ASP.NET v Azure.

    Dlaždice Microsoft Azure App Service

  3. V okně Vytvořit plán App Service vyberte váš účet Azure. Vyberte Změnit typ>webové aplikace. Ponechejte výchozí Název webové aplikace a vyberte Předplatné, Skupinu prostředků a Plán služby App Service.

  4. Vyberte Vytvořit.

  5. Poznamenejte si vlastnost Adresa URL webu v části Souhrn. Tato adresa URL je váš koncový bod back-endu, který použijete později v tomto kurzu.

  6. Vyberte Publikovat.

Průvodce po dokončení publikuje webovou aplikaci ASP.NET do Azure a pak tuto aplikaci otevře ve výchozím prohlížeči. Vaši aplikaci bude možné zobrazit ve službě Azure App Service.

Adresa URL používá název webové aplikace, který jste zadali dříve, ve formátu http://< app_name.azurewebsites.net>.

Úprava aplikace pro iOS

  1. Otevřete aplikaci jednostránkového zobrazení, kterou jste vytvořili v kurzu Odesílání nabízených oznámení do aplikací pro iOS pomocí Služby Notification Hubs .

    Poznámka

    V této části se předpokládá, že je váš projekt nakonfigurovaný s prázdným názvem organizace. Pokud ne, předestavte název vaší organizace na všechny názvy tříd.

  2. Main.storyboard Do souboru přidejte komponenty zobrazené na snímku obrazovky z knihovny objektů.

    Úprava scénáře v tvůrci rozhraní Xcode

    • Uživatelské jméno: Pole UITextField se zástupným textem Zadejte uživatelské jméno hned pod popiskem výsledků odeslání a omezené na levý a pravý okraj a pod popiskem odeslat výsledky.

    • Heslo: Pole UITextField se zástupným textem Zadejte heslo hned pod textovým polem uživatelského jména a omezené na levý a pravý okraj a pod textové pole uživatelského jména. Zaškrtněte možnost Zabezpečené zadávání textu v inspektoru atributů v části Return Key.

    • Přihlášení: Tlačítko UIbutton označené bezprostředně pod textovým polem hesla a zrušení zaškrtnutí políčka Povoleno v kontrole atributů v části Control-Content

    • WNS: Popiskem a přepínačem povolíte odesílání oznámení Windows Notification Service, pokud je nastavená v centru. Projděte si kurz windows Začínáme.

    • GCM: Popiskem a přepnutím povolíte odesílání oznámení do služby Google Cloud Messaging, pokud je nastavené v centru. Podívejte se na kurz k androidu Začínáme.

    • APNS: Popisky a přepnutím povolíte odesílání oznámení do služby oznámení platformy Apple.

    • Uživatelské jméno příjemce:A UITextField se zástupným textem, značkou uživatelského jména příjemce, bezprostředně pod popiskem GCM a omezeným na levé a pravé okraje a pod popiskem GCM.

      Některé komponenty byly přidány v kurzu Odesílání nabízených oznámení do aplikací pro iOS pomocí služby Azure Notification Hubs .

  3. Stisknutím klávesy Ctrl přetáhněte ze součástí v zobrazení na ViewController.h a přidejte tyto nové výstupy:

    @property (weak, nonatomic) IBOutlet UITextField *UsernameField;
    @property (weak, nonatomic) IBOutlet UITextField *PasswordField;
    @property (weak, nonatomic) IBOutlet UITextField *RecipientField;
    @property (weak, nonatomic) IBOutlet UITextField *NotificationField;
    
    // Used to enable the buttons on the UI
    @property (weak, nonatomic) IBOutlet UIButton *LogInButton;
    @property (weak, nonatomic) IBOutlet UIButton *SendNotificationButton;
    
    // Used to enabled sending notifications across platforms
    @property (weak, nonatomic) IBOutlet UISwitch *WNSSwitch;
    @property (weak, nonatomic) IBOutlet UISwitch *GCMSwitch;
    @property (weak, nonatomic) IBOutlet UISwitch *APNSSwitch;
    
    - (IBAction)LogInAction:(id)sender;
    
  4. V ViewController.hsouboru přidejte #define následující příkazy za příkazy importu. <Your backend endpoint> Zástupný symbol nahraďte cílovou adresou URL, kterou jste použili k nasazení back-endu aplikace v předchozí části. Příklad http://your_backend.azurewebsites.net:

    #define BACKEND_ENDPOINT @"<Your backend endpoint>"
    
  5. Ve svém projektu vytvořte novou třídu Cocoa Touch s názvem RegisterClient pro rozhraní s ASP.NET back-endem, který jste vytvořili. Vytvořte třídu dědící z NSObject. Pak do RegisterClient.hpole přidejte následující kód :

    @interface RegisterClient : NSObject
    
    @property (strong, nonatomic) NSString* authenticationHeader;
    
    -(void) registerWithDeviceToken:(NSData*)token tags:(NSSet*)tags
        andCompletion:(void(^)(NSError*))completion;
    
    -(instancetype) initWithEndpoint:(NSString*)Endpoint;
    
    @end
    
  6. V části RegisterClient.maktualizujte @interface :

    @interface RegisterClient ()
    
    @property (strong, nonatomic) NSURLSession* session;
    @property (strong, nonatomic) NSURLSession* endpoint;
    
    -(void) tryToRegisterWithDeviceToken:(NSData*)token tags:(NSSet*)tags retry:(BOOL)retry
                andCompletion:(void(^)(NSError*))completion;
    -(void) retrieveOrRequestRegistrationIdWithDeviceToken:(NSString*)token
                completion:(void(^)(NSString*, NSError*))completion;
    -(void) upsertRegistrationWithRegistrationId:(NSString*)registrationId deviceToken:(NSString*)token
                tags:(NSSet*)tags andCompletion:(void(^)(NSURLResponse*, NSError*))completion;
    
    @end
    
  7. @implementation Nahraďte oddíl v RegisterClient.m následujícím kódem:

    @implementation RegisterClient
    
    // Globals used by RegisterClient
    NSString *const RegistrationIdLocalStorageKey = @"RegistrationId";
    
    -(instancetype) initWithEndpoint:(NSString*)Endpoint
    {
        self = [super init];
        if (self) {
            NSURLSessionConfiguration* config = [NSURLSessionConfiguration defaultSessionConfiguration];
            _session = [NSURLSession sessionWithConfiguration:config delegate:nil delegateQueue:nil];
            _endpoint = Endpoint;
        }
        return self;
    }
    
    -(void) registerWithDeviceToken:(NSData*)token tags:(NSSet*)tags
                andCompletion:(void(^)(NSError*))completion
    {
        [self tryToRegisterWithDeviceToken:token tags:tags retry:YES andCompletion:completion];
    }
    
    -(void) tryToRegisterWithDeviceToken:(NSData*)token tags:(NSSet*)tags retry:(BOOL)retry
                andCompletion:(void(^)(NSError*))completion
    {
        NSSet* tagsSet = tags?tags:[[NSSet alloc] init];
    
        NSString *deviceTokenString = [[token description]
            stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"<>"]];
        deviceTokenString = [[deviceTokenString stringByReplacingOccurrencesOfString:@" " withString:@""]
                                uppercaseString];
    
        [self retrieveOrRequestRegistrationIdWithDeviceToken: deviceTokenString
            completion:^(NSString* registrationId, NSError *error) {
            NSLog(@"regId: %@", registrationId);
            if (error) {
                completion(error);
                return;
            }
    
            [self upsertRegistrationWithRegistrationId:registrationId deviceToken:deviceTokenString
                tags:tagsSet andCompletion:^(NSURLResponse * response, NSError *error) {
                if (error) {
                    completion(error);
                    return;
                }
    
                NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response;
                if (httpResponse.statusCode == 200) {
                    completion(nil);
                } else if (httpResponse.statusCode == 410 && retry) {
                    [self tryToRegisterWithDeviceToken:token tags:tags retry:NO andCompletion:completion];
                } else {
                    NSLog(@"Registration error with response status: %ld", (long)httpResponse.statusCode);
    
                    completion([NSError errorWithDomain:@"Registration" code:httpResponse.statusCode
                                userInfo:nil]);
                }
    
            }];
        }];
    }
    
    -(void) upsertRegistrationWithRegistrationId:(NSString*)registrationId deviceToken:(NSData*)token
                tags:(NSSet*)tags andCompletion:(void(^)(NSURLResponse*, NSError*))completion
    {
        NSDictionary* deviceRegistration = @{@"Platform" : @"apns", @"Handle": token,
                                                @"Tags": [tags allObjects]};
        NSData* jsonData = [NSJSONSerialization dataWithJSONObject:deviceRegistration
                            options:NSJSONWritingPrettyPrinted error:nil];
    
        NSLog(@"JSON registration: %@", [[NSString alloc] initWithData:jsonData
                                            encoding:NSUTF8StringEncoding]);
    
        NSString* endpoint = [NSString stringWithFormat:@"%@/api/register/%@", _endpoint,
                                registrationId];
        NSURL* requestURL = [NSURL URLWithString:endpoint];
        NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:requestURL];
        [request setHTTPMethod:@"PUT"];
        [request setHTTPBody:jsonData];
        NSString* authorizationHeaderValue = [NSString stringWithFormat:@"Basic %@",
                                                self.authenticationHeader];
        [request setValue:authorizationHeaderValue forHTTPHeaderField:@"Authorization"];
        [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
    
        NSURLSessionDataTask* dataTask = [self.session dataTaskWithRequest:request
            completionHandler:^(NSData *data, NSURLResponse *response, NSError *error)
        {
            if (!error)
            {
                completion(response, error);
            }
            else
            {
                NSLog(@"Error request: %@", error);
                completion(nil, error);
            }
        }];
        [dataTask resume];
    }
    
    -(void) retrieveOrRequestRegistrationIdWithDeviceToken:(NSString*)token
                completion:(void(^)(NSString*, NSError*))completion
    {
        NSString* registrationId = [[NSUserDefaults standardUserDefaults]
                                    objectForKey:RegistrationIdLocalStorageKey];
    
        if (registrationId)
        {
            completion(registrationId, nil);
            return;
        }
    
        // request new one & save
        NSURL* requestURL = [NSURL URLWithString:[NSString stringWithFormat:@"%@/api/register?handle=%@",
                                _endpoint, token]];
        NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:requestURL];
        [request setHTTPMethod:@"POST"];
        NSString* authorizationHeaderValue = [NSString stringWithFormat:@"Basic %@",
                                                self.authenticationHeader];
        [request setValue:authorizationHeaderValue forHTTPHeaderField:@"Authorization"];
    
        NSURLSessionDataTask* dataTask = [self.session dataTaskWithRequest:request
            completionHandler:^(NSData *data, NSURLResponse *response, NSError *error)
        {
            NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*) response;
            if (!error && httpResponse.statusCode == 200)
            {
                NSString* registrationId = [[NSString alloc] initWithData:data
                    encoding:NSUTF8StringEncoding];
    
                // remove quotes
                registrationId = [registrationId substringWithRange:NSMakeRange(1,
                                    [registrationId length]-2)];
    
                [[NSUserDefaults standardUserDefaults] setObject:registrationId
                    forKey:RegistrationIdLocalStorageKey];
                [[NSUserDefaults standardUserDefaults] synchronize];
    
                completion(registrationId, nil);
            }
            else
            {
                NSLog(@"Error status: %ld, request: %@", (long)httpResponse.statusCode, error);
                if (error)
                    completion(nil, error);
                else {
                    completion(nil, [NSError errorWithDomain:@"Registration" code:httpResponse.statusCode
                                userInfo:nil]);
                }
            }
        }];
        [dataTask resume];
    }
    
    @end
    

    Tento kód implementuje logiku vysvětlenou v článku s pokyny k registraci z back-endu vaší aplikace pomocí NSURLSession k provádění volání REST do back-endu vaší aplikace a NSUserDefaults k místnímu ukládání ID registrace vrácené centrem oznámení.

    Tato třída vyžaduje, aby její vlastnost authorizationHeader byla nastavena, aby fungovala správně. Tato vlastnost je nastavena ViewController třídou po přihlášení.

  8. V ViewController.hsouboru přidejte #import příkaz pro RegisterClient.h. Pak přidejte deklaraci tokenu zařízení a odkaz na RegisterClient instanci v části @interface :

    #import "RegisterClient.h"
    
    @property (strong, nonatomic) NSData* deviceToken;
    @property (strong, nonatomic) RegisterClient* registerClient;
    
  9. V souboru ViewController.m přidejte deklaraci privátní metody v části @interface :

    @interface ViewController () <UITextFieldDelegate, NSURLConnectionDataDelegate, NSXMLParserDelegate>
    
    // create the Authorization header to perform Basic authentication with your app back-end
    -(void) createAndSetAuthenticationHeaderWithUsername:(NSString*)username
                    AndPassword:(NSString*)password;
    
    @end
    

    Poznámka

    Následující fragment kódu není schématem zabezpečeného createAndSetAuthenticationHeaderWithUsername:AndPassword: ověřování. Implementaci byste měli nahradit vaším konkrétním ověřovacím mechanismem, který generuje ověřovací token, který bude využíván třídou klienta register, například OAuth, Active Directory.

  10. Pak v @implementation části ViewController.mpřidejte následující kód, který přidá implementaci pro nastavení tokenu zařízení a hlavičky ověřování.

    -(void) setDeviceToken: (NSData*) deviceToken
    {
        _deviceToken = deviceToken;
        self.LogInButton.enabled = YES;
    }
    
    -(void) createAndSetAuthenticationHeaderWithUsername:(NSString*)username
                    AndPassword:(NSString*)password;
    {
        NSString* headerValue = [NSString stringWithFormat:@"%@:%@", username, password];
    
        NSData* encodedData = [[headerValue dataUsingEncoding:NSUTF8StringEncoding] base64EncodedDataWithOptions:NSDataBase64EncodingEndLineWithCarriageReturn];
    
        self.registerClient.authenticationHeader = [[NSString alloc] initWithData:encodedData
                                                    encoding:NSUTF8StringEncoding];
    }
    
    -(BOOL)textFieldShouldReturn:(UITextField *)textField
    {
        [textField resignFirstResponder];
        return YES;
    }
    

    Všimněte si, jak nastavení tokenu zařízení povolí tlačítko Přihlásit se . Je to proto, že v rámci akce přihlášení se kontroler zobrazení zaregistruje k nabízeným oznámením v back-endu aplikace. Nechcete, aby byla akce Přihlášení přístupná, dokud nebude token zařízení správně nastavený. Přihlášení můžete oddělit od registrace nabízených oznámení, pokud se první z nich stane před druhou registrací.

  11. V souboru ViewController.m použijte následující fragmenty kódu k implementaci metody akce pro tlačítko Přihlásit se a metodu pro odeslání zprávy oznámení pomocí ASP.NET back-endu.

    - (IBAction)LogInAction:(id)sender {
        // create authentication header and set it in register client
        NSString* username = self.UsernameField.text;
        NSString* password = self.PasswordField.text;
    
        [self createAndSetAuthenticationHeaderWithUsername:username AndPassword:password];
    
        __weak ViewController* selfie = self;
        [self.registerClient registerWithDeviceToken:self.deviceToken tags:nil
            andCompletion:^(NSError* error) {
            if (!error) {
                dispatch_async(dispatch_get_main_queue(),
                ^{
                    selfie.SendNotificationButton.enabled = YES;
                    [self MessageBox:@"Success" message:@"Registered successfully!"];
                });
            }
        }];
    }
    
    - (void)SendNotificationASPNETBackend:(NSString*)pns UsernameTag:(NSString*)usernameTag
                Message:(NSString*)message
    {
        NSURLSession* session = [NSURLSession
            sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:nil
            delegateQueue:nil];
    
        // Pass the pns and username tag as parameters with the REST URL to the ASP.NET backend
        NSURL* requestURL = [NSURL URLWithString:[NSString
            stringWithFormat:@"%@/api/notifications?pns=%@&to_tag=%@", BACKEND_ENDPOINT, pns,
            usernameTag]];
    
        NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:requestURL];
        [request setHTTPMethod:@"POST"];
    
        // Get the mock authenticationheader from the register client
        NSString* authorizationHeaderValue = [NSString stringWithFormat:@"Basic %@",
            self.registerClient.authenticationHeader];
        [request setValue:authorizationHeaderValue forHTTPHeaderField:@"Authorization"];
    
        //Add the notification message body
        [request setValue:@"application/json;charset=utf-8" forHTTPHeaderField:@"Content-Type"];
        [request setHTTPBody:[message dataUsingEncoding:NSUTF8StringEncoding]];
    
        // Execute the send notification REST API on the ASP.NET Backend
        NSURLSessionDataTask* dataTask = [session dataTaskWithRequest:request
            completionHandler:^(NSData *data, NSURLResponse *response, NSError *error)
        {
            NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*) response;
            if (error || httpResponse.statusCode != 200)
            {
                NSString* status = [NSString stringWithFormat:@"Error Status for %@: %d\nError: %@\n",
                                    pns, httpResponse.statusCode, error];
                dispatch_async(dispatch_get_main_queue(),
                ^{
                    // Append text because all 3 PNS calls may also have information to view
                    [self.sendResults setText:[self.sendResults.text stringByAppendingString:status]];
                });
                NSLog(status);
            }
    
            if (data != NULL)
            {
                xmlParser = [[NSXMLParser alloc] initWithData:data];
                [xmlParser setDelegate:self];
                [xmlParser parse];
            }
        }];
        [dataTask resume];
    }
    
  12. Aktualizujte akci tlačítka Odeslat oznámení tak, aby používalo back-end ASP.NET a odeslalo ho do libovolného PNS povoleného přepínačem.

    - (IBAction)SendNotificationMessage:(id)sender
    {
        //[self SendNotificationRESTAPI];
        [self SendToEnabledPlatforms];
    }
    
    -(void)SendToEnabledPlatforms
    {
        NSString* json = [NSString stringWithFormat:@"\"%@\"",self.notificationMessage.text];
    
        [self.sendResults setText:@""];
    
        if ([self.WNSSwitch isOn])
            [self SendNotificationASPNETBackend:@"wns" UsernameTag:self.RecipientField.text Message:json];
    
        if ([self.GCMSwitch isOn])
            [self SendNotificationASPNETBackend:@"gcm" UsernameTag:self.RecipientField.text Message:json];
    
        if ([self.APNSSwitch isOn])
            [self SendNotificationASPNETBackend:@"apns" UsernameTag:self.RecipientField.text Message:json];
    }
    
  13. ViewDidLoad Do funkce přidejte následující příkaz pro vytvoření instance instance a nastavte delegáta RegisterClient pro vaše textová pole.

    self.UsernameField.delegate = self;
    self.PasswordField.delegate = self;
    self.RecipientField.delegate = self;
    self.registerClient = [[RegisterClient alloc] initWithEndpoint:BACKEND_ENDPOINT];
    
  14. Teď v nástroji AppDelegate.modeberte veškerý obsah metody application:didRegisterForPushNotificationWithDeviceToken: a nahraďte ji následujícím kódem (abyste měli jistotu, že kontroler zobrazení obsahuje nejnovější token zařízení načtený ze služby APNs):

    // Add import to the top of the file
    #import "ViewController.h"
    
    - (void)application:(UIApplication *)application
                didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
    {
        ViewController* rvc = (ViewController*) self.window.rootViewController;
        rvc.deviceToken = deviceToken;
    }
    
  15. Nakonec se AppDelegate.mujistěte, že máte následující metodu:

    - (void)application:(UIApplication *)application didReceiveRemoteNotification: (NSDictionary *)userInfo {
        NSLog(@"%@", userInfo);
        [self MessageBox:@"Notification" message:[[userInfo objectForKey:@"aps"] valueForKey:@"alert"]];
    }
    

Testování aplikace

  1. V XCode spusťte aplikaci na fyzickém zařízení s iOSem (nabízená oznámení nefungují v simulátoru).

  2. V uživatelském rozhraní aplikace pro iOS zadejte stejnou hodnotu pro uživatelské jméno i heslo. Pak klikněte na Přihlásit se.

    Testovací aplikace pro iOS

  3. Mělo by se zobrazit automaticky otevírané okno s informací o úspěchu registrace. Klikněte na OK.

    Zobrazené oznámení o testu iOS

  4. Do textového pole *Značka uživatelského jména příjemce zadejte značku uživatelského jména použitou při registraci z jiného zařízení.

  5. Zadejte zprávu s oznámením a klikněte na Odeslat oznámení. Oznámení obdrží pouze zařízení, která mají registraci se značkou uživatelského jména příjemce. Odesílá se jenom těmto uživatelům.

    Oznámení se značkou testu iOS

Další kroky

V tomto kurzu jste zjistili, jak posílat nabízená oznámení konkrétním uživatelům, k jejichž registracím jsou přidružené značky. V dalším kurzu se dozvíte, jak posílat nabízená oznámení na základě polohy: