August 2015

Band 30, Nummer 8

Azure Mobile Services – Azure Mobile Services: Hervorragendes Back-End für AngularJS

Von Jonathan Miller

AngularJS ist ein hervorragendes Framework zum Entwickeln von JavaScript-Anwendungen für das Web und Mobilgeräte. Es ist sehr leistungsfähig, bringt aber etwas Lernaufwand mit sich. Zum Einstieg habe ich Blogs und Bücher gelesen und Videoschulungen verfolgt, die das Erlernen clientseitiger Features wie z. B. Formulare, Routing und Validierung erleichtert. Leider scheinen die clientseitigen Themen stets die Back-End-Aspekte zu überlagern, denn die meisten Lernressourcen gehen kaum darauf ein. In einem Kurs wurde der Angular-Dienst "$httpBackend" fast ausschließlich verwendet. "$httpBackend" bietet eine tolle Möglichkeit, Test-Fakes zu erstellen, eignet sich aber nicht für das dauerhafte Speichern von Daten für eine Produktionsanwendung. Eine andere Ressource verwendete ein Open-Source-Produkt namens deployd (deployd.com), das eine schnelle und einfache Möglichkeit bietet, einen REST/API-Back-End-Server in Betrieb zu nehmen. deployd kann kostenlos heruntergeladen und auf einem Entwicklungscomputer oder Server ausgeführt werden. Es eignet sich hervorragend für das Entwickeln und Testen von REST-APIs. Das Problem ist wiederum die fehlende Eignung für Produktionsumgebungen. In einer Produktionsumgebung muss ich für die zu verwendende AngularJS-Anwendung einen REST-/JSON-Server im Internet verfügbar machen, möchte mich aber nicht um das Hosten und Verwalten von Servern im Internet kümmern. Ich möchte neue Anwendungen schnell hochfahren und nach Bedarf skalieren. Ich brauche integrierte Sicherheit ohne viel Komplexität. Ich möchte REST-/JSON-APIs zum Speichern von Anwendungsdaten einrichten können. Und ich möchte alle diese Aspekte einfach erlernen und in meine Anwendung integrieren. Bei meiner Recherche fand ich zum Glück heraus, dass Microsoft bereits alle diese Anforderungen erfüllt. In diesem Artikel zeige ich Ihnen, wie das Azure Mobile Services-Back-End und ein AngularJS-Front-End integriert werden.

Azure Mobile Services (AMS) ist wirklich eine Komplettlösung für das Back-End. Sie vereint alle Back-End-Komponenten, die für eine Produktionsanwendung benötigt werden, und verfügt über eine Reihe überzeugender Features:

  • Sehr schneller und redundanter Cloudspeicher
  • Umfassende Vereinfachung des Erstellens von Tabellen, auf die über REST/JSON zugegriffen werden kann
  • Integrierte Sicherheit und Authentifizierung bei beliebten Anbietern wie z. B. Microsoft, Google, Facebook und Twitter
  • Kostenloser Einstieg mit Skalierungsmöglichkeiten für anspruchsvolle Anwendungen
  • Erleichterung der serverseitigen Validierung
  • Unterstützung von JavaScript- und .NET-Back-Ends. Microsoft hat die Einrichtung einer AMS-Website und deren Integration in nahezu alle Clientplattformen stark vereinfacht.

Meine Angular Notes-App

Um das Verbinden von AngularJS und AMS zu veranschaulichen, stelle ich eine sehr einfache Angular Notes-Anwendung zusammen. Diese App soll aus einer einzelnen Seite und einer Liste mit Notizen bestehen. Neben jeder Notiz soll sich die Schaltfläche "Löschen" befinden. Außerdem soll ein Textfeld zum Hinzufügen einer neuen Notiz zur Liste vorhanden sein. Abbildung 1 zeigt, wie meine Angular Notes-App aussieht.

Angular Notes-App
Abbildung 1: Angular Notes-App

Für diese App verwende ich Visual Studio 2013 Update 4 und das Azure-Konto, das zu meinem MSDN-Leistungsumfang gehört. Um folgen zu können, müssen Sie zuerst eine neue ASP.NET-Webanwendung erstellen. Wählen Sie die leere Vorlage und keine Optionen (siehe Abbildung 2).

