August 2015

Band 30, Nummer 8

Dieser Artikel wurde maschinell übersetzt.

Wolke-verbundene Mobile Apps - erstellen Sie einen Webdienst mit azurblauen Web Apps und WebJobs

Von Erik Reitan

Heute sind viele mobile Anwendungen auf einen oder mehrere Webdienste verbunden, die wertvolle und interessante Daten bereitstellen. Bei der Konzeption und Entwicklung solcher Anwendungen, ist der einfachste Ansatz machen direkte REST-API-Aufrufe zu diesen Dienstleistungen und dann verarbeiten der Antwort im Client. Dieser Ansatz hat jedoch eine Reihe von Nachteilen. Zum einen verbraucht jedes Netzwerkaufruf und jedes Bit der clientseitige Verarbeitung kostbaren Batteriestrom und Bandbreite. Darüber hinaus kann umfangreiche clientseitige Verarbeitung etwas dauern durchführen, vor allem auf low-End-Hardware, dass die app weniger zu reagieren. Und verschiedene Web-Dienste möglicherweise Drosselung Einschränkungen auferlegen, was bedeutet, dass eine reine Client-seitige Lösung nicht ohne weiteres auf eine größere Anzahl von Benutzern skaliert wird.

Daher ist es sinnvoll in vielen Szenarien — vor allem solche, die Daten aus mehreren Quellen ziehen — eigene Back-End erstellen, können Sie bestimmte Aufgaben auslagern. Als Teil unserer Arbeit bei Microsoft als ein Team, das Inhalte für ASP.NETMicrosoft Azure und Cross-Plattform-Entwicklung-Tools in Visual Studioentwickelt, haben wir ein spezielles Beispiel für diesen Ansatz.

In diesem zweiteiligen Artikel diskutieren wir unseren Ansatz, einige der Herausforderungen, denen wir begegnet und einige der Lektionen, die wir bei der Entwicklung unserer Anwendung gelernt. Diese Anwendung, die wir mit dem Namen "Altostratus" (eine interessante Art der Wolke), sucht Stack Overflow und Twitter zu speziellen Themen, die wir nennen "Gespräche." Wir haben diese zwei Anbieter, weil beide gute Web-APIs haben. Die Anwendung besteht aus zwei Hauptkomponenten:

  • Eine Wolke zurück zu beenden, auf Azure gehostet. Die Back-End stellt Anforderungen an die Anbieter in regelmäßigen Abständen und aggregiert die Daten in das Formular, das für den Kunden am besten geeignet ist. Dies vermeidet Drosselung Bedenken, irgendwelche Bedenken hinsichtlich der Wartezeit bei den Anbietern reduziert, minimiert die clientseitige Verarbeitung und reduziert die Anzahl der Netzwerkanforderungen vom Client. Ein Nachteil ist, dass die WebJob alle paar Stunden läuft, damit Sie nicht in Echtzeit Daten bekommen.
  • Eine leichte mobile Client-app erstellt mit Xamarin Ausführung auf Windows, Android und iOS (siehe Abbildung 1). Die mobile Client holt die aggregierten Daten aus Back-End und stellt es für den Benutzer. Es hält auch einen synchronisierten Cache der Daten in einer lokalen Datenbank für eine gute offline Erfahrung und schnellere Startzeiten.

die Xamarin Mobile Client ausgeführt auf einem Android Tablet (links), ein Windows Phone (Mitte) und ein iPhone (rechts)
Abbildung 1 die Xamarin Mobile Client ausgeführt auf einem Android Tablet (links), ein Windows Phone (Mitte) und ein iPhone (rechts)

Benutzer können optional in dem mobilen Client mit sozialen Anbietern (Google oder Facebook) anmelden. Wenn angemeldet, können Benutzer Einstellungen festlegen, die Kommunikation mit dem Kunden weiter zu optimieren. Insbesondere können sie auswählen, welche Themenbereiche zu erhalten und die maximale Anzahl von Gesprächen innerhalb jeden Themenbereich. Benutzereinstellungen werden im Backend gespeichert, sodass ein Benutzer kann von einem beliebigen Client anmelden und die gleiche Erfahrung erhalten. Um diese Idee zu demonstrieren, haben wir auch einen einfachen WebClient, der mit dem gleichen Backend spricht.

Im Rahmen dieses Projektes wollten wir auch die Anwendung Lifecycle Management (ALM) Tools integriert Visual Studio und Visual Studio Online verwenden, sprintet und Backlog Arbeit verwalten und automatisierte Komponententests, kontinuierliche Integration (CI) und kontinuierliche Bereitstellung (CD).

In diesem zweiteiligen Artikel erforscht die Details unseres Projektes. Teil 1 konzentriert sich auf die Back-End und unsere Verwendung von ALM-Tools (DevOps). Wir reden auch über einige der Herausforderungen, die wir gefunden und einige Lektionen gelernt haben, wie z.B.:

  • Wie Sie sicher automatisieren der Bereitstellung von Kennwörtern auf nicht-Web-apps.
  • Umgang mit Azure Automatisierung Timeouts.
  • Effiziente und informative Hintergrundverarbeitung.
  • CI/CD Einschränkungen und Problemumgehungen.

Teil 2 diskutieren wie wir Xamarin verwendet, um mehrere mobile Client, Zielplattformen einschließlich Authentifizierung und einen synchronisierten clientseitigen Cache Daten pflegen.

Architektur

Abbildung 2 zeigt die allgemeine Architektur für die Altostratus-Lösung.

  • Auf Back-End verwenden wir Azure App Service zum Hosten der Webanwendung und Azure SQL-Datenbank zum Speichern von Daten in einer relationalen Datenbank. Wir verwenden Entity Framework (EF) für den Datenzugriff.
  • Wir verwenden Azure WebJobs geplante Hintergrundtask ausgeführt, die aggregiert Daten aus Stack Overflow und Twitter und schreibt es in die Datenbank. Die WebJob kann leicht zum Aggregieren von Daten aus zusätzlichen Quellen erweitert werden.
  • Die mobile Client wird mit Xamarin erstellt und kommuniziert mit dem Back-End mit einer einfachen REST-API.
  • Die REST-API wird mit ASP.NET Web API implementiert ist ein Framework zum Erstellen von HTTP-Diensten in der Microsoft .NET Framework.
  • Der Web-Client ist eine relativ einfache JavaScript-app. Wir haben die KnockoutJS-Bibliothek für die Datenbindung und jQuery für AJAX-Aufrufe verwendet.

Altostratus-Architektur
Abbildung 2 Altostratus-Architektur

Das Datenbankschema

Wir verwenden das Datenbankschema zu definieren und Verwalten der SQL-Datenbank-Backend EF Code erste. Als Abbildung 3 zeigt, die Datenbank speichert die folgenden Entitäten:

  • Anbieter: Eine Datenquelle wie Twitter oder Stack-Überlauf.
  • Gespräch mit: Ein Element von einem Anbieter. Für Stack Overflow entspricht dies einer Frage mit Antworten. Für Twitter entspricht er einem Tweet. (Es könnte auch sein, ein Tweet mit Antworten, aber wir nicht dieses Feature implementieren.)
  • Kategorie: Das Thema für ein Gespräch, z. B. "Azure" oder "ASP.NET."
  • Kategorie: Eine Suchzeichenfolge für eine bestimmte Kategorie und Anbieter. Diese entsprechen Tags in Stack Overflow ("Azurblau-Web-Sites") und Hash-Tags in Twitter ("#azurewebsites"). Die Back-End verwendet diese, um den Anbietern Abfragen. Der Benutzer sieht sie nicht.
  • UserPreference: Benutzerspezifische Einstellungen speichert.
  • UserCategory: Definiert eine Join-Tabelle für UserPreference und Kategorie.

das Altostratus-Datenmodell
Abbildung 3 das Altostratus-Datenmodell

Im Allgemeinen, die Create, Read, Update, ist Delete (CRUD) Code in der Anwendung von Altostratus typisch für EF Code ersten. Eine besondere Aufmerksamkeit hat zu tun mit der Handhabung der Datenbank seeding und Migrationen. Code wird zunächst erstellt und Samen eine neue Datenbank, wenn keine Datenbank zum ersten Mal vorhanden ist, das wenn ein Programm versucht, auf Daten zuzugreifen. Weil der erste Versuch, Zugriff auf Daten in den WebJob passieren könnte, haben wir Code in der WebJob Main-Methode zu verwenden die MigrateDatabase EF­ToLatestVersion Initialisierung:

static void Main()
{
  Task task;
  try
  {
    Database.SetInitializer<ApplicationDbContext>(
      new MigrateDatabaseToLatestVersion<ApplicationDbContext,
        Altostratus.DAL.Migrations.Configuration>());

Ohne diesen Code würde die WebJob standardmäßig die CreateDatabaseIfNotExists Initialisierung verwenden, die die Datenbank seed nicht. Das Ergebnis wäre eine leere Datenbank und Störungen, wenn die Anwendung versucht, Daten aus leere Tabellen zu lesen.

Entwerfen der WebJob

WebJobs bietet eine ideale Lösung für die laufenden Tasks im Hintergrund, ist die Arbeit, die zuvor einer dedizierten Azure Worker Rolle ausführen erfordert hätten. Sie können auf eine Azure Web app ohne zusätzliche Kosten WebJobs ausführen. Erfahren Sie über die Vorteile bieten WebJobs über Worker-Funktionen bei Troy jagt Blog-Post, "Azure WebJobs sind Awesome und beginnen Sie jetzt mit ihnen Recht!" (Bit.ly/1c28yAk).

Die WebJob für unsere Lösung führt in regelmäßigen Abständen drei Funktionen: Twitter-Daten abrufen, Stack Overflow Daten abrufen und bereinigen. Diese Funktionen sind unabhängig, aber sie müssen sequenziell ausgeführt werden, weil sie demselben EF Kontext teilen. Nach Abschluss einer WebJob Azure Portal zeigt den Status der einzelnen Funktionen (siehe Abbildung 3). Wenn die Funktion abgeschlossen ist, wird es mit einem grünen Erfolgsmeldung gekennzeichnet, und wenn eine Ausnahme ausgelöst wird, wird es mit einer roten Meldung fehlgeschlagen markiert.

Ausfälle sind nicht selten in Altostratus, weil wir die kostenlose Anbieter-Stack Overflow und Twitter APIs verwenden. Abfragen sind insbesondere beschränkt: Wenn Sie die Abfrage überschritten, geben die Anbieter einen Drosselung Fehler zurück. Dies war ein Hauptgrund für das Erstellen von Back-End an erster Stelle. Es wäre einfach für eine mobile app Anfragen direkt an diese Anbieter vornehmen, konnte eine wachsende Zahl von Nutzern schnell die Drosselung Grenze erreichen. Stattdessen kann die Back-End machen nur wenige regelmäßige Abfragen zu erfassen und die Daten zu aggregieren.

Ein Problem, auf die wir gestoßen war um WebJob Fehlerbehandlung. Normalerweise, wenn irgendeine Funktion in der WebJob eine Ausnahme auslöst, die WebJob Instanz beendet, die restlichen Funktionen nicht ausführen und der gesamte WebJob Testlauf wird als fehlerhaft markiert. Um alle Aufgaben ausführen und zeigen einer Störungsmeldung an die Funktionsebene der WebJob Ausnahmen abfangen muss. Wenn irgendeine Funktion in Main auslöst, protokollieren wir die letzte Ausnahme und erneut auszulösen, so dass die WebJob als fehlgeschlagen markiert wird. Der Pseudo-Code in Abbildung 4 veranschaulicht diesen Ansatz. (Projekt-Download enthält den vollständigen Code).

Abbildung 4 abfangen und erneutes Auslösen von Ausnahmen

static void Main()
{
  Task task;
  try
  {
    Exception _lastException = null;
    try
    {
      task = host.CallAsync("Twitter");
      task.Wait();
    }
    catch (Exception ex)
    {
      _lastException = ex;
    }
    try
    {
      task = host.CallAsync("StackOverflow");
      task.Wait();
    }
    catch (Exception ex)
    {
      _lastException = ex;
    }
    task = host.CallAsync("Purge Old Data");
    task.Wait();
    if (_lastException != null)
    {
      throw _lastException;
    }
  }
  catch (Exception ex)
  {
  }
}

In Abbildung 4, nur die letzte Ausnahme der WebJob Ebene angezeigt wird. Auf der Ebene ist jede Ausnahme angemeldet, also keine Ausnahmen verloren gehen. Abbildung 5 zeigt das Dashboard für eine WebJob, die erfolgreich ausgeführt wurde. Sie können jede Funktion zu sehen der Diagnoseausgabe aufgliedern.

erfolgreiche WebJob Run
Abbildung 5 erfolgreiche WebJob Run

Entwerfen die REST-API

Die mobile app kommuniziert mit dem Back-End durch ein einfaches REST-API, die wir mit ASP.NET Web API 2 implementiert. (Beachten Sie, dass ASP.NET 5, wodurch es einfacher, beide in eine Web-app zu integrieren Web API in MVC-6 Rahmen, eingegliedert ist.)

Abbildung 6 fasst unsere REST-API.

Abbildung 6 REST API Übersicht

Api/Kategorien zu erhalten Ruft die Kategorien.
GET-api/Gespräche? von = Iso-8601-Datum Ruft die Gespräche.
Api/Userpreferences zu erhalten Ruft den Benutzereinstellungen.
Api/Userpreferences setzen Einstellungen des Benutzers aktualisiert.

Alle Antworten sind im JSON-Format. Z. B. Abbildung 7 zeigt eine HTTP-Antwort für Gespräche.

Abbildung 7 eine HTTP-Antwort für Gespräche

HTTP/1.1 200 OK
Content-Length: 93449
Content-Type: application/json; charset=utf-8
Server: Microsoft-IIS/8.0
Date: Tue, 21 Apr 2015 22:38:47 GMT
[
  {
    "Url": "http://twitter.com/815911142/status/590317675412262912",
    "LastUpdated": "2015-04-21T00:54:36",
    "Title": "Tweet by rickraineytx",
    "Body": "Everything you need to know about #AzureWebJobs is here.
      <a href=\"http://t.co/t2bywUQoft\"">http://t.co/t2bywUQoft</a>",
    "ProviderName": "Twitter",
    "CategoryName": "Azure"
  },
  // ... Some results deleted for space
]

Für Gespräche brauchen wir nicht die Anforderung authentifiziert werden. Das bedeutet, dass die mobile app immer aussagekräftige Daten anzeigen kann, ohne dass der Benutzer sich zuerst anmelden. Aber wir wollten auch zeigen, mit dem Back-End zusätzliche Optimierungen durchführen, wenn sich ein Benutzer anmeldet. Unsere Client-app erlaubt, welche Kategorien von Konversationen angezeigt werden, zusammen mit einer Konversation-Grenze zu wählen. Also wenn eine Anforderung authentifiziert ist (d. h. in die app angemeldeten Benutzers), filtert Back-End automatisch die Antwort basierend auf diese Präferenzen. Dies schränkt die Menge der Daten, die vom Client verarbeitet werden müssen, die im Laufe der Zeit reduziert die Anforderungen an Bandbreite, Arbeitsspeicher, Speicher und Batteriebetrieb.

Die Gespräche-API nimmt auch eine optionale "aus" Parameter in der Abfragezeichenfolge. Wenn angegeben, filtert dadurch die Ergebnisse um nur Konversationen, die nach diesem Datum aktualisiert erweitert:

GET api/conversations?from=2015-04-20T03:59Z

Dies minimiert die Größe der Antwort. Unsere mobilen Client verwendet diesen Parameter, um nur die Daten bitten es braucht, um seinen Cache zu synchronisieren.

Wir könnten entworfen haben die API zu Abfragezeichenfolgen verwenden, um den Benutzereinstellungen auf einer Basis pro Anforderung zu kommunizieren was bedeutet, dass diese Einstellungen nur im Client beibehalten werden würde. Das würde die Notwendigkeit für die Authentifizierung vermieden werden können. Während dieser Ansatz für einfache Szenarien gut funktionieren würde, wollten wir ein Beispiel zu geben, die auf kompliziertere Situationen ausgedehnt werden könnte, wo Abfragezeichenfolgen nicht ausreichen würden. Speichern von Einstellungen im Backend bedeutet auch, dass sie automatisch auf jedem Client angewendet sind, wo der gleiche Benutzer anmeldet.

Daten-Objekte übertragen (DTOs)

Die REST-API ist die Grenze zwischen dem Datenbankschema und der Draht-Darstellung. Wir wollten die EF-Modelle direkt zu serialisieren:

  • Sie enthalten Informationen, die der Client nicht, wie Fremdschlüssel benötigen.
  • Sie können die API für übermäßige Entsendung anfällig machen. (Übermäßige Posting ist, wenn ein Client Datenbankfelder, die Sie nicht einziehen aktualisiert, um nach Updates verfügbar zu machen. Es kann auftreten, wenn eine HTTP-Anfrage-Nutzlast direkt in ein EF-Modell zu konvertieren, ohne Überprüfung der Eingabe ausreichend. Siehe bit.ly/1It1wl2 Weitere Informationen.)
  • Die "Form" der EF-Modelle eignen sich für das Erstellen von Datenbanktabellen und nicht optimal für den Client.

Daher haben wir eine Reihe von Datenübertragung Objekte (DTOs), die nur C#-Klassen, die das Format für die REST-API-Antworten zu definieren. Hier ist beispielsweise unser EF-Modell für Kategorien:

public class Category
{
  public int CategoryID { get; set; }
  [StringLength(100)]
  public string Name { get; set; }  // e.g: Azure, ASP.NET
  public ICollection<Tag> Tags { get; set; }
  public ICollection<Conversation> Conversations { get; set; }
}

Die Kategorie-Entität hat einen Primärschlüssel (CategoryID) und Navigationseigenschaften ("Tags" und "Gespräche"). Die Navigationseigenschaften erleichtern die Beziehungen im EF Folgen — z. B. alle Tags für eine Kategorie zu finden.

Fragt der Client nach den Kategorien, braucht es nur eine Liste der Namen der Kategorien:

[ "Azure", "ASP.NET" ]

Diese Konvertierung ist leicht durchzuführen, verwenden eine LINQ -Select-Anweisung in der Web API-Controller-Methode:

public IEnumerable<string> GetCategories()
{
  return db.Categories.Select(x => x.Name);
}

Die UserPreference-Entität ist etwas komplizierter:

public class UserPreference
{
  // FK to AspNetUser table Id   
  [Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
  public string ApplicationUser_Id { get; set; }
  public int ConversationLimit { get; set; }    
  public int SortOrder { get; set; }            
  public ICollection<UserCategory> UserCategory { get; set; }
  [ForeignKey("ApplicationUser_Id")]
  public ApplicationUser AppUser { get; set; }
}

ApplicationUser_Id ist ein Fremdschlüssel in der Tabelle User. UserCategory verweist auf eine Zuordnungstabelle, die eine m: n-Beziehung zwischen Benutzer- und Kategorien erstellt.

Hier ist, wie wir möchten, dass dies an den Client zu suchen:

public class UserPreferenceDTO
{
  public int ConversationLimit { get; set; }
  public int SortOrder { get; set; }
  public ICollection<string> Categories { get; set; }
}

Dies blendet die Dinge, die Implementierungsdetails des Datenbankschemas, wie Fremdschlüssel und Zuordnungstabellen, und fasst zusammen Kategorienamen in eine Liste von Zeichenfolgen.

Die LINQ Ausdruck zum Konvertieren von UserPreference in UserPreference­DTO ist relativ komplex, so dass wir AutoMapper stattdessen verwendet. AutoMapper ist eine Bibliothek, die Objekttypen zugeordnet. Die Idee ist, ein Mapping zu definieren einmal, und dann mit AutoMapper die Zuordnung für Sie zu tun.

Wir konfigurieren AutoMapper, wenn die app gestartet wird:

Mapper.CreateMap<Conversation, ConversationDTO>();
Mapper.CreateMap<UserPreference, UserPreferenceDTO>()
  .ForMember(dest => dest.Categories,
             opts => opts.MapFrom(
               src => src.UserCategory.Select(
                 x => x.Category.Name).ToList()));
              // This clause maps ICollection<UserCategory> to a flat
              // list of category names.

Der erste Aufruf von CreateMap ordnet Gespräch ConversationDTO, AutoMappers Standardkonventionen Zuordnung verwenden. Für UserPreference ist die Zuordnung nicht ganz so einfach, so gibt es einige zusätzliche Konfiguration.

Sobald AutoMapper konfiguriert ist, ist die mapping-Objekte einfach:

var prefs = await db.UserPreferences
  .Include(x => x.UserCategory.Select(y => y.Category))  
  .SingleOrDefaultAsync(x => x.ApplicationUser_Id == userId);
var results = AutoMapper.Mapper.Map<UserPreference,
  UserPreferenceDTO>(prefs);

AutoMapper ist klug genug, dies in eine LINQ -Anweisung umzuwandeln, so wählt der auf die Datenbank passieren.

Authentifizierung und Autorisierung

Wir verwenden eine soziale Login (Google und Facebook), um Benutzer zu authentifizieren. (Der Authentifizierungsvorgang wird in Teil2 dieses Artikels ausführlich beschrieben.) Nach der Web API Auth­Enticates die Anforderung der Web API-Controller können diese Informationen verwenden, um Anfragen zu autorisieren und der Benutzer in der Datenbank zu suchen.

Um eine REST-API auf autorisierte Benutzer beschränken, dekorieren wir die Controllerklasse mit dem Attribut [Authorize]:

[Authorize]
public class UserPreferencesController : ApiController

Wenn eine Anforderung zum api/Userpreferences autorisiert ist nicht, gibt Web API jetzt automatisch einen 401-Fehler:

HTTP/1.1 401 Unauthorized
Content-Type: application/json; charset=utf-8
WWW-Authenticate: Bearer
Date: Tue, 21 Apr 2015 23:55:47 GMT
Content-Length: 68
{
  "Message": "Authorization has been denied for this request."
}

Beachten Sie, dass Web-API einen WWW-Authenticate-Header der Antwort hinzugefügt. Dies teilt dem Client mit, welche Art von Authentifizierungsschema unterstützt wird – in diesem Fall OAuth2 Träger-Token.

In der Standardeinstellung [Authorize] setzt voraus, dass jeder authentifizierter Benutzer auch berechtigt ist und nur anonyme Zugriffe gesperrt sind. Sie können auch einschränken Autorisierung an Benutzer in der angegebenen Rollen (z. B. "Admins") oder implementieren einen benutzerdefinierte Autorisierungsfilter für komplexere Szenarien der Autorisierung.

Die Gespräche-API ist ein interessanter Fall: Wir können anonyme Anfragen, aber zusätzliche Logik anwenden, wenn die Anforderung authentifiziert wird. Der folgende Code überprüft, ob die aktuelle Anforderung authentifiziert ist und wenn ja, die Benutzereinstellungen aus der Datenbank ruft:

if (User.Identity.IsAuthenticated)
{
  string userId = User.Identity.GetUserId();
  prefs = await db.UserPreferences
    .Include(x => x.UserCategory.Select(y => y.Category))
    .Where(x => x.ApplicationUser_Id == userId).SingleOrDefaultAsync();
}

Wenn Prefs ungleich Null ist, verwenden wir die Präferenzen, um die EF-Abfrage zu gestalten. Für anonyme Anforderungen führen wir nur eine Standardabfrage.

Daten-Feeds

Wie bereits erwähnt, zieht unsere app Daten von Stack Overflow und Twitter. Unsere Design-Prinzipien gehörte zu Daten aus mehreren verschiedenen Quellen und in einer einzigen, normalisierte Quelle zu aggregieren. Dies erleichtert die Interaktion zwischen Kunden und Back-End, da Kunden nicht das Datenformat für jeden bestimmten Anbieter wissen müssen. Back-End führten wir ein Providermodell, das macht es einfach, aggregierte zusätzliche Quellen, ohne dass Änderungen an der Web-APIs oder Kunden. -Anbieter machen eine konsistente Schnittstelle verfügbar:

interface IProviderAPI
{
   Task<IEnumerable<Conversation>> GetConversationsAsync(
     Provider provider, Category category,
     IEnumerable<Tag> tags, DateTime from, int maxResults, TextWriter logger);
}

Die Stack-Überlauf und Twitter APIs sorgen für viel Funktion, aber mit dieser Fähigkeit Komplexität kommt. Wir fanden, dass die Pakete StacMan und LINQtoTwitter NuGet, die mit den APIs arbeiten viel einfacher. LINQtoTwitter und StacMan sind gut dokumentiert, aktiv unterstützte open-Source und sind einfach zu bedienen mit c#.

Umgang mit Passwörtern und andere Geheimnisse

Wir folgten den Artikel "Best Practices für die Bereitstellung von Passwörter und andere vertraulichen Daten zu ASP.NET und Azure App-Service" (bit.ly/1zlNiQI), welche Mandate niemals Passwörter im Quellcode überprüfen. Nur in zusätzliche Konfigurationsdateien für lokale Entwicklung-Rechner speichern wir Geheimnisse. Um die app auf Azure bereitstellen, verwenden wir Windows PowerShell oder Azure Portal.

Dieser Ansatz eignet sich für die Web-app. Wir zogen die Geheimnisse aus der web.config-Datei mit dem folgenden Markup:

<appSettings file="..\..\AppSettingsSecrets.config">
</appSettings>

Verschieben der Datei auf zwei Ebenen aus dem Quellverzeichnis bedeutet, es ist völlig aus dem Projektmappenverzeichnis und wird nicht zur Quellcodeverwaltung hinzugefügt werden.

Die app.config-Datei verwendet, die von einer Konsolenanwendung (WebJobs) unterstützt keine relativen Pfade, aber absolute Pfade werden unterstützt. Einen absoluten Pfad können Sie um Ihre Geheimnisse aus Ihrem Projektverzeichnis zu wechseln. Das folgende Markup fügt die Geheimnisse in der C:\secrets\AppSettingsSecrets.config-Datei und nicht-sensibler Daten in der Datei app.config:

<configuration>
  <appSettings file="C:\secrets\AppSettingsSecrets.config">
    <add key="TwitterMaxThreads" value="24" />
    <add key="StackOverflowMaxThreads" value="24" />
    <add key="MaxDaysForPurge" value="30" />
  </appSettings>
</configuration>

Um die Geheimnisse in unserer Windows PowerShell -Skripts zu behandeln mit dem Cmdlet Export-CliXml die verschlüsselte Geheimnisse auf Datenträger und Import-CliXml die Geheimnisse lesen zu exportieren.

Alles automatisieren

DevOps bewährte Methode besteht darin, alles zu automatisieren. Wir schrieben ursprünglich Windows PowerShell -Skripts für die Azure Ressourcen unsere app erfordert (Web-app, Datenbank, Speicher) zu erstellen und alle Ressourcen, z. B. das Festlegen der Verbindungszeichenfolge und die app Einstellungen Geheimnisse einbinden.

Visual Studio -Bereitstellungs-Assistenten macht einen guten Job der Automatisierung der Bereitstellung auf Azure, aber es gibt noch einige manuelle Schritte zum Bereitstellen und konfigurieren die app:

  • Eingeben das Kennwort des Administratorkontos auf Azure SQL-Datenbank.
  • Die Geheimnisse des app-Einstellungen für die Web-app und WebJob eingeben.
  • Zeichenfolgen der WebJob Speicher Konto WebJob Überwachung anschließen.
  • Aktualisieren die Bereitstellungs-URL in Konsolen Entwickler Facebook und Google OAuth soziale Anmeldungen zu aktivieren.

Eine neue Bereitstellung URL müssen Sie Ihre OAuth Provider-Authentifizierung-URL zu aktualisieren, so gibt es keine Möglichkeit, den letzten Schritt zu automatisieren, ohne Verwendung eines benutzerdefinierten Domainnamen. Unsere Windows PowerShell -Skripts erstellen alle Azure Ressourcen, die wir brauchen und sie Haken, damit alles, die automatisiert werden können automatisiert ist.

Das erste Mal, das Sie einer WebJob Visual Studiowerden Sie aufgefordert, einen Zeitplan für die Ausführung der WebJob einrichten. (Wir laufen die WebJob alle drei Stunden.) Zum Zeitpunkt des Schreibens dieses Artikels gibt es keine Möglichkeit in Windows PowerShell um einen WebJob Zeitplan festzulegen. Nach dem Ausführen der Windows PowerShell -Gründung und Bereitstellungsskripts, benötigen Sie den einmaligen zusätzlichen Schritt der Bereitstellung von WebJob aus Visual Studio , um den Zeitplan einrichten, oder können Sie einen Zeitplan auf dem Portal einrichten.

Wir entdeckten bald, dass die Windows PowerShell -Skripte manchmal Timeout beim Versuch würde, eine Ressource zu erstellen. Mit dem traditionellen Azure PowerShell-Ansatz, gibt es keine einfache Möglichkeit, mit einem zufälligen Ressourcenerstellung-Fehler zu umgehen. Löschen alle Ressourcen, die erfolgreich erstellt wurden, erfordert ein nicht-triviale Skript, die möglicherweise Ressourcen, die Sie entfernen möchten, nicht löschen konnte. Auch wenn das Skript erfolgreich ist, nachdem Sie testen, müssen Sie alle Ressourcen, die das Skript erstellt löschen. Überblick über die Ressourcen zum Löschen nach einem Testlauf ist nicht trivial und fehleranfällig.

Hinweis: die Ressource-Erstellung-Timeout keinen Fehler mit Azure ist. Aus der Ferne erstellen komplexe Ressourcen, z. B. einem Datenserver oder Web-app, ist naturgemäß zeitaufwändig. Wolke-apps müssen von Anfang an mit Timeouts und Misserfolg umzugehen ausgelegt werden.

Azurblaue Resource Manager (ARM) zur Rettung

ARM können Sie Ressourcen als Gruppe zu erstellen. Dies können Sie einfach alle Ihre Ressourcen erstellen und vorübergehenden Fehler behandeln so wenn Ihr Skript keine Ressource zu erstellen, können Sie erneut versuchen. Darüber hinaus ist die Bereinigung einfach. Sie löschen einfach Ihre Ressourcengruppe und die abhängigen Objekte werden automatisch gelöscht.

Vorübergehenden Fehler selten auftreten und in der Regel nur eine Wiederholung ist notwendig, dass der Vorgang erfolgreich ausgeführt. Der folgende Ausschnitt aus einem Windows PowerShell -Skript zeigt einen einfachen Ansatz Wiederholungslogik mit linearen Backoff zu implementieren, wenn eine ARM-Vorlage verwenden:

$cnt = 0
$SleepSeconds = 30$ProvisioningState = 'Failed'while ( [string]::Compare($ProvisioningState, 'Failed', $True) -eq 0 -and ($cnt -lt 4 ) ){   My-New-AzureResourceGroup -RGname $RGname `    -WebSiteName $WebSiteName -HostingPlanName $HostingPlanName
  $RGD = Get-AzureResourceGroupDeployment -ResourceGroupName $RGname  $ProvisioningState = $RGD.ProvisioningState  Start-Sleep -s ($SleepSeconds * $cnt)  $cnt++}

Mein-New-AzureResourceGroup ist eine Windows PowerShell -Funktion, die wir geschrieben, die einen Aufruf des Cmdlets New-AzureResourceGroup, umschließt die eine ARM Vorlage Azure Ressourcen erstellen. Der New-AzureResourceGroup-Aufruf wird fast immer gelingen, aber die Erstellung von Ressourcen, die von der Vorlage angegebenen Timeout kann.

Wenn eine Ressource erstellt, war nicht, der Bereitstellung Staat nicht nachgekommen ist und das Skript wird schlafen und versuchen Sie es erneut. Während eine Wiederholung sind nicht die Ressourcen, die bereits erfolgreich erstellt wurden neu erstellt. Das vorherige Skript versucht drei Wiederholungen. (Vier Ausfälle in Folge geben fast sicher einen nicht flüchtigen Fehler.)

Die Idempotenz, der ARM ist äußerst nützlich in Skripts, die viele Ressourcen zu erstellen. Wir sind nicht vorschlagen, Sie brauchen diese Wiederholungslogik für Ihre Installationen, aber ARM gibt Ihnen diese Option, wenn es vorteilhaft ist.  Finden Sie unter "Verwenden von Azure PowerShell mit Azure Ressourcen-Manager" (bit.ly/1GyaMzv).

Integration und automatische Bereitstellung zu erstellen.

Visual Studio Online gestellt es einfach fortlaufende Integration (CI) einzurichten. Wenn Code in Team Foundation Server (TFS) eingecheckt wird, es automatisch löst einen Build und führt die Komponententests. Wir beschäftigt auch kontinuierlichen Bereitstellung (CD). Wenn der Build und automatisierte Komponententests erfolgreich sind, wird die app automatisch zu unserer Azure Test-Website bereitgestellt. Lesen Sie über die Einrichtung von CI/CD auf Azure auf der Dokumentationsseite, "Kontinuierliche Belieferung der azurblauen Using Visual Studio Online" (bit.ly/1OkMkaW).

Früh in den Dev Zyklus, als es noch drei oder mehr der uns aktiv Quellcode, Einchecken, wir die Standardeinstellung verwendet bauen Sie Definition Trigger Kick-off der Build/Test/Bereitstellung Zyklus um 03:00, Montag bis Freitag. Dies funktionierte gut, eine Chance für uns alle einen Schnelltest auf der Website zu tun, als wir anfingen, jeden Morgen arbeiten. Wie die Codebasis stabilisiert und -Check-ins wurden weniger häufig, aber vielleicht mehr würde kritisch, wir die CI-Modus, also jedes Einchecken Triggersatz den Prozess auslösen. Als wir ankamen, nach der "Code bereinigen"-Phase, mit häufig risikoarme Änderungen, wir stellt den Trigger zu parallelen baut, wo ist der Build/Test/Bereitstellung-Zyklus maximal jede Stunde ausgelöst. Abbildung 8 zeigt eine Buildzusammenfassung, die Bereitstellung und Test Berichterstattung enthält.

Buildzusammenfassung
Abbildung 8 Buildzusammenfassung

Zusammenfassung

In diesem Artikel haben wir einige der Überlegungen beim Erstellen von einer Wolke-Back-End, die sammelt und verarbeitet Daten und dient zur mobilen Clients. Keine einzelne Stück unserer Beispielanwendung ist furchtbar kompliziert, aber es gibt eine Menge von beweglichen Teilen, das ist typisch für Cloud-basierte Lösungen. Wir haben uns auch bei Visual Studio Online machte es möglich für ein kleines Team kontinuierliche Ausführung wie baut und kontinuierliche Bereitstellung ohne dedizierte DevOps Manager.

In Teil2 sehen wir im Detail auf die Clientanwendung und wie Xamarin Formen mehrere Zielplattformen mit einem Minimum von plattformspezifischen Code ermöglichte. Wir werden auch in die Geheimnisse der OAuth2 für soziale Login eintauchen.


Rick Anderson arbeitet als leitende Programmierung Autor für Microsoft, ASP.NET MVC Microsoft Azure und Entity Frameworkim Mittelpunkt. Sie können ihn auf Twitter zu folgen twitter.com/RickAndMSFT.

Kraig Brockschmidt arbeitet als leitender Inhalte Entwickler bei Microsoft und konzentriert sich auf Cross-Plattform mobile apps. Er ist der Autor von "Programming Windows Store Apps mit HTML, CSS und JavaScript" (zwei Ausgaben) von Microsoft Press und Blogs auf kraigbrockschmidt.com.

Tom Dykstra ist ein senior Inhalte Entwickler bei Microsoft, Microsoft Azure und ASP.NETim Mittelpunkt.

Erik Reitan ist ein senior Content-Entwickler bei Microsoft. Er konzentriert sich auf Microsoft Azure und ASP.NET. Folgen Sie ihm auf Twitter bei twitter.com/ReitanErik.

Mike Wasson ist ein Content-Entwickler bei Microsoft. Jahrelang hat er die Win32-Multimedia-APIs dokumentiert. Er schreibt derzeit über Microsoft Azure und ASP.NET.

Unser Dank gilt den folgenden technischen Experten für die Durchsicht dieses Artikels: Michael Collier, John de Havilland, Brady Gaster, Ryan Jones, Vijay Ramakrishnan und Pranav Rastogi