Building static websites with Azure Functions, mustache & Azure Storage

In diesem Artikel zeige ich, wie man dynamische Daten mithilfe einer zeitgesteuerten Azure Function (in nodejs) & mustache eine statische Website generieren kann, welche in Azure Storage gehostet wird.

Für diese Demo bauen wir eine simple, personalisierte Startseite, die aktuelle Schlagzeilen eines WordPress-Blogs und ein paar weitere Links anzeigt. Sicherlich nicht das spannendste Projekt, aber ausreichend, um ein paar interessante Dinge zu zeigen.

Folgendes wollen wir erreichen:

  • Die mustache-Templates anlegen
  • Die Template-Dateien in der Azure Function laden und vorbereiten
  • Die Daten aus der WordPress REST API abrufen
  • Das HTML rendern und in Azure Storage hochladen

Die Mustache Templates

Um zu zeigen, wie man die Template-Dateien aufteilen kann, habe ich jeweils eine Datei für den Index, den Header und den Footer angelegt. Mit den entsprechenden Tags {{> header.mustache}} und {{> footer.mustache}} lassen sich die entsprechenden HTML-Teile in der Index-Datei einbinden.

Die News zeigen wir als einfache Liste mit einem Link zum Originalartikel an. Das Ganze sieht dann so aus:

{{> header.mustache}}
<h1>WindowsArea.de News</h1>
<ul>
{{#news}}
<li><a href="{{link}}" target="_blank">{{title.rendered}}</a></li>
{{/news}}
</ul>
<h1>My Links</h1>
<ul>
{{#links}}
<li><a href="{{url}}" target="_blank">{{url}}</a></li>
{{/links}}
</ul>
{{> footer.mustache}}
view raw index.mustache hosted with ❤ by GitHub

Diese drei Dateien legen wir in einem eigenen „templates“-Verzeichnis im Azure Blob Storage ab. Auf diese Weise können wir die Templates unabhängig vom Backend-Code anpassen. Außerdem ermöglicht uns dies, die Dateien in der Function via Bindings direkt zu laden.

Das „templates“ Verzeichnis in Azure Blob Storage (Azure Storage Explorer)

Die Azure Function

Wie oben erwähnt nutzen wir JavaScript für die Logik der Function. Diese habe ich mit Visual Studio Code erstellt und ein paar npm Packages hinzugefügt, die wir benötigen:

  • request – für den Download der News
  • mu2 – eine Node.js Mustache engine
  • azure-storage – das SDK um die fertigen HTML Dateien in den Azure Blob Storage hochzuladen

Fangen wir damit an, die News-Artikel in der mynews Variable zu speichern:

function getData(){
context.log('get data');
var options = {
url: 'https://windowsarea.de/wp-json/wp/v2/posts',
method: 'GET',
json: true
}
request(options, function (error, response, data) {
context.log("request callback");
if (error) {
context.log(error);
}
else {
context.log(data.length);
mynews = data;
}
prepareTemplates();
});
}
view raw getData.js hosted with ❤ by GitHub

Nachdem wir hier in den Optionen json auf true gesetzt haben, wird der heruntergeladene JSON-String direkt als JavaScript-Objekt (bzw. in diesem Fall als Array) geparsed zurückgegeben.

Anschließend rufen wir die prepareTemplates() Methode auf, welche die Header- und Footer-Dateien zur weiteren Verwendung mit mustache vorbereitet. Die Dateien können wir  direkt über das context.bindings Object aufrufen. Ein Blick in die functions.json Datei verrät, wie die Azure Function konfiguriert wurde.

function prepareTemplates(){
context.log('prepare Templates');
mu.compileText('header.mustache', context.bindings.headerTemplate, function () {
mu.compileText('footer.mustache', context.bindings.footerTemplate, function () {
renderSite();
});
});
}
view raw prepareTemplates.js hosted with ❤ by GitHub

Nachdem dies erledigt ist kann die Webseite selbst nun gebaut werden. Hierzu übergeben wir unsere Daten der mu.render() Methode und speichern den Stream-Output in der pageFile Variable. Hier erhalten wir den resultierenden HTML-Code als String.

function renderSite(){
context.log("render site");
var pageFile = "";
mu.compileText('index.mustache', context.bindings.indexTemplate, function (err, parsed) {
var renderstream = mu.render(parsed, { news: mynews, links: mylinks });
renderstream.on('data', function (data) {
pageFile += data;
});
renderstream.on('end', function (data) {
context.log("done creating index.html");
uploadSite(pageFile);
});
});
}
view raw renderSite.js hosted with ❤ by GitHub

Nach Beendigung des Streams müssen wir die Datei noch in Azure hochladen. Prinzipiell wäre es möglich, dies über die Azure Function Output Bindings zu lösen. Allerdings haben wir hier keinerlei Kontrolle über den Content Type der Datei, was in manchen Browsern zu Problemen führt.

Zunächst legen wir aber noch das benötigte $web Verzeichnis an und speichern dort dann unsere HTML-Datei als neuen Blob. Hier lässt sich dann auch der richtige Content Type ‚text/html‘ mit übergeben.

function uploadSite(page){
context.log("Upload site ");
var blobService = storage.createBlobService();
// create $web container
blobService.createContainerIfNotExists('$web', function(){
// upload index.html to $web container
const options = { contentSettings: { contentType: 'text/html' } }
blobService.createBlockBlobFromText("$web", "index.html", page, options, function (error) {
context.log("uploaded");
context.done();
});
});
}
view raw uploadSite.js hosted with ❤ by GitHub

Und das wars. Der gesamte Code kann auf Github gefunden werden. Hier findet ihr das finale, wunderbar gestylte Ergebnis.

Veröffentlicht von

Thomas

Developer, Consultant, Microsoft Insider MVP

Kommentar verfassen

Diese Website verwendet Akismet, um Spam zu reduzieren. Erfahre mehr darüber, wie deine Kommentardaten verarbeitet werden.