Erstellen ein leeres ASP.NET-Projekts
Abbildung 2: Erstellen ein leeres ASP.NET-Projekts

Nun müssen Sie die AngularJS- und Bootstrap-Bibliotheken hinzufügen, wozu Sie die NuGet-Pakete für "Angular.Core" und "Bootstrap" hinzufügen.

Um die anfängliche Ansicht zu erstellen, fügen Sie dem Stammverzeichnis des Projekts "notes.html" eine neue HTML-Seite hinzu. Ändern Sie den HTML-Code entsprechend Abbildung 3. Beachten Sie, dass auf Angular und Bootstrap verwiesen wird. Der Code enthält außerdem das Tag "ng-app", das Angular anweist, diese Seite zu verarbeiten. Schließlich enthält der Code den Abschnitt "body" mit dem Tag "ng-controller" für den Controller, den ich später erstellen werde. Ich habe für eine bessere Optik einige Bootstrap-Klassen eingestreut. Sie sind nicht erforderlich und können ignoriert werden.

Abbildung 3: Anfängliches HTML-Layout

<html ng-app="notesApp">
<head>
  <title>Angular Notes</title>
  <link type="text/css" rel="stylesheet" href="Content/bootstrap.css" />
  <script src="Scripts/angular.js"></script>
  <script src="notesCtrl.js"></script>
</head>
<body ng-controller="notesCtrl as vm">
  <div class="page-header text-center">
    <h1>
      <span class="glyphicon glyphicon-cloud" aria-hidden="true"></span>
      <span style="padding-bottom:10px">Angular Notes</span>
    </h1>
  </div>
</body>
</html>

Um dem Abschnitt die Liste mit Notizen hinzuzufügen, fügen Sie unten einen Abschnitt "div" hinzu (siehe Abbildung 4). Dieser "div"-Code durchläuft die Liste mit Notizen und zeigt für jede eine Tabellenzeile an. Dieser Code fügt auch die Schaltfläche "Löschen" für jede Zeile hinzu. Das Tag "key", durch das dies funktioniert, ist das Tag "ng-repeat", welches das Notizenarray auf dem Controller in einer Schleife durchläuft.

Abbildung 4: Hinzufügen der Liste der Notizen

<div class="container">
  <div class="panel panel-default">
    <table class="table table-striped">
      <tr class="list-group" ng-repeat="note in vm.notes">
        <td>
          {{note.notetext}}
          <button class="btn btn-xs btn-danger pull-right"
            ng-click="vm.deleteNote(note)">
            <span class="glyphicon glyphicon-remove" aria-hidden="true"></span>
          </button>
        </td>
      </tr>
    </table>
  </div>
</div>

Schließlich fügen Sie zum Erstellen eines neuen Notizenfelds dieser Ansicht einen letzten "div"-Abschnitt hinzu, damit ein Benutzer eine neue Notiz erstellen kann. Platzieren Sie ihn über dem "div"-Abschnitt für die Tabelle "notes". Beachten Sie im folgenden Code, dass die Daten im Inhalt des Eingabefelds "vm.addNoteText" gebunden sind und dass durch Klicken auf die Schaltfläche oder Drücken der EINGABETASTE die "vm.addNote"-Methode auf dem Controller aufgerufen wird:

<div class="input-group" style="padding-bottom:15px">
  <input type="text" class="form-control" ng-model="vm.addNoteText"
    placeholder="new note" ng-keypress="($event.which === 13)?vm.addNote():0" />
  <span class="input-group-btn">
    <button ng-click="vm.addNote()" class="btn btn-success">Add Note</button>
  </span>
</div>

Um den Controller hinzuzufügen, erstellen Sie im Stammverzeichnis des Projekts "notesCtrl.js" eine neue JavaScript-Datei. Abbildung 5 zeigt den Code für den gesamten Controller. Er besteht aus dem anfänglichen anzuzeigenden Notizenarray, der "addNote"-Funktion, die ein Element zum Array hinzufügt, und der "deleteNote"-Funktion, die eine Notiz aus dem Array entfernt. Stellen Sie sicher, dass ein Verweis auf dieses Skript in der Ansicht "notes.html" vorhanden ist.

Abbildung 5: Hinzufügen des Controllers

angular.module('notesApp', [])
  .controller('notesCtrl', function () {
    var vm = this;
    vm.addNoteText = '';
    vm.notes = [
      { "notetext": "Fix driveway" },
      { "notetext": "Replace roof" },
      { "notetext": "Fix dryer" },
      { "notetext": "Tear out deck" },
      { "notetext": "Add electricity to garage" }
    ];
    vm.addNote = function () {
      if (vm.addNoteText !== '') {
          vm.notes.push({ "notetext": vm.addNoteText });
          vm.addNoteText = '';
      }
    }
    vm.deleteNote = function (note) {
      vm.notes.splice(vm.notes.indexOf(note), 1);
    }
  });

An diesem Punkt ist die Angular-Hauptanwendung fertig gestellt. Bei Ausführen der Seite wird eine Liste mit Notizen angezeigt. Durch Klicken auf das rote X neben einer Notiz wird diese entfernt. Bei Eingeben einer neuen Notiz in das Textfeld und Klicken auf "Add Note" wird sie der Liste hinzugefügt. Bisher ist befindet sich die Liste allerdings nur im Speicher. Bei einer Aktualisierung der Seite wird die ursprüngliche Liste wiederhergestellt, und alle Änderungen gehen verloren. Lassen Sie uns nun die Datenspeicherung aus dem Arbeitsspeicher und in die Cloud verlagern.

Speichern von Daten in Azure Mobile Services

Ich werde den Speichermechanismus für die Notizen ändern. Statt eines statischen Arrays im Arbeitsspeicher werde ich meine Notizen in AMS laden und speichern.

Erstellen Sie im Azure-Portal einen neuen Azure Mobile Service. Stellen Sie sicher, dass dieser das Back-End "JavaScript" verwendet. Klicken Sie als Nächstes auf die Registerkarte "Daten" innerhalb des mobilen Diensts, und erstellen Sie dann, wie in Abbildung 6 gezeigt, eine neue Datentabelle.

Erstellen einer neuen Tabelle "notes"
Abbildung 6: Erstellen einer neuen Tabelle "notes"

Um die Textspalte "note" hinzuzufügen, fügen Sie eine Zeichenfolgenspalte mit dem Namen "notetext" der Tabelle "notes" hinzu.

Nun müssen Sie den Anwendungsschlüssel abrufen. Auf der Hauptseite des Azure-Portals von Azure Mobile Services sehen Sie eine Schaltfläche mit der Bezeichnung "Schlüssel verwalten". Klicken Sie darauf, um den Anwendungsschlüssel abzurufen und zur späteren Verwendung zu speichern. Die Angular-Anwendung benötigt ihn für den Zugriff auf AMS.

Damit AMS in der Angular-App funktioniert, müssen Sie der Ansicht "Notes.html" zwei Skriptverweise hinzufügen. Der erste ist für die JavaScript-Bibliothek, die von Microsoft für Azure Mobile Services bereitgestellt wurde. Der zweite ist ein Dienst im Angular-Stil, der die Microsoft-Bibliothek umschließt. Der Hauptvorteil dieser Bibliothek ist, dass sie die Microsoft-JavaScript-Bibliothek für AMS erweitert und Schnittstellen und Zusagen im Angular-Stil bietet. Ausgezeichneten Beispielcode für diese Bibliothek finden Sie auf GitHub unter bit.ly/1po76vI.

Stellen Sie sicher, dass diese neuen Einträge zwischen den Verweisen auf "angular.js" und "notesCtrl.js" wie folgt vorhanden sind:

<script src="Scripts/angular.js"></script>
<script src="https://ajax.aspnetcdn.com/ajax/mobileservices/MobileServices.Web-1.1.2.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-azure-mobile-service/1.3.4/angular-azure-mobile-service.min.js"></script>
<script src="notesCtrl.js"></script>

Zum Verweisen auf Azure Mobile Services im Controller ändern Sie die erste Zeile von "NotesCtrl.js", indem Sie eine Abhängigkeit zu "azure-mobile-Service.module" hinzufügen:

angular.module('notesApp', ['azure-mobile-service.module'])

Fügen Sie an das Ende der Datei "notesCtrl.js" eine Konstante mit der URL der Azure Mobile-Website und dem zuvor abgerufenen Anwendungsschlüssel hinzu. Die AMS-Bibliothek verwendet diese Elemente für den Zugriff auf die AMS-Website:

angular.module('notesApp').constant('AzureMobileServiceClient', {
  API_URL: "https://angularnotes.azure-mobile.net/",
  API_KEY: "gkwGJioLD3jNxrAX6krXh6jVk6SFkeQr",
});

Ersetzen Sie jetzt den Code im Controller, der "vm.notes" auf ein statisches Array mit aus AMS abgerufenen Daten festlegt. Dieser Code ruft die gesamte Tabelle "notes" ab und legt das Ergebnis im Array "vm.notes" ab:

Azureservice.query('notes', {})
.then(function (items)
{
  vm.notes = items;
});

Als Nächstes ändern Sie die "vm.addNote"-Funktion so, dass neue Notizen in der Datentabelle "notes" gespeichert werden. Sobald AMS den Erfolg des Vorgangs meldet, wird das Element dem Array im Arbeitsspeicher hinzugefügt. Auf diese Weise muss die Anwendung nicht immer die gesamte Liste erneut laden, sobald ihr etwas hinzugefügt wird:

vm.addNote = function () {
  if (vm.addNoteText !== '') {
    Azureservice.insert('notes', {
      "notetext" : vm.addNoteText                 
    }).then(function (newitem) {
      vm.notes.push(newitem);
      vm.addNoteText = '';
    });
  }
}

Ändern Sie schließlich die "vm.deleteNote"-Funktion so, dass die Notiz aus der AMS-Tabelle "notes" gelöscht wird. Wiederum wartet der Code, bis AMS Erfolg meldet, und löscht die Notiz dann aus dem Array im Arbeitsspeicher:

vm.deleteNote = function (note) {
  Azureservice.del('notes', {
    "id": note.id
  }).then(function () {
    vm.notes.splice(vm.notes.indexOf(note), 1);
  });
}

Jetzt werden alle Notizen aus der Tabelle "notes" in AMS abgerufen. Wenn ein Benutzer eine Notiz hinzufügt oder löscht, erfolgen diese Vorgänge für die Datentabelle in AMS. Dafür musste ich nur sehr wenig Code schreiben. Dies sind zwei der wichtigsten Vorteile von AMS: einfache Einrichtung und Integration.

Authentifizieren von Benutzern in Azure Mobile Services

Das Hinzufügen der Authentifizierung zu einer Web-App kann mühsam sein. Es stellt sich immer die Frage, ob eine eigene Lösung entwickelt werden sollte. Doch heutzutage, außer wenn wirklich überzeugende Gründe vorliegen, ist es fast immer sinnvoll, einen der wichtigsten Identitätsanbieter auf dem Markt zu verwenden. Diese Anbieter kennen sich mit dem sicheren Speichern und Zurücksetzen von Kennwörtern umfassend aus. AMS erleichtert das Verbinden mit den beliebten Identitätsanbietern Microsoft, Facebook, Twitter und Google. AMS integriert nahtlos die Authentifizierung und Autorisierung mit der Anmeldefunktion und den in AMS erstellten Tabellen. Für diesen Artikel habe ich ein Microsoft-Konto für die Authentifizierung verwendet. Nachdem die Authentifizierung konfiguriert ist, ändere ich das Beispiel so, dass nur authentifizierte Benutzer Notizen anzeigen oder bearbeiten und ihre eigene Liste anzeigen können.

Der erste Schritt ist das Einrichten Ihrer App im Microsoft Live-Portal (bit.ly/1JS4jq3). Dadurch werden die Client-ID und der geheime Clientschlüssel bereitgestellt, die/den AMS benötigt, damit die Microsoft-Identitätsprüfung funktioniert. Fügen Sie diese Angaben nun auf der Registerkarte "Identität" in AMS ein (siehe Abbildung 7). Es gibt unter bit.ly/1Ij22hy einen empfehlenswerten Artikel, der Sie durch die Registrierung der Anwendung begleitet.

Einrichten des Microsoft-Identitätsanbieters
Abbildung 7: Einrichten des Microsoft-Identitätsanbieters

Fügen Sie als Nächstes der Ansicht Schaltflächen für die An- und Abmeldung hinzu. Fügen Sie den folgenden "div"-Code über dem "div"-Code hinzu, der die Tabelle "notes" enthält:

<div class="text-center">
  <button class="btn btn-primary" ng-if="!vm.isLoggedIn()"
  ng-click="vm.login()">Login</button>
  <button class="btn btn-primary" ng-if="vm.isLoggedIn()"
  ng-click="vm.logout()">Logout</button>
</div>

Der "ng-if"-Code für die Anmeldeschaltfläche bewirkt, dass die Anmeldeschaltfläche nur angezeigt wird, wenn der Benutzer nicht angemeldet ist. Der "ng-if"-Code für die Abmeldeschaltfläche bewirkt, dass die Abmeldeschaltfläche nur angezeigt wird, wenn der Benutzer angemeldet ist.

Fügen Sie nun ein weiteres "ng-if"-Tag zum "div"-Container hinzu, um die Liste und das Textfeld "New Note" auszublenden, wenn der Benutzer nicht angemeldet ist. Dies ist keine Sicherheitsfunktion, denn Sicherheit wird von AMS erzwungen. Hierdurch sieht die Seite lediglich zweckmäßiger aus:

<div class="container" ng-if="vm.isLoggedIn()" style="padding:15px">

Fügen Sie nun dem Controller die Authentifizierungsfunktionen hinzu. Die "isLoggedIn"-Funktion wird von der Ansicht verwendet, um festzustellen, ob die Schaltfläche für Anmeldung/Abmeldung und die Notizenliste ein- oder ausgeblendet werden sollen. Die Funktion gibt bloß das "IsLoggedIn"-Ergebnis aus dem Modul "Azureservice" zurück:

vm.isLoggedIn = function ()
{
  return Azureservice.isLoggedIn();
}

Die Anmeldungsfunktion der App wird aufgerufen, wenn der Benutzer auf die Anmeldungsschaltfläche klickt. Dadurch wird die "login"-Funktion in der Bibliothek "Azureservice" aufgerufen. Verschieben Sie den Code, der AMS auf die Notizenliste abfragt, vom oberen Rand des Controllers in diese Funktion. Nun wird die Liste erst geladen, nachdem sich der Benutzer erfolgreich authentifiziert hat:

vm.login = function () {
  Azureservice.login('microsoftaccount')
  .then(function () {
    Azureservice.query('notes', {})
    .then(function (items) {
      vm.notes = items;
    });
  });
}

Die "logout"-Funktion meldet den Benutzer von AMS ab, indem die "logout"-Funktion im Modul "Azureservice" aufgerufen wird. Die Funktion leert auch das Notizenarray:

vm.logout = function () {
  vm.notes = [];
  Azureservice.logout();
}

Bislang ist das Einzige, das einen nicht authentifizierten Benutzer an der Nutzung der Notizenliste hindert, dass der Code die Liste erst lädt, nachdem der Benutzer authentifiziert wurde. Dies ist aber nicht besonders sicher. Es ist viel besser, Sicherheitseinstellungen von AMS im Back-End erzwingen zu lassen. Öffnen Sie im Azure-Portal den AMS-Dienst, öffnen Sie die Tabelle "notes", und klicken Sie auf "Berechtigungen". Ändern Sie alle Berechtigungen in "Nur authentifizierte Benutzer" (siehe Abbildung 8). Nun misslingen alle Aufrufe diese Tabelle, wenn der Benutzer nicht authentifiziert ist.

Schützen der Tabelle "notes"
Abbildung 8: Schützen der Tabelle "notes"

Trennen von Benutzerdaten

Wenngleich die Websiteauthentifizierung funktioniert, verwenden alle Benutzer nach wie vor eine einzelne Liste. Lassen Sie uns nun einrichten, dass jeder Benutzer nur seine eigenen Notizen anzeigen und bearbeiten kann. Dies erfordert nur wenige Änderungen im Azure-Portal. Die Angular-App ändert sich überhaupt nicht.

Wechseln Sie zunächst zur Datentabelle "notes". Klicken Sie auf "Spalten", und fügen Sie eine neue Zeichenfolgenspalte namens "userid" hinzu. Auf diese Weise ordne ich eine Notiz einem Benutzer zu. Öffnen Sie als Nächstes die Registerkarte "Skript" für die Tabelle "notes". Wählen Sie in der Dropdownliste mit den Vorgängen EINFÜGEN aus, und fügen Sie dem Skript Folgendes hinzu:

function insert(item, user, request) {
  item.userid = user.userId;
  request.execute();
}

Diese neue Zeile dient zum Festlegen der Benutzer-ID der neuen Zeile auf die Benutzer-ID im Authentifizierungsanbieter. Es ist viel sicherer, diese Einstellung im Code im Back-End vorzunehmen. Der Code wird in Azure ausgeführt, sodass der Benutzer (oder Angreifer) nicht darauf zugreifen kann.

Um die anhand der Benutzer-ID zurückgegebenen Notizen zu filtern, wählen in der Dropdownliste mit den Vorgängen LESEN aus und ändern das Skript:

function read(query, user, request) {
  query.where({ userid: user.userId });
  request.execute();
}

Die neue Zeile "query.where" filtert die von der Spalte "userid" zurückgegebenen Zeilen. Die Benutzer-ID des angemeldeten Benutzers wird als der Wert angegeben. Das Filtern der Daten auf dem Server, bevor sie den Client erreichen, ist viel sicherer als zu versuchen, dies im Clientcode zu erreichen.

Nun speichert die Angular Notes-Anwendung Notizen sicher in Azure Mobile Services. Jeder Benutzer hat eine eigene Liste mit Notizen, auf die nur nach vorheriger Authentifizierung zugegriffen werden kann.

Zusammenfassung

Für diese in der Cloud ausgeführte Demoanwendung mit erstklassiger Datenspeicherung und Authentifizierung ist nur eine kleine Menge Code erforderlich. Machen Sie nicht den Fehler zu glauben, dass wenn Sie AngularJS auf Ihrem Front-End verwenden möchten, Sie sich vom Microsoft-Stapel im Back-End verabschieden müssen. AMS und AngularJS lassen sich reibungslos integrieren. Azure Mobile Services ist ein hervorragendes Back-End für AngularJS-Anwendungen.


Jonathan Miller ist leitender Softwarearchitekt bei CuroGens in Indianapolis. Er entwickelt seit über 10 Jahren Produkte für den Microsoft-Stapel und programmiert für .NET seit dessen Einführung. Miller entwickelt Produkte für den gesamten Stapel und ist Experte für Front-End-Technologien (Windows Forms, Windows Presentation Foundation, Silverlight, ASP.NET, AngularJS/Bootstrap), Middleware (Windows-Dienste, Web-API) und Back-Ends (SQL Server, Microsoft Azure).

Unser Dank gilt den folgenden technischen Experten von Microsoft für die Durchsicht dieses Artikels: David Crawford und Simon Gurevich
Simon Gurevich ist seit 15 Jahren bei Microsoft und bekleidet derzeit die Position Principal Consultant in den Microsoft Consulting Services. Sein Spezialgebiet sind Technologien für die Azure-Plattform. Im Lauf seiner Karriere hat Simon auf der Microsoft-Plattform verteilte Anwendungsarchitekturen entwickelt und in den letzten Jahren eng mit der Azure-Produktgruppe zusammengearbeitet. Bevor er zu Microsoft kam, hatte Simon eine Position im Bereich "Forschung und Entwicklung" in einem Labor für Computertelefonie inne. Seine Aufgabe war die Entwicklung serverbasierter Anwendungen für die umfassende Weiterleitung von Telefonanrufen. Simon hat einen Master in Informatik und Mathematik.