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í. ASP.NET Back-end 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í aplikacím pro iOS pomocí služby Azure Notification Hubs. Tento kurz je také předpokladem kurzu Secure Push (iOS). Pokud chcete jako back-endovou službu používat Mobile Apps, přečtěte si téma Mobile Apps Začínáme s nabízenými oznámeními.

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.

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

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.

Screenshot of the Extensions and Updates dialog box with the NuGet Package manage for Visual Studios package highlighted.

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 Visual Studio vyberte Nový>Project.

  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ě Konfigurovat nový projekt 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žít kontrolery (zrušte zaškrtnutí políčka používat minimální rozhraní API ).
    • Zrušte zaškrtnutí políčka Povolit podporu OpenAPI.
    • Vyberte Vytvořit.

Odebrání souborů šablon WeatherForecast

  1. Odeberte ukázkové soubory WeatherForecast.cs a Controllers/WeatherForecastController.cs z nového projektu AppBackend .
  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.

The Configure Microsoft Azure Web App window

Pokud tuto stránku nevidíte pro konfiguraci plánu služby App Service, 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. Chcete-li zaregistrovat obslužnou rutinu zprávy, přidejte na konec Register metody do souboru Program.cs 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.

    The Add New Item window

  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.

    The Add Controller window.

  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.

    The Microsoft Azure App Service tile

  3. V okně Vytvořit plán App Service vyberte váš účet Azure. Vyberte změnit aplikaci TypeWeb>. 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, s formátem http://< app_name.azurewebsites.net>.

Úprava aplikace pro iOS

  1. Pomocí kurzu Azure Notification Hubs otevřete aplikaci jednostránkového zobrazení, kterou jste vytvořili v aplikaci Pro odesílání nabízených oznámení do aplikací pro iOS .

    Poznámka

    Tato část předpokládá, že projekt je nakonfigurovaný s prázdným názvem organizace. Pokud ne, předvytřiďte 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ů.

    Edit storyboard in Xcode interface builder

    • Uživatelské jméno: UITextField se zástupným textem, zadejte uživatelské jméno, bezprostředně pod popiskem pro odeslání výsledků a omezeným na levý a pravý okraj a pod popiskem pro odeslání výsledků.

    • Heslo: UITextField se zástupným textem, zadáním hesla, bezprostředně pod textovým polem uživatelského jména a omezením na levý a pravý okraj a pod textové pole uživatelského jména. V části Návratový klíč zaškrtněte možnost Zabezpečené zadávání textu v inspektoru atributů.

    • Přihlášení: Pod textovým polem hesla se okamžitě označí tlačítko UIButton a v části Control-Content zrušte zaškrtnutí políčka Povoleno v inspektoru atributů.

    • WNS: Popisek a přepínač pro povolení odesílání oznámení Windows Notification Service, pokud je nastavený v centru. Podívejte se na Windows Začínáme kurz.

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

    • APNS: Popisek a přepnutí, aby bylo možné oznámení odeslat do služby Oznámení platformy Apple.

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

      Některé komponenty byly přidány do aplikací pro iOS pomocí kurzu k odesílání nabízených oznámení do aplikací pro iOS.

  3. Ctrl přetáhněte z komponent v zobrazení ViewController.h a přidejte tyto nové zásuvky:

    @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. Do ViewController.hpříkazu import přidejte následující #define příkazy. <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. Například: http://your_backend.azurewebsites.net

    #define BACKEND_ENDPOINT @"<Your backend endpoint>"
    
  5. V projektu vytvořte novou třídu Cocoa Touch pojmenovanou RegisterClient pro rozhraní s ASP.NET back-endem, který jste vytvořili. Vytvořte třídu zděděnou z NSObject. Potom přidejte následující kód do :RegisterClient.h

    @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 Oddíl v RegisterClient.m nahraďte 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 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ých centrem oznámení.

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

  8. V ViewController.h, přidat #import příkaz pro RegisterClient.h. Potom přidejte deklaraci tokenu zařízení a odkaz na RegisterClient instanci v oddílu @interface :

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

    @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í zabezpečeným schématem ověřování, měli byste nahradit implementaci konkrétního createAndSetAuthenticationHeaderWithUsername:AndPassword: mechanismu ověřování, který generuje ověřovací token, který bude využívat registrovat klientskou třídu, např. OAuth, Active Directory.

  10. Potom v @implementation části přidejte ViewController.mná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 jako součást akce přihlášení se kontroler zobrazení zaregistruje k nabízeným oznámením v back-endu aplikace. Nechcete, aby akce Přihlášení byla přístupná, dokud se token zařízení správně nenastavil. Přihlášení můžete oddělit od registrace nabízených oznámení, pokud k tomu dojde dříve.

  11. V ViewController.m pomocí následujících fragmentů kódu implementujte metodu akce pro tlačítko Přihlásit se a metodu pro odeslání 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žívala back-end ASP.NET a odeslala se 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í instanci a RegisterClient nastavte delegáta pro textová pole.

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

    // 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. AppDelegate.mNakonec se ujistě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. Potom klikněte na Přihlásit.

    iOS test application

  3. Měli byste vidět automaticky otevírané okno s informacemi o úspěchu registrace. Klikněte na OK.

    iOS test notification displayed

  4. Do textového pole *Jméno příjemce zadejte značku uživatelského jména použitou s registrací z jiného zařízení.

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

    iOS test tagged notification

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: