Självstudie: Skicka push-meddelanden till specifika användare med Hjälp av Azure Notification Hubs

Denna självstudie visar hur du använder Azure Notification Hubs till att skicka push-meddelanden till en specifik appanvändare på en specifik enhet. En ASP.NET WebAPI-serverdel används för att autentisera klienter och generera meddelanden, som du ser i vägledningsavsnittet Registrera från appens serverdel.

I den här självstudien gör du följande:

  • Skapa ett WebAPI-projekt
  • Autentisera klienter mot WebAPI-serverdelen
  • Registrera för meddelanden med hjälp av WebAPI-serverdelen
  • Skicka meddelanden från WebAPI-serverdelen
  • Publicera den nya WebAPI-serverdelen
  • Ändra din iOS-app
  • Testa programmet

Förutsättningar

Den här självstudien förutsätter att du har skapat och konfigurerat din meddelandehubb enligt beskrivningen i Skicka push-meddelanden till iOS-appar med Azure Notification Hubs. Den här självstudien är också en förutsättning för självstudiekursen om säker push (iOS). Om du vill använda Mobile Apps som serverdelstjänst kan du läsa Komma igång med Push i Mobile Apps.

Skapa ett WebAPI-projekt

I följande avsnitt beskrivs skapandet av en ny ASP.NET-WebAPI-serverdel. Den här processen har tre huvudsakliga syften:

  • Autentisera klienter: Du lägger till en meddelandehanterare för att autentisera klientbegäranden och associera användaren med begäran.
  • Registrera dig för meddelanden med hjälp av WebAPI-serverdel: Du lägger till en domänkontrollant för att hantera nya registreringar för en klientenhet för att ta emot meddelanden. Det autentiserade användarnamnet läggs automatiskt till i registreringen som en tagg.
  • Skicka meddelanden till klienter: Du lägger till en kontrollant som gör att användare kan utlösa en säker push-överföring till enheter och klienter som är associerade med taggen.

Skapa den nya ASP.NET Core 6.0-webb-API-serverdelen genom att utföra följande åtgärder:

Börja med att starta Visual Studio. På menyn Verktyg väljer du Tillägg och uppdateringar. Sök efter NuGet Package Manager i din version av Visual Studio och kontrollera att du har den senaste versionen. Om din version inte är den senaste versionen, avinstallera den och installera sedan om NuGet Package Manager.

Skärmbild av dialogrutan Tillägg och Uppdateringar med NuGet Package manage for Visual Studios-paketet markerat.

Anteckning

Kontrollera att du har installerat Visual Studio Azure SDK för webbplatsdistribution.

  1. Starta Visual Studio eller Visual Studio Express.

  2. Välj Server Explorer och logga in på ditt Azure-konto. För att skapa webbplatsresurser på ditt konto, måste du vara inloggad.

  3. I Menyn Arkiv i Visual Studio väljer du Nytt>projekt.

  4. Ange webb-API i sökrutan.

  5. Välj projektmallen ASP.NET Core webb-API och välj Nästa.

  6. I dialogrutan Konfigurera det nya projektet namnger du projektet AppBackend och väljer Nästa.

  7. I dialogrutan Ytterligare information :

    • Bekräfta att Framework är .NET 6.0 (långsiktigt stöd).
    • Bekräfta att kryssrutan för Använd kontrollanter (avmarkera för att använda minimala API:er) är markerad.
    • Avmarkera Aktivera OpenAPI-stöd.
    • Välj Skapa.

Ta bort WeatherForecast-mallfilerna

  1. Ta bort exempelfilerna WeatherForecast.cs och Controllers/WeatherForecastController.cs från det nya AppBackend-projektet .
  2. Öppna Properties\launchSettings.json.
  3. Ändra launchUrl-egenskaper från weatherforcast till appbackend.

I fönstret Konfigurera Microsoft Azure Web App väljer du en prenumeration och sedan i listan App Service plan gör du någon av följande åtgärder:

  • Välj en Azure App Service plan som du redan har skapat.
  • Välj Skapa en ny app service-plan, och skapa sedan en.

Du behöver ingen databas för den här självstudiekursen. När du har valt app service-plan väljer du OK för att skapa projektet.

Konfigurera Microsoft Azure Web App-fönstret

Om du inte ser den här sidan för att konfigurera App Service-plan fortsätter du med självstudien. Du kan konfigurera den när du publicerar appen senare.

Autentisera klienter mot WebAPI-serverdelen

I det här avsnittet skapar du en ny meddelandehanterarklass med namnet AuthenticationTestHandler för den nya serverdelen. Den här klassen härleds från DelegatingHandler och läggs till som en meddelandehanterare så att den kan bearbeta alla begäranden som kommer till serverdelen.

  1. I Solution Explorer högerklickar du på projektet AppBackend och väljer sedan Lägg till och sedan Klass.

  2. Ge den nya klassen namnet AuthenticationTestHandler.cs och välj sedan Lägg till för att generera klassen. Den här klassen autentiserar användare med hjälp av Grundläggande autentisering för enkelhetens skull. Din app kan använda alla autentiseringsscheman.

  3. Lägg till följande using-instruktioner i AuthenticationTestHandler.cs:

    using System.Net.Http;
    using System.Threading;
    using System.Security.Principal;
    using System.Net;
    using System.Text;
    using System.Threading.Tasks;
    
  4. Ersätt AuthenticationTestHandler-klassdefinitionen i AuthenticationTestHandler.cs med följande kod:

    Hanteraren godkänner begäran om följande tre villkor är uppfyllda:

    • Begäran innehåller en auktoriseringsrubrik.
    • Begäran använder grundläggande autentisering.
    • Strängen för användarnamn och lösenordssträngen är samma sträng.

    I annat fall avvisas begäran. Den här begäran är inte en riktig autentiserings- och auktoriseringsmetod. Det är bara ett enkelt exempel för den här självstudien.

    Om meddelandebegäran autentiseras och godkänns av AuthenticationTestHandler, kopplas användaren med den grundläggande autentiseringen till den aktuella begäran i HttpContext. Senare används användarinformationen i HttpContext av en annan kontrollant (RegisterController) för att lägga till en tagg till meddelanderegistreringsbegäran.

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

    Anteckning

    Säkerhetsmeddelande: Klassen AuthenticationTestHandler tillhandahåller inte riktig autentisering. Den används endast för att efterlikna grundläggande autentisering och är inte säker. Du måste implementera en säker autentiseringsmekanism i dina tjänster och program i produktionsmiljön.

  5. Om du vill registrera meddelandehanteraren lägger du till följande kod i slutet av Register metoden i filen Program.cs :

    config.MessageHandlers.Add(new AuthenticationTestHandler());
    
  6. Spara ändringarna.

Registrera för meddelanden med hjälp av WebAPI-serverdelen

I det här avsnittet lägger du till en ny kontrollant till WebAPI-serverdelen som ska hantera begäranden för att registrera en användare och en enhet för meddelanden med hjälp av klientbiblioteket för meddelandehubbar. Kontrollanten lägger till en användartagg för den användare som autentiserades och kopplades till HttpContext av AuthenticationTestHandler. Taggen har strängformatet "username:<actual username>".

  1. Högerklicka på AppBackend-projektet i Solution Explorer och välj sedan Hantera NuGet-paket.

  2. I den vänstra rutan välj Online och sedan, i rutan Sök skriver du Microsoft.Azure.NotificationHubs.

  3. Välj Microsoft Azure Notification Hubs i resultatlistan och sedan Installera. Slutför installationen och stäng sedan fönstret för NuGet-pakethanteraren.

    Den här åtgärden lägger till en referens i Azure Notification Hubs SDK med hjälp av Microsoft.Azure.Notification Hubs-NuGet-paketet.

  4. Skapa en ny klassfil som representerar anslutningen med meddelandehubben som används för att skicka meddelanden. I Solution Explorer högerklickar du på mappen Modeller, välj Lägg till och sedan Klass. Ge den nya klassen namnet Notifications.cs och välj sedan Lägg till för att generera den nya klassen.

    Fönstret Lägg till nytt objekt

  5. Lägg till följande using-instruktion överst i Notifications.cs-filen:

    using Microsoft.Azure.NotificationHubs;
    
  6. Ersätt Notifications-klassdefinitionen med följande kod och ersätt de två platshållarna med anslutningssträngen (med fullständig åtkomst) för din meddelandehubb samt hubbnamnet (tillgängligt på 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>");
        }
    }
    

    Viktigt

    Ange namnet och DefaultFullSharedAccessSignature för hubben innan du fortsätter.

  7. Skapa nu en ny kontrollant med namnet RegisterController. I Solution Explorer högerklickar du på mappen Styrenheter. Välj sedan Lägg till och sedan Styrenhet.

  8. Välj API-kontrollant – tom och välj sedan Lägg till.

  9. I rutan Kontrollnamn skriver du RegisterController som namn på den nya klassen och väljer sedan Lägg till.

    Fönstret Lägg till kontrollant.

  10. Lägg till följande using-instruktioner i RegisterController.cs:

    using Microsoft.Azure.NotificationHubs;
    using Microsoft.Azure.NotificationHubs.Messaging;
    using AppBackend.Models;
    using System.Threading.Tasks;
    using System.Web;
    
  11. Lägg till följande kod i RegisterController-klassdefinitionen. Lägg till en användartagg för den användare som är kopplad till HttpContext i den här koden. Användaren autentiserades och kopplades till HttpContext med hjälp av meddelandefiltret som du la till, AuthenticationTestHandler. Du kan också lägga till valfria kontroller som kontrollerar att användaren har behörighet att registrera sig för de begärda taggarna.

    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. Spara ändringarna.

Skicka meddelanden från WebAPI-serverdelen

I det här avsnittet kan du lägga till en ny domänkontrollant som exponerar en metod för klientenheter för att skicka en avisering. Meddelandet baseras på taggen för användarnamnet som använder Azure Notification Hubs .NET-biblioteket i ASP.NET-WebAPI-serverdelen.

  1. Skapa en ny domänkontrollant med namnet NotificationsController på samma sätt som du skapade RegisterController i föregående avsnitt.

  2. Lägg till följande using-instruktioner i NotificationsController.cs:

    using AppBackend.Models;
    using System.Threading.Tasks;
    using System.Web;
    
  3. Lägg till följande metod i NotificationsController-klassen:

    Den här koden skickar en meddelandetyp som är baserad på PNS-parametern (Platform Notification Service) pns. Värdet för to_tag används för att ställa in usernamne-taggen för meddelandet. Den här taggen måste matcha en username-tagg för en aktiv meddelandehubbsregistrering. Meddelandet hämtas från innehållsdelen i POST-begäran och formateras för PNS-måltjänsten.

    Beroende på det PNS-system som dina stödda enheter använder för att ta emot meddelanden, stöds meddelanden i olika format. På Windows-enheter kan du till exempel använda ett popup-meddelande med WNS som inte stöds direkt av en annan PNS. I dessa fall måste din serverdel alltså formatera meddelandet till ett meddelande som stöds för PNS-tjänsten för enheter som du vill ha stöd för. Därefter använder du lämpligt överförings-API i NotificationHubClient-klassen.

    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. Tryck på F5 för att köra programmet och för att kontrollera att allt fungerar som det ska hittills. Appen öppnar en webbläsare och den visas på startsidan för ASP.NET.

Publicera den nya WebAPI-serverdelen

Därefter distribuerar du appen till en Azure-webbplats så att den är tillgänglig från alla enheter.

  1. Högerklicka på AppBackend-projektet och välj sedan Publicera.

  2. Välj Microsoft Azure App Service som publiceringsmål och välj sedan \*\*Publicera. Fönstret Skapa App Service öppnas. Här kan du skapa alla nödvändiga Azure-resurser för att köra ASP.NET-webbapp i Azure.

    Panelen Microsoft Azure App Service

  3. Välj ditt Azure-konto i fönstret Skapa App Service. Välj Ändra typ>av webbapp. Behåll standardnamnet på webbappen och välj sedan Prenumeration, Resursgrupp och App Service Plan.

  4. Välj Skapa.

  5. Notera egenskapen Plats-URL i avsnittet Sammanfattning. Den här URL:en är din slutpunkt för serverdel senare under självstudien.

  6. Välj Publicera.

När du har slutfört guiden publiceras ASP.NET-webbappen till Azure och sedan öppnas appen i standardwebbläsaren. Programmet kan visas i Azure App Services.

URL:en använder webbappens namn som du angav tidigare, med formatet http://< app_name.azurewebsites.net>.

Ändra din iOS-app

  1. Öppna den ensidesvyapp som du skapade i självstudien Skicka push-meddelanden till iOS-appar med Azure Notification Hubs .

    Anteckning

    Det här avsnittet förutsätter att projektet har konfigurerats med ett tomt organisationsnamn. Annars förbereder du organisationens namn till alla klassnamn.

  2. Main.storyboard I filen lägger du till de komponenter som visas på skärmbilden från objektbiblioteket.

    Redigera storyboard i Xcode-gränssnittsverktyget

    • Användarnamn: Ett UITextField med platshållartext, Ange användarnamn, direkt under etiketten skicka resultat och begränsad till vänster och höger marginaler och under etiketten skicka resultat.

    • Lösenord: Ett UITextField med platshållartext, Ange lösenord, omedelbart under textfältet användarnamn och begränsad till vänster och höger marginaler och under fältet användarnamn text. Kontrollera alternativet Säker textpost i Attributkontroll under Returnyckel.

    • Logga in: En UIButton som är märkt direkt under textfältet för lösenord och avmarkera alternativet Aktiverad i Attributkontroll under Kontrollinnehåll

    • WNS: Märk och växla för att aktivera sändning av meddelandet Windows Notification Service om den har konfigurerats på hubben. Se självstudien om Windows Komma igång.

    • GCM: Märk och växla för att aktivera sändning av meddelandet till Google Cloud Messaging om det har konfigurerats på hubben. Se självstudien om Android Komma igång.

    • APNS: Märk och växla för att aktivera sändning av meddelandet till Apple Platform Notification Service.

    • Mottagarens användarnamn:Ett UITextField med platshållartext, mottagarens användarnamntagg, direkt under GCM-etiketten och begränsad till vänster och höger marginaler och under GCM-etiketten.

      Vissa komponenter har lagts till i självstudien Skicka push-meddelanden till iOS-appar med Hjälp av Azure Notification Hubs .

  3. Ctrl-dra från komponenterna i vyn till ViewController.h och lägg till dessa nya uttag:

    @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. I ViewController.hlägger du till följande #define efter dina importinstruktioner. <Your backend endpoint> Ersätt platshållaren med mål-URL:en som du använde för att distribuera appens serverdel i föregående avsnitt. Till exempel http://your_backend.azurewebsites.net:

    #define BACKEND_ENDPOINT @"<Your backend endpoint>"
    
  5. I projektet skapar du en ny Cocoa Touch-klass med namnet RegisterClient för gränssnittet med ASP.NET serverdel som du skapade. Skapa klassen som ärver från NSObject. Lägg sedan till följande kod i 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. I uppdaterar RegisterClient.mdu @interface avsnittet :

    @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. Ersätt avsnittet @implementation i RegisterClient.m med följande kod:

    @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
    

    Den här koden implementerar logiken som förklaras i vägledningsartikeln Registrera från din appserverdel med hjälp av NSURLSession för att utföra REST-anrop till din appserverdel och NSUserDefaults för att lokalt lagra registrationId som returneras av meddelandehubben.

    Den här klassen kräver att dess egenskap authorizationHeader anges för att fungera korrekt. Den här egenskapen anges av ViewController klassen efter inloggningen.

  8. I ViewController.hlägger du till en #import -instruktion för RegisterClient.h. Lägg sedan till en deklaration för enhetstoken och en referens till en RegisterClient instans i avsnittet @interface :

    #import "RegisterClient.h"
    
    @property (strong, nonatomic) NSData* deviceToken;
    @property (strong, nonatomic) RegisterClient* registerClient;
    
  9. Lägg till en privat metoddeklaration i avsnittet i @interface ViewController.m:

    @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
    

    Anteckning

    Följande kodfragment är inte ett schema för säker autentisering. Du bör ersätta implementeringen av createAndSetAuthenticationHeaderWithUsername:AndPassword: med din specifika autentiseringsmekanism som genererar en autentiseringstoken som ska användas av registerklientklassen, t.ex. OAuth, Active Directory.

  10. I avsnittet @implementation i ViewController.mlägger du sedan till följande kod, som lägger till implementeringen för att ange enhetstoken och autentiseringshuvudet.

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

    Observera hur du aktiverar knappen Logga in genom att ange enhetstoken. Det beror på att vykontrollanten som en del av inloggningsåtgärden registrerar sig för push-meddelanden med appens serverdel. Du vill inte att inloggningsåtgärden ska vara tillgänglig förrän enhetstoken har konfigurerats korrekt. Du kan frikoppla inloggningen från push-registreringen så länge den förstnämnda inträffar före den senare.

  11. I ViewController.m använder du följande kodfragment för att implementera åtgärdsmetoden för inloggningsknappen och en metod för att skicka aviseringsmeddelandet med hjälp av ASP.NET serverdel.

    - (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. Uppdatera åtgärden för knappen Skicka meddelande så att den använder ASP.NET serverdel och skicka till valfri PNS som aktiveras av en växel.

    - (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 I funktionen lägger du till följande för att instansiera instansen RegisterClient och ange ombudet för textfälten.

    self.UsernameField.delegate = self;
    self.PasswordField.delegate = self;
    self.RecipientField.delegate = self;
    self.registerClient = [[RegisterClient alloc] initWithEndpoint:BACKEND_ENDPOINT];
    
  14. Ta nu bort allt innehåll i AppDelegate.mmetoden application:didRegisterForPushNotificationWithDeviceToken: och ersätt det med följande (för att se till att kontrollanten innehåller den senaste enhetstoken som hämtats från 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. Kontrollera slutligen att du har följande metod i AppDelegate.m:

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

Testa programmet

  1. I XCode kör du appen på en fysisk iOS-enhet (push-meddelanden fungerar inte i simulatorn).

  2. I användargränssnittet för iOS-appen anger du samma värde för både användarnamn och lösenord. Klicka sedan på Logga in.

    iOS-testprogram

  3. Du bör se ett popup-fönster som informerar dig om att registreringen lyckades. Klicka på OK.

    iOS-testmeddelande visas

  4. I textfältet *Mottagarens användarnamntagg anger du den användarnamnstagg som används med registreringen från en annan enhet.

  5. Ange ett meddelande och klicka på Skicka meddelande. Endast de enheter som har en registrering med taggen mottagaranvändarnamn får meddelandet. Den skickas bara till dessa användare.

    iOS-testtaggad avisering

Nästa steg

I den här självstudien har du lärt dig mer om push-meddelanden till specifika användare som har taggar associerade med sina registreringar. Information om hur du skickar platsbaserade meddelanden finns i nästa självstudie: