Samouczek: wysyłanie powiadomień wypychanych do określonych użytkowników przy użyciu usługi Azure Notification Hubs

W tym samouczku pokazano, jak wysyłać powiadomienia push do użytkownika konkretnej aplikacji na konkretnym urządzeniu za pomocą usługi Azure Notification Hubs. Zaplecze interfejsu WebAPI ASP.NET służy do uwierzytelniania klientów i generowania powiadomień, jak pokazano w temacie wskazówki Rejestrowanie z zaplecza aplikacji.

W tym samouczku wykonasz następujące kroki:

  • Tworzenie projektu interfejsu WebAPI
  • Uwierzytelnianie klientów w zapleczu interfejsu WebAPI
  • Rejestrowanie na potrzeby powiadomień za pomocą zaplecza interfejsu WebAPI
  • Wysyłanie powiadomień z zaplecza interfejsu WebAPI
  • Publikowanie nowego zaplecza interfejsu WebAPI
  • Modyfikowanie aplikacji systemu iOS
  • Testowanie aplikacji

Wymagania wstępne

W tym samouczku założono, że centrum powiadomień zostało utworzone i skonfigurowane zgodnie z opisem w temacie Wysyłanie powiadomień wypychanych do aplikacji systemu iOS przy użyciu usługi Azure Notification Hubs. Ten samouczek jest również wymaganiem wstępnym samouczka bezpiecznego wypychania (iOS). Jeśli chcesz używać usługi Mobile Apps jako usługi zaplecza, zobacz Artykuł Mobile Apps Get Started with Push (Aplikacje mobilne — wprowadzenie do wypychania).

Tworzenie projektu interfejsu WebAPI

W poniższych sekcjach omówiono proces tworzenia nowego zaplecza interfejsu WebAPI platformy ASP.NET. Ten proces ma trzy główne cele:

  • Uwierzytelnianie klientów: procedura obsługi komunikatów jest dodawana w celu uwierzytelnienia żądań klientów i kojarzenia użytkownika z żądaniem.
  • Rejestrowanie do używania powiadomień za pomocą zaplecza interfejsu WebAPI: kontroler jest dodawany w celu obsługi nowych rejestracji, aby urządzenie klienckie mogło otrzymywać powiadomienia. Nazwa uwierzytelnionego użytkownika jest automatycznie dodawana do rejestracji jako tag.
  • Wysyłanie powiadomień do klientów: jest dodawany kontroler udostępniający użytkownikom możliwość wyzwalania bezpiecznej operacji wypychania do urządzeń i klientów skojarzonych z tagiem.

Utwórz nowe zaplecze internetowego interfejsu API ASP.NET Core 6.0, wykonując następujące czynności:

Aby to sprawdzić, uruchom program Visual Studio. W menu Narzędzia wybierz pozycję Rozszerzenia i aktualizacje. Wyszukaj pozycję Menedżer pakietów NuGet w swojej wersji programu Visual Studio i sprawdź, czy masz najnowszą wersję. Jeśli nie używasz najnowszej wersji, odinstaluj ją i ponownie zainstaluj Menedżera pakietów NuGet.

Zrzut ekranu przedstawiający okno dialogowe Rozszerzenia i Aktualizacje z wyróżnionym pakietem NuGet Dla programu Visual Studios.

Uwaga

Upewnij się, że zainstalowano zestaw Azure SDK programu Visual Studio na potrzeby wdrażania witryny internetowej.

  1. Uruchom program Visual Studio lub Visual Studio Express.

  2. Wybierz pozycję Eksplorator serwera i zaloguj się do konta platformy Azure. Aby tworzyć zasoby witryny internetowej na swoim koncie, musisz się zalogować.

  3. W menu Plik programu Visual Studio wybierz pozycję Nowy>projekt.

  4. Wprowadź internetowy interfejs API w polu wyszukiwania.

  5. Wybierz szablon projektu internetowego interfejsu API ASP.NET Core i wybierz przycisk Dalej.

  6. W oknie dialogowym Konfigurowanie nowego projektu nadaj projektowi nazwę AppBackend i wybierz pozycję Dalej.

  7. W oknie dialogowym Dodatkowe informacje :

    • Upewnij się, że platforma.NET 6.0 (obsługa długoterminowa).
    • Upewnij się, że pole wyboru Użyj kontrolerów (usuń zaznaczenie, aby używać minimalnych interfejsów API) jest zaznaczone.
    • Usuń zaznaczenie pola wyboru Włącz obsługę interfejsu OpenAPI.
    • Wybierz przycisk Utwórz.

Usuwanie plików szablonów WeatherForecast

  1. Usuń przykładowe pliki WeatherForecast.cs i Controllers/WeatherForecastController.cs z nowego projektu AppBackend .
  2. Otwórz plik Properties\launchSettings.json.
  3. Zmień właściwości launchUrl z weatherforcast na appbackend.

W oknie Konfigurowanie aplikacji internetowej platformy Microsoft Azure wybierz subskrypcję, a następnie na liście Plan usługi App Service wykonaj jedną z następujących akcji:

  • Wybierz plan Azure App Service, który został już utworzony.
  • Wybierz pozycję Utwórz nowy plan usługi App Service, a następnie utwórz plan.

Ten samouczek nie wymaga bazy danych. Po wybraniu planu usługi App Service kliknij przycisk OK, aby utworzyć projekt.

Okno Konfigurowanie aplikacji internetowej platformy Microsoft Azure

Jeśli nie widzisz tej strony na potrzeby konfigurowania planu usługi App Service, przejdź do samouczka. Można ją skonfigurować podczas publikowania aplikacji później.

Uwierzytelnianie klientów w zapleczu interfejsu WebAPI

W tej sekcji tworzysz nową klasę procedury obsługi komunikatów o nazwie AuthenticationTestHandler dla nowego zaplecza. Ta klasa pochodzi od klasy DelegatingHandler i jest dodawana jako procedura obsługi komunikatów, aby umożliwić przetwarzanie wszystkich żądań przychodzących do zaplecza.

  1. W Eksploratorze rozwiązań kliknij prawym przyciskiem myszy projekt AppBackend, wybierz polecenie Dodaj, a następnie wybierz pozycję Klasa.

  2. Nadaj nowej klasie nazwę AuthenticationTestHandler.cs, a następnie wybierz pozycję Dodaj, aby wygenerować klasę. Ta klasa służy do uwierzytelniania użytkowników za pomocą Uwierzytelniania podstawowego dla uproszczenia. Twoja aplikacja może używać dowolnego schematu uwierzytelniania.

  3. W klasie AuthenticationTestHandler.cs dodaj następujące instrukcje using:

    using System.Net.Http;
    using System.Threading;
    using System.Security.Principal;
    using System.Net;
    using System.Text;
    using System.Threading.Tasks;
    
  4. W klasie AuthenticationTestHandler.cs zastąp definicję klasy AuthenticationTestHandler następującym kodem:

    Ta procedura obsługi autoryzuje żądanie, gdy następujące trzy warunki zostaną spełnione:

    • Żądanie zawiera nagłówek Authorization.
    • Żądanie używa uwierzytelniania basic.
    • Ciąg nazwy użytkownika i ciąg hasła to ten sam ciąg.

    W przeciwnym razie żądanie jest odrzucane. To uwierzytelnianie nie reprezentuje rzeczywistego podejścia do uwierzytelniania i autoryzacji. Jest to wyłącznie prosty przykład na potrzeby tego samouczka.

    Jeśli komunikat żądania jest uwierzytelniany i autoryzowany przez klasę AuthenticationTestHandler, użytkownik uwierzytelniania podstawowego jest dołączany do bieżącego żądania w obiekcie HttpContext. Informacje o użytkowniku w obiekcie HttpContext zostaną później użyte przez inny kontroler (RegisterController) w celu dodania tagu do żądania rejestracji powiadomienia.

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

    Uwaga

    Uwaga dotycząca zabezpieczeń: klasa AuthenticationTestHandler nie zapewnia rzeczywistego uwierzytelniania. Jest ona używana tylko do naśladowania uwierzytelniania podstawowego i nie jest bezpieczna. W swoich aplikacjach i usługach produkcyjnych musisz zaimplementować mechanizm bezpiecznego uwierzytelniania.

  5. Aby zarejestrować program obsługi komunikatów Register , dodaj następujący kod na końcu metody w pliku Program.cs :

    config.MessageHandlers.Add(new AuthenticationTestHandler());
    
  6. Zapisz zmiany.

Rejestrowanie na potrzeby powiadomień za pomocą zaplecza interfejsu WebAPI

W tej sekcji dodajesz nowy kontroler do zaplecza interfejsu WebAPI w celu obsługi żądań rejestracji użytkownika i urządzenia do otrzymywania powiadomień przy użyciu biblioteki klienta dla centrów powiadomień. Kontroler dodaje tag użytkownika, który został uwierzytelniony i dołączony do obiektu HttpContext przez klasę AuthenticationTestHandler. Tag ma format ciągu "username:<actual username>".

  1. W Eksploratorze rozwiązań kliknij prawym przyciskiem myszy projekt AppBackend, a następnie wybierz polecenie Zarządzaj pakietami NuGet.

  2. W okienku po lewej stronie wybierz pozycję Online, a następnie w polu Wyszukaj wpisz ciąg Microsoft.Azure.NotificationHubs.

  3. Na liście wyników wybierz pozycję Microsoft Azure Notification Hubs, a następnie wybierz pozycję Instaluj. Zakończ instalację, a następnie zamknij okno Menedżera pakietów NuGet.

    Ta akcja spowoduje dodanie odwołania do zestawu SDK usługi Azure Notification Hubs z użyciem pakietu NuGet Microsoft.Azure.Notification Hubs.

  4. Utwórz plik nowej klasy, która reprezentuje połączenie z centrum powiadomień używane do wysyłania powiadomień. W Eksploratorze rozwiązań kliknij prawym przyciskiem myszy folder Modele, wybierz polecenie Dodaj, a następnie kliknij pozycję Klasa. Nadaj nowej klasie nazwę Notifications.cs, a następnie wybierz pozycję Dodaj, aby wygenerować klasę.

    Okno Dodawanie nowego elementu

  5. W klasie Notifications.cs dodaj następującą instrukcję using na początku pliku:

    using Microsoft.Azure.NotificationHubs;
    
  6. Zastąp definicję klasy Notifications poniższym kodem i zastąp dwa symbole zastępcze parametrami połączenia (z pełnym dostępem) dla Twojego centrum powiadomień i nazwą centrum (dostępną w witrynie 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>");
        }
    }
    

    Ważne

    Przed kontynuowaniem wprowadź nazwę i defaultFullSharedAccessSignature centrum.

  7. Następnie utwórz nowy kontroler o nazwie RegisterController. W Eksploratorze rozwiązań kliknij prawym przyciskiem myszy folder Kontrolery, wybierz polecenie Dodaj, a następnie kliknij pozycję Kontroler.

  8. Wybierz pozycję Kontroler interfejsu API — pusty, a następnie wybierz pozycję Dodaj.

  9. W polu Nazwa kontrolera wpisz ciąg RegisterController, aby nadać nazwę nowej klasie, a następnie wybierz pozycję Dodaj.

    Okno Dodawanie kontrolera.

  10. W pliku RegisterController.cs dodaj następujące instrukcje using:

    using Microsoft.Azure.NotificationHubs;
    using Microsoft.Azure.NotificationHubs.Messaging;
    using AppBackend.Models;
    using System.Threading.Tasks;
    using System.Web;
    
  11. Dodaj następujący kod wewnątrz definicji klasy RegisterController. W tym kodzie dodajesz tag użytkownika, który został dołączony do obiektu HttpContext. Użytkownik został uwierzytelniony i dołączony do obiektu HttpContext przez dodany przez Ciebie filtr komunikatów, AuthenticationTestHandler. Można również dodać opcjonalne sprawdzenia w celu weryfikacji, czy użytkownik ma uprawnienia do rejestrowania żądanych tagów.

    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. Zapisz zmiany.

Wysyłanie powiadomień z zaplecza interfejsu WebAPI

W tej sekcji dodasz nowego kontrolera, który opisuje sposób wysyłania powiadomień z urządzeń klienckich. Powiadomienie opiera się na tagu nazwy użytkownika, który używa biblioteki .NET usługi Azure Notification Hubs w zaplecze interfejsu WebAPI na platformie ASP.NET.

  1. Utwórz innego nowego kontrolera o nazwie NotificationsController w taki sam sposób, jak utworzono kontrolera RegisterController w poprzedniej sekcji.

  2. W pliku NotificationsController.cs dodaj następujące instrukcje using:

    using AppBackend.Models;
    using System.Threading.Tasks;
    using System.Web;
    
  3. Dodaj następującą metodę do klasy NotificationsController:

    Ten kod wysyła typ powiadomienia na podstawie parametru pns usługi powiadomień platformy (PNS, Platform Notification Service). Wartość to_tag służy do ustawiania tagu username w komunikacie. Ten tag musi być zgodny z tagiem username aktywnej rejestracji centrum powiadomień. Komunikat powiadomienia jest pobierany z treści żądania POST i formatowany dla docelowej usługi PNS.

    W zależności od usługi PNS używanej do odbierania powiadomień przez obsługiwane urządzenia powiadomienia są obsługiwane przy użyciu różnych formatów. Na przykład w przypadku urządzeń z systemem Windows można użyć wyskakujących powiadomień za pomocą usługi WNS, które nie są bezpośrednio obsługiwane przez inną usługę PNS. W takim przypadku zaplecze musi sformatować powiadomienie jako obsługiwane powiadomienie w przypadku usługi PNS urządzeń, które planujesz obsługiwać. Następnie użyj odpowiedniego interfejsu API wysyłania w klasie NotificationHubClient.

    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. Aby uruchomić aplikację i sprawdzić dokładność pracy wykonanej do tej pory, naciśnij klawisz F5. Aplikacja otwiera przeglądarkę internetową z wyświetloną stroną główną platformy ASP.NET.

Publikowanie nowego zaplecza interfejsu WebAPI

Następnie wdrożysz tę aplikację w witrynie internetowej platformy Azure, aby udostępnić ją wszystkim urządzeniom.

  1. Kliknij prawym przyciskiem myszy projekt AppBackend, a następnie wybierz polecenie Publikuj.

  2. Wybierz pozycję Microsoft Azure App Service jako docelową lokalizację publikacji, a następnie wybierz pozycję \*\*Publikuj. Zostanie otwarte okno Tworzenie usługi App Service. W tym miejscu możesz tworzyć wszystkie niezbędne zasoby platformy Azure służące do uruchamiania aplikacji internetowej ASP.NET na platformie Azure.

    Kafelek usługi Microsoft Azure App Service

  3. W oknie Tworzenie usługi App Service wybierz swoje konto platformy Azure. Wybierz pozycję Zmień typ>aplikacji internetowej. Zachowaj wartość domyślną w polu Nazwa aplikacji internetowej i wybierz wartości w polach Subskrypcja, Grupa zasobów i Plan usługi App Service.

  4. Wybierz przycisk Utwórz.

  5. Zanotuj wartość właściwości Adres URL witryny w sekcji Podsumowanie. Ten adres URL jest Twoim punktem końcowym zaplecza w dalszej części samouczka.

  6. Kliknij pozycję Opublikuj.

Po ukończeniu pracy z kreatorem aplikacja internetowa ASP.NET zostanie opublikowana na platformie Azure, a następnie otwarta w przeglądarce domyślnej. Twoja aplikacja jest widoczna w usłudze Azure App Services.

Adres URL używa określonej wcześniej nazwy aplikacji internetowej z formatem http://< app_name.azurewebsites.net>.

Modyfikowanie aplikacji systemu iOS

  1. Otwórz aplikację Widok jednostronicowy utworzoną w samouczku Wysyłanie powiadomień wypychanych do aplikacji systemu iOS przy użyciu usługi Azure Notification Hubs .

    Uwaga

    W tej sekcji założono, że projekt jest skonfigurowany z pustą nazwą organizacji. Jeśli nie, należy wstępnie przypisać nazwę organizacji do wszystkich nazw klas.

  2. Main.storyboard W pliku dodaj składniki pokazane na zrzucie ekranu z biblioteki obiektów.

    Edytowanie scenorysu w konstruktorze interfejsu Xcode

    • Nazwa użytkownika: pole UITextField z tekstem zastępczym , Wprowadź nazwę użytkownika, bezpośrednio pod etykietą wysyłania wyników i ograniczone do lewego i prawego marginesu oraz pod etykietą wysyłania wyników.

    • Hasło: Pole UITextField z tekstem zastępczym , Wprowadź hasło, bezpośrednio pod polem tekstowym nazwy użytkownika i ograniczone do lewego i prawego marginesu oraz pod polem tekstowym nazwy użytkownika. Zaznacz opcję Bezpieczny wpis tekstu w inspektorze atrybutów w obszarze Return Key (Klucz zwrotny).

    • Zaloguj się: kontrolka UIButton oznaczona bezpośrednio pod polem tekstowym hasła i usuń zaznaczenie opcji Włączone w inspektorze atrybutów w obszarze Control-Content

    • WNS: etykieta i przełączenie w celu włączenia wysyłania powiadomień w usłudze powiadomień systemu Windows, jeśli została skonfigurowana w centrum. Zobacz samouczek dotyczący usługi Windows Wprowadzenie.

    • GCM: etykieta i przełączenie w celu włączenia wysyłania powiadomienia do usługi Google Cloud Messaging, jeśli została skonfigurowana w centrum. Zobacz Samouczek dotyczący Wprowadzenie systemu Android.

    • APNS: etykieta i przełączenie w celu włączenia wysyłania powiadomienia do usługi Apple Platform Notification Service.

    • Nazwa użytkownika odbiorcy:A UITextField z tekstem zastępczym, tagiem nazwy użytkownika odbiorcy, bezpośrednio pod etykietą GCM i ograniczonymi do lewego i prawego marginesu oraz pod etykietą GCM.

      Niektóre składniki zostały dodane w samouczku Wysyłanie powiadomień wypychanych do aplikacji systemu iOS przy użyciu usługi Azure Notification Hubs .

  3. Ctrl przeciągnij ze składników w widoku do ViewController.h i dodaj następujące nowe gniazda:

    @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. W ViewController.hpliku dodaj następujące elementy #define po instrukcjach importowania. Zastąp <Your backend endpoint> symbol zastępczy docelowym adresem URL użytym do wdrożenia zaplecza aplikacji w poprzedniej sekcji. Na przykład http://your_backend.azurewebsites.net:

    #define BACKEND_ENDPOINT @"<Your backend endpoint>"
    
  5. W projekcie utwórz nową klasę Cocoa Touch o nazwie RegisterClient do interfejsu z utworzonym zapleczem ASP.NET. Utwórz klasę dziedziczą z NSObjectklasy . Następnie dodaj następujący kod w pliku 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. W pliku RegisterClient.mzaktualizuj sekcję @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. Zastąp sekcję @implementation w pliku RegisterClient.m następującym kodem:

    @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
    

    Ten kod implementuje logikę objaśnioną w artykule ze wskazówkami Rejestrowanie z zaplecza aplikacji przy użyciu instrukcji NSURLSession w celu wykonywania wywołań REST do zaplecza aplikacji oraz NSUserDefaults w celu lokalnego przechowywania identyfikatora registrationId zwróconego przez centrum powiadomień.

    Ta klasa wymaga ustawienia jej właściwości authorizationHeader w celu poprawnego działania. Ta właściwość jest ustawiana przez klasę ViewController po zalogowaniu.

  8. W ViewController.hpliku dodaj instrukcję #import dla elementu RegisterClient.h. Następnie dodaj deklarację tokenu urządzenia i odwołanie do RegisterClient wystąpienia w @interface sekcji :

    #import "RegisterClient.h"
    
    @property (strong, nonatomic) NSData* deviceToken;
    @property (strong, nonatomic) RegisterClient* registerClient;
    
  9. W pliku ViewController.m dodaj deklarację metody prywatnej w @interface sekcji :

    @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
    

    Uwaga

    Poniższy fragment kodu nie jest bezpiecznym schematem uwierzytelniania. Należy zastąpić implementację createAndSetAuthenticationHeaderWithUsername:AndPassword: elementu określonym mechanizmem uwierzytelniania, który generuje token uwierzytelniania używany przez klasę klienta rejestru, np. OAuth, Active Directory.

  10. Następnie w sekcji w @implementation pliku ViewController.mdodaj następujący kod, który dodaje implementację ustawiania tokenu urządzenia i nagłówka uwierzytelniania.

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

    Zwróć uwagę, że ustawienie tokenu urządzenia powoduje włączenie przycisku Zaloguj . Jest to spowodowane tym, że w ramach akcji logowania kontroler widoku rejestruje się w celu otrzymywania powiadomień wypychanych z zapleczem aplikacji. Nie chcesz, aby akcja Zaloguj się była dostępna do momentu prawidłowego skonfigurowania tokenu urządzenia. Możesz rozdzielić dane logowania z rejestracji wypychanej, o ile pierwsze nastąpi przed tym ostatnim.

  11. W pliku ViewController.m użyj poniższych fragmentów kodu, aby zaimplementować metodę akcji dla przycisku Zaloguj i metodę wysyłania komunikatu powiadomienia przy użyciu zaplecza ASP.NET.

    - (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. Zaktualizuj akcję dla przycisku Wyślij powiadomienie , aby użyć zaplecza ASP.NET i wysłać do dowolnego systemu powiadomień platformy obsługiwanego przez przełącznik.

    - (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 W funkcji dodaj następujące polecenie, aby utworzyć wystąpienie RegisterClient wystąpienia i ustawić delegata dla pól tekstowych.

    self.UsernameField.delegate = self;
    self.PasswordField.delegate = self;
    self.RecipientField.delegate = self;
    self.registerClient = [[RegisterClient alloc] initWithEndpoint:BACKEND_ENDPOINT];
    
  14. Teraz w AppDelegate.mpliku usuń całą zawartość metody application:didRegisterForPushNotificationWithDeviceToken: i zastąp ją następującym kodem (aby upewnić się, że kontroler widoku zawiera najnowszy token urządzenia pobrany z usługi 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. Na koniec upewnij AppDelegate.msię, że masz następującą metodę:

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

Testowanie aplikacji

  1. W programie XCode uruchom aplikację na fizycznym urządzeniu z systemem iOS (powiadomienia wypychane nie działają w symulatorze).

  2. W interfejsie użytkownika aplikacji systemu iOS wprowadź tę samą wartość dla nazwy użytkownika i hasła. Następnie kliknij pozycję Zaloguj.

    Aplikacja testowa systemu iOS

  3. Powinno zostać wyświetlone wyskakujące okienko z informacją o powodzeniu rejestracji. Kliknij przycisk OK.

    Wyświetlone powiadomienie testowe systemu iOS

  4. W polu tekstowym *Tag nazwy użytkownika odbiorcy wprowadź tag nazwy użytkownika używany z rejestracją z innego urządzenia.

  5. Wprowadź komunikat powiadomienia i kliknij pozycję Wyślij powiadomienie. Tylko urządzenia, które mają rejestrację z tagiem nazwy użytkownika odbiorcy, otrzymają komunikat powiadomienia. Jest on wysyłany tylko do tych użytkowników.

    Powiadomienie o tagach testowych systemu iOS

Następne kroki

W tym samouczku przedstawiono sposób wysyłania powiadomień push do konkretnych użytkowników, którzy mają tagi skojarzone ze swoimi rejestracjami. Aby dowiedzieć się, jak wypychać powiadomienia oparte na lokalizacji, przejdź do następującego samouczka: