<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>medienstudio.net</title>
	<atom:link href="https://medienstudio.net/feed/" rel="self" type="application/rss+xml" />
	<link>https://medienstudio.net/</link>
	<description></description>
	<lastBuildDate>Wed, 17 Apr 2024 11:52:50 +0000</lastBuildDate>
	<language>de</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.4</generator>
<site xmlns="com-wordpress:feed-additions:1">53756420</site>	<item>
		<title>Microsoft Fabric – die Kerndienste erklärt</title>
		<link>https://medienstudio.net/development/microsoft-fabric-die-kerndienste-erklaert/</link>
					<comments>https://medienstudio.net/development/microsoft-fabric-die-kerndienste-erklaert/#respond</comments>
		
		<dc:creator><![CDATA[Thomas]]></dc:creator>
		<pubDate>Mon, 15 Apr 2024 15:18:56 +0000</pubDate>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[microsoft fabric]]></category>
		<guid isPermaLink="false">https://medienstudio.net/?p=2835</guid>

					<description><![CDATA[<p>Im ersten Artikel dieser Serie wurden die Stärken von Microsoft Fabric gezeigt. Dieser Beitrag liefert nun einen Überblick über die Kern-Dienste, aus denen Fabric im zusammengesetzt ist. Insgesamt gibt es 8 &#8222;User Experiences&#8220;, also acht Ansichten, die innerhalb von Fabric aufgerufen werden können. Diese sind maßgeschneidert für die unterschiedlichen Anwender-Profile, die mit Fabric arbeiten, wie &#8230; <a href="https://medienstudio.net/development/microsoft-fabric-die-kerndienste-erklaert/" class="more-link"><span class="screen-reader-text">Microsoft Fabric – die Kerndienste erklärt</span> weiterlesen</a></p>
<p>The post <a href="https://medienstudio.net/development/microsoft-fabric-die-kerndienste-erklaert/">Microsoft Fabric – die Kerndienste erklärt</a> appeared first on <a href="https://medienstudio.net">medienstudio.net</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>Im ersten Artikel dieser Serie wurden <a href="https://medienstudio.net/development/microsoft-fabric-auf-den-ersten-blick/">die Stärken von Microsoft Fabric</a> gezeigt. Dieser Beitrag liefert nun einen Überblick über die Kern-Dienste, aus denen Fabric im zusammengesetzt ist. Insgesamt gibt es 8 &#8222;User Experiences&#8220;, also acht Ansichten, die innerhalb von Fabric aufgerufen werden können. Diese sind maßgeschneidert für die unterschiedlichen Anwender-Profile, die mit Fabric arbeiten, wie z.B. Data Engineers &amp; Scientists, Datenbank-Administratoren oder Business-Analysten. Und so wie es bei diesen Profilen Überschneidungen gibt findet man diese auch in Fabric wieder, beispielweise bei den Data Pipelines die sowohl in Data Factory, Data Engineering und in Data Warehouse eingesetzt werden können.</p>



<span id="more-2835"></span>



<figure class="wp-block-image size-large"><a href="https://medienstudio.net/wp-content/uploads/2024/04/image-4.png"><img fetchpriority="high" decoding="async" width="1024" height="370" src="https://medienstudio.net/wp-content/uploads/2024/04/image-4-1024x370.png" alt="" class="wp-image-2836" srcset="https://medienstudio.net/wp-content/uploads/2024/04/image-4-1024x370.png 1024w, https://medienstudio.net/wp-content/uploads/2024/04/image-4-300x108.png 300w, https://medienstudio.net/wp-content/uploads/2024/04/image-4-768x277.png 768w, https://medienstudio.net/wp-content/uploads/2024/04/image-4-1536x555.png 1536w, https://medienstudio.net/wp-content/uploads/2024/04/image-4.png 2047w" sizes="(max-width: 1024px) 100vw, 1024px" /></a></figure>



<p></p>



<h2 class="wp-block-heading">Data Factory</h2>



<figure class="wp-block-image size-large"><a href="https://medienstudio.net/wp-content/uploads/2024/04/image-6.png"><img decoding="async" width="1024" height="263" src="https://medienstudio.net/wp-content/uploads/2024/04/image-6-1024x263.png" alt="" class="wp-image-2838" srcset="https://medienstudio.net/wp-content/uploads/2024/04/image-6-1024x263.png 1024w, https://medienstudio.net/wp-content/uploads/2024/04/image-6-300x77.png 300w, https://medienstudio.net/wp-content/uploads/2024/04/image-6-768x197.png 768w, https://medienstudio.net/wp-content/uploads/2024/04/image-6.png 1300w" sizes="(max-width: 1024px) 100vw, 1024px" /></a></figure>



<p>Mit der Data Factory beginnt in der Regel die Reise ins Fabric-Universum, denn hiermit werden klassische ETL Prozesse abgebildet und die Daten in den OneLake übertragen. Hier stehen zwei Bausteine zur Verfügung: Dataflows und Data Pipelines.</p>



<p>Mit <strong>Dataflows</strong> lassen sich via Low-code &amp; No-code Konnektoren nahezu beliebig Datenquellen anbinden. Diese werden mit Power Query erstellt und können manuell oder wiederkehrend ausgeführt werden. Beispielsweise lassen sich hiermit stündlich aktuelle Werte aus einer REST API abrufen um später historische Auswertungen tätigen zu können.</p>



<p><strong>Data Pipelines</strong> werden benutzt um komplexe Daten-Import-Prozesse zu orchestrieren und deren Ausführung zu planen. Diese Workflows bestehen z.B. aus Python Notebooks, Stored Procedures, Azure Functions oder anderen Dataflows und können dank automatischer Skalierung auch Datenmengen in Petabyte-Größenordnung verarbeiten. Konkret können Data Pipelines z.B. für den initialen Import großer Datenmengen verwendet werden, die vor dem Abspeichern in OneLake gefiltert und anderweitig aufbereitet werden müssen.</p>



<p>Wie die Namen vermuten lassen handelt es sich hierbei um angepasste bzw. integrierte Versionen von Azure Data Factory, Synapse Pipelines und Power BI Dataflows.</p>



<h2 class="wp-block-heading">Synapse Data Warehouse</h2>



<figure class="wp-block-image size-large"><a href="https://medienstudio.net/wp-content/uploads/2024/04/image-10.png"><img decoding="async" width="1024" height="255" src="https://medienstudio.net/wp-content/uploads/2024/04/image-10-1024x255.png" alt="" class="wp-image-2842" srcset="https://medienstudio.net/wp-content/uploads/2024/04/image-10-1024x255.png 1024w, https://medienstudio.net/wp-content/uploads/2024/04/image-10-300x75.png 300w, https://medienstudio.net/wp-content/uploads/2024/04/image-10-768x191.png 768w, https://medienstudio.net/wp-content/uploads/2024/04/image-10.png 1303w" sizes="(max-width: 1024px) 100vw, 1024px" /></a></figure>



<p>In der Synapse Data Warehouse Experience können Warehouses erstellt und verwaltet werden. Hierin werden strukturierte Daten mittels T-SQL gespeichert und abgerufen, wie man es z.B. von einem SQL Server gewohnt ist. Die Daten werden im Delta-Parquet-Format im OneLake gespeichert.</p>



<h2 class="wp-block-heading">Synapse Data Engineering</h2>



<figure class="wp-block-image size-large"><a href="https://medienstudio.net/wp-content/uploads/2024/04/image-5.png"><img loading="lazy" decoding="async" width="1024" height="261" src="https://medienstudio.net/wp-content/uploads/2024/04/image-5-1024x261.png" alt="" class="wp-image-2837" srcset="https://medienstudio.net/wp-content/uploads/2024/04/image-5-1024x261.png 1024w, https://medienstudio.net/wp-content/uploads/2024/04/image-5-300x76.png 300w, https://medienstudio.net/wp-content/uploads/2024/04/image-5-768x196.png 768w, https://medienstudio.net/wp-content/uploads/2024/04/image-5.png 1300w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a></figure>



<p>In der Synapse Data Engineering Ansicht sind die Lakehouses zu finden. Diese unterscheiden sich von den Warehouses unter anderem dadurch, dass sie sowohl strukturierte (tabellarische) als auch semi-strukturierte (z.B. Websites) und unstrukturierte Daten (z.B. PDF Dateien) enthalten können. Lakehouse nutz im Hintergrund die Apache Spark Engine und kann dementsprechend auch in Spark Jobs verwendet werden.</p>



<p>In welchen Fällen Lakehouses oder Warehouses eingesetzt werden hängt dabei sowohl von den Daten als auch von den Konsumenten (Durchsatz, Spark vs T-SQL) ab.</p>



<h2 class="wp-block-heading">Synapse Data Science</h2>



<figure class="wp-block-image size-large"><a href="https://medienstudio.net/wp-content/uploads/2024/04/image-9.png"><img loading="lazy" decoding="async" width="1024" height="256" src="https://medienstudio.net/wp-content/uploads/2024/04/image-9-1024x256.png" alt="" class="wp-image-2841" srcset="https://medienstudio.net/wp-content/uploads/2024/04/image-9-1024x256.png 1024w, https://medienstudio.net/wp-content/uploads/2024/04/image-9-300x75.png 300w, https://medienstudio.net/wp-content/uploads/2024/04/image-9-768x192.png 768w, https://medienstudio.net/wp-content/uploads/2024/04/image-9.png 1312w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a></figure>



<p>Synapse Data Science ermöglicht den Zugriff auf alle im OneLake gespeicherten Daten speziell für Data Scientists. Diese können hier ihre Workflows abbilden und aus den angereicherten Daten Erkenntnisse gewinnen. So können z.B. Machine Learning Experimente, Modelle und Notebooks erstellt und ausgeführt werden.</p>



<h2 class="wp-block-heading">Synapse Real-Time Analytics</h2>



<figure class="wp-block-image size-large"><a href="https://medienstudio.net/wp-content/uploads/2024/04/image-11.png"><img loading="lazy" decoding="async" width="1024" height="270" src="https://medienstudio.net/wp-content/uploads/2024/04/image-11-1024x270.png" alt="" class="wp-image-2843" srcset="https://medienstudio.net/wp-content/uploads/2024/04/image-11-1024x270.png 1024w, https://medienstudio.net/wp-content/uploads/2024/04/image-11-300x79.png 300w, https://medienstudio.net/wp-content/uploads/2024/04/image-11-768x202.png 768w, https://medienstudio.net/wp-content/uploads/2024/04/image-11.png 1279w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a></figure>



<p>Mit Synapse Real-Time Analytics lassen sich mittels Eventstream real-time Events sammeln, verarbeiten und weiterleiten. Eine KQL-Datenbank kann die Daten im OneLake persistieren und so Fabric-weit zugänglich machen. Über KQL-Querysets können Queries geteilt und z.B. Power BI Reports erzeugt werden. Klassische Anwendungsfälle wären hierfür z.B. IoT- oder Sales-Daten.</p>



<h2 class="wp-block-heading">Power BI</h2>



<figure class="wp-block-image size-large is-resized"><a href="https://medienstudio.net/wp-content/uploads/2024/04/image-12.png"><img loading="lazy" decoding="async" width="1024" height="342" src="https://medienstudio.net/wp-content/uploads/2024/04/image-12-1024x342.png" alt="" class="wp-image-2844" style="width:690px;height:auto" srcset="https://medienstudio.net/wp-content/uploads/2024/04/image-12-1024x342.png 1024w, https://medienstudio.net/wp-content/uploads/2024/04/image-12-300x100.png 300w, https://medienstudio.net/wp-content/uploads/2024/04/image-12-768x257.png 768w, https://medienstudio.net/wp-content/uploads/2024/04/image-12.png 1359w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a></figure>



<p>Power BI ist in der Regel das letzte Tool in der Datenverarbeitungs-Kette. Hier werden die gewonnen Erkenntnisse visualisiert und u.a. für Entscheidungsträger zugänglich gemacht. Die Daten stammen dabei natürlich aus dem OneLake und können  sowohl historisch aber auch dank der KQL-Datenbank live gestreamt sein.</p>



<h2 class="wp-block-heading">Data Activator</h2>



<figure class="wp-block-image size-large"><a href="https://medienstudio.net/wp-content/uploads/2024/04/image-7.png"><img loading="lazy" decoding="async" width="1024" height="265" src="https://medienstudio.net/wp-content/uploads/2024/04/image-7-1024x265.png" alt="" class="wp-image-2839" srcset="https://medienstudio.net/wp-content/uploads/2024/04/image-7-1024x265.png 1024w, https://medienstudio.net/wp-content/uploads/2024/04/image-7-300x78.png 300w, https://medienstudio.net/wp-content/uploads/2024/04/image-7-768x198.png 768w, https://medienstudio.net/wp-content/uploads/2024/04/image-7.png 1281w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a></figure>



<p>Mit dem Data Activator lassen sich Live-Daten in Eventstreams und Power BI Reports auf Muster oder Bedingungen überwachen. Die Logik wird hierbei mit dem No-Code-Tool Reflex entwickelt. Werden passende Events gefunden können Aktionen ausgelöst werden, wie das Benachrichtigen des Benutzers oder das Ausführen von Power Automate Workflows.</p>



<h2 class="wp-block-heading">Industry Solutions</h2>



<figure class="wp-block-image size-large"><a href="https://medienstudio.net/wp-content/uploads/2024/04/image-8.png"><img loading="lazy" decoding="async" width="1024" height="265" src="https://medienstudio.net/wp-content/uploads/2024/04/image-8-1024x265.png" alt="" class="wp-image-2840" srcset="https://medienstudio.net/wp-content/uploads/2024/04/image-8-1024x265.png 1024w, https://medienstudio.net/wp-content/uploads/2024/04/image-8-300x78.png 300w, https://medienstudio.net/wp-content/uploads/2024/04/image-8-768x199.png 768w, https://medienstudio.net/wp-content/uploads/2024/04/image-8.png 1273w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a></figure>



<p>In Industry Solutions liefert stehen Templates für besondere Szenarien bereit. Aktuell können so mittels dieser Bausteine typische Workflows in Fabric für Nachhaltigkeit,  Einzelhandel und Gesundheitswesen erstellt und anschließend den eigenen Bedürfnissen angepasst werden.</p>



<h2 class="wp-block-heading">Zusammenfassung</h2>



<p>Das nachfolgende Schaubild zeigt noch einmal, wie die Dienste grundsätzlich zueinander in Beziehung stehen. Auch wenn es viele Datenquellen und Formate gibt die auf unterschiedliche Art und Weise verarbeitet werden liegen letztendlich doch alle Information im offenen Apache Delta-Parquet im OneLake.</p>



<figure class="wp-block-image size-large"><a href="https://medienstudio.net/wp-content/uploads/2024/04/image-13.png"><img loading="lazy" decoding="async" width="1024" height="576" src="https://medienstudio.net/wp-content/uploads/2024/04/image-13-1024x576.png" alt="" class="wp-image-2847" srcset="https://medienstudio.net/wp-content/uploads/2024/04/image-13-1024x576.png 1024w, https://medienstudio.net/wp-content/uploads/2024/04/image-13-300x169.png 300w, https://medienstudio.net/wp-content/uploads/2024/04/image-13-768x432.png 768w, https://medienstudio.net/wp-content/uploads/2024/04/image-13-1536x864.png 1536w, https://medienstudio.net/wp-content/uploads/2024/04/image-13-2048x1152.png 2048w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a></figure>
<p>The post <a href="https://medienstudio.net/development/microsoft-fabric-die-kerndienste-erklaert/">Microsoft Fabric – die Kerndienste erklärt</a> appeared first on <a href="https://medienstudio.net">medienstudio.net</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://medienstudio.net/development/microsoft-fabric-die-kerndienste-erklaert/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">2835</post-id>	</item>
		<item>
		<title>Microsoft Fabric auf den ersten Blick</title>
		<link>https://medienstudio.net/development/microsoft-fabric-auf-den-ersten-blick/</link>
					<comments>https://medienstudio.net/development/microsoft-fabric-auf-den-ersten-blick/#respond</comments>
		
		<dc:creator><![CDATA[Thomas]]></dc:creator>
		<pubDate>Mon, 15 Apr 2024 15:15:09 +0000</pubDate>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[microsoft fabric]]></category>
		<guid isPermaLink="false">https://medienstudio.net/?p=2825</guid>

					<description><![CDATA[<p>Vor fast einem Jahr hat Microsoft auf der hauseigenen BUILD Conference ein neues Produkt namens Microsoft Fabric vorgestellt. Nachdem Fabric seit November 2023 für alle Kunden zur Verfügung steht und ausgiebig getestet werden konnte, beleuchtet dieser Artikel was dieses Tool ausmacht und welche Probleme im Unternehmen damit gelöst werden können. Was ist Microsoft Fabric? Microsoft &#8230; <a href="https://medienstudio.net/development/microsoft-fabric-auf-den-ersten-blick/" class="more-link"><span class="screen-reader-text">Microsoft Fabric auf den ersten Blick</span> weiterlesen</a></p>
<p>The post <a href="https://medienstudio.net/development/microsoft-fabric-auf-den-ersten-blick/">Microsoft Fabric auf den ersten Blick</a> appeared first on <a href="https://medienstudio.net">medienstudio.net</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>Vor fast einem Jahr hat Microsoft auf der hauseigenen BUILD Conference ein neues Produkt namens Microsoft Fabric vorgestellt. Nachdem Fabric seit November 2023 für alle Kunden zur Verfügung steht und ausgiebig getestet werden konnte, beleuchtet dieser Artikel was dieses Tool ausmacht und welche Probleme im Unternehmen damit gelöst werden können.</p>



<span id="more-2825"></span>



<h2 class="wp-block-heading"><strong>Was ist Microsoft Fabric?</strong></h2>



<p>Microsoft Fabric ist ein Software-as-a-Service (SaaS) Angebot zur zentralen Verwaltung und Analyse aller Arten von Unternehmensdaten. Es bündelt zahlreiche etablierte und neue Dienste von Microsoft in einer Lösung, die die Sammlung, Verarbeitung und Auswertung von Daten in einer zentralen Anwendung ermöglichen. Hierbei sind die eingebundenen Dienste darauf optimiert, nahtlos zusammenzuarbeiten. Dies verringert den Bedarf an selbst programmierter Logik und erhöht die Geschwindigkeit, in der Erkenntnisse aus Daten jeder Größenordnung gewonnen werden können.</p>



<p>Die zentrale Rolle spielt hierbei <strong>OneLake</strong>, der zentrale Speicherort für alle Daten die in Fabric importiert oder erzeugt werden. Dieser sorgt dafür, dass alle dort gespeicherten Daten für das gesamte Unternehmen in einem einheitlichen Format zugänglich gemacht werden können.</p>



<figure class="wp-block-image size-large"><a href="https://medienstudio.net/wp-content/uploads/2024/04/image-13.png"><img loading="lazy" decoding="async" width="1024" height="576" src="https://medienstudio.net/wp-content/uploads/2024/04/image-13-1024x576.png" alt="" class="wp-image-2847" srcset="https://medienstudio.net/wp-content/uploads/2024/04/image-13-1024x576.png 1024w, https://medienstudio.net/wp-content/uploads/2024/04/image-13-300x169.png 300w, https://medienstudio.net/wp-content/uploads/2024/04/image-13-768x432.png 768w, https://medienstudio.net/wp-content/uploads/2024/04/image-13-1536x864.png 1536w, https://medienstudio.net/wp-content/uploads/2024/04/image-13-2048x1152.png 2048w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a></figure>



<h2 class="wp-block-heading">Welche konkreten Probleme löst Microsoft Fabric?</h2>



<p>Durch den zentralisierten Ansatz von Microsoft Fabric und dem OneLake ergeben sich zahlreiche Vorteile zu den bisherigen Individuallösungen, die sich in vielen Unternehmen über Abteilungen hinweg etabliert haben. Hier eine nicht abschließende Auflistung and Benefits:</p>



<ul class="wp-block-list">
<li><strong>Eine einheitliche User Experience<br></strong>Die Benutzer können alle Funktionen innerhalb der Fabric App abrufen und müssen nicht in unterschiedliche Web Apps wechseln oder gar zusätzliche Programme installieren. Dies kann unter anderem die Einarbeitungszeit deutlich verkürzen.</li>



<li><strong>Keine Datenduplizierung<br></strong>Durch die Speicherung aller Daten im OneLake im einheitlichen Delta-Parquet Format können alle Personen und Fachbereiche im Unternehmen ohne Duplizierung auf dieselbe Datenquelle zugreifen. Dies spart Speicherplatz und somit bares Geld und erhöht die Datenkonsistenz, also die Qualität der Daten.</li>



<li><strong>Auffindbarkeit von Daten</strong><br>Durch die zentrale Speicherung der Daten können diese im Unternehmen auch einfacher auffindbar gemacht werden. Durch die integrierte Suche können Datensätze gefunden und sofort weiterverarbeitet werden, ohne dass aufwändig Zugriff auf Datenbanken oder Dokumente erfragt und manuell bearbeitet werden muss. Zudem lassen sich qualitativ hochwertige Datensätze explizit für alle sichtbar als &#8222;Empfohlen&#8220; markieren.</li>



<li><strong>Zentrale Zugriffssteuerung</strong><br>Über den OneLake kann der Zugriff auf Daten für Benutzer zentral geregelt werden. Dies wird automatisch in den unterschiedlichen Sub-Diensten durchgesetzt und gilt auch für hiervon abgeleitete bzw. weiterverarbeitete Datensätze.</li>



<li><strong>Zentrales Monitoring</strong><br>Über den <a href="https://learn.microsoft.com/de-de/fabric/admin/monitoring-hub?WT.mc_id=5005148">Monitoring Hub</a> (auf Deutsch auch Überwachungshub genannt)  lassen sich alle Aktivitäten in Fabric überwachen, wie z.B. die Ausführung von Datenpipelines, Dataflows, Notebooks oder Spark-Auftragsdefinitionen.</li>



<li><strong>Sicherheit und Governance</strong><br>Fabric ist als SaaS vom Microsoft umfassend geschützt. Alle Daten werden stets verschlüsselt gespeichert und übertragen. Benutzer melden sich via Microsoft Entra ID an und können so z.B. via Multifaktor-Authentifizierung oder IP-Ranges zusätzlich abgesichert werden. Des weiteren stehen Werkzeuge zur Verfügung um zahlreiche Compliance Standards zu erfüllen. <a href="https://learn.microsoft.com/fabric/security/security-overview?WT.mc_id=5005148">Weitere Informationen</a></li>



<li><strong>Zentrale Abrechnung</strong><br>Durch das SaaS Modell laufen alle entstehenden Kosten an einem Punk zusammen. So können die Gesamtkosten deutlich einfacher überblickt werden als wenn jede Abteilung eigene SQL Datenbanken einer separaten Azure Subscription betreibt.</li>
</ul>



<p>Im zweiten Teil dieser Artikelserie werfen wir einen Blick auf die unterschiedlichen Dienste, aus denen Microsoft Fabric besteht.</p>



<figure class="wp-block-embed is-type-wp-embed is-provider-medienstudio-net wp-block-embed-medienstudio-net"><div class="wp-block-embed__wrapper">
<blockquote class="wp-embedded-content" data-secret="1uOLrNxidE"><a href="https://medienstudio.net/development/microsoft-fabric-die-kerndienste-erklaert/">Microsoft Fabric – die Kerndienste erklärt</a></blockquote><iframe loading="lazy" class="wp-embedded-content" sandbox="allow-scripts" security="restricted"  title="&#8222;Microsoft Fabric – die Kerndienste erklärt&#8220; &#8212; medienstudio.net" src="https://medienstudio.net/development/microsoft-fabric-die-kerndienste-erklaert/embed/#?secret=DUSHdCIo3z#?secret=1uOLrNxidE" data-secret="1uOLrNxidE" width="600" height="338" frameborder="0" marginwidth="0" marginheight="0" scrolling="no"></iframe>
</div></figure>
<p>The post <a href="https://medienstudio.net/development/microsoft-fabric-auf-den-ersten-blick/">Microsoft Fabric auf den ersten Blick</a> appeared first on <a href="https://medienstudio.net">medienstudio.net</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://medienstudio.net/development/microsoft-fabric-auf-den-ersten-blick/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">2825</post-id>	</item>
		<item>
		<title>Building static websites with Azure Functions, mustache &#038; Azure Storage</title>
		<link>https://medienstudio.net/development/building-static-websites-with-azure-functions-mustache-azure-storage/</link>
					<comments>https://medienstudio.net/development/building-static-websites-with-azure-functions-mustache-azure-storage/#respond</comments>
		
		<dc:creator><![CDATA[Thomas]]></dc:creator>
		<pubDate>Mon, 30 Jul 2018 13:34:12 +0000</pubDate>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[azure]]></category>
		<category><![CDATA[azure functions]]></category>
		<category><![CDATA[mustache]]></category>
		<guid isPermaLink="false">https://medienstudio.net/?p=1603</guid>

					<description><![CDATA[<p>In diesem Artikel zeige ich, wie man dynamische Daten mithilfe einer zeitgesteuerten Azure Function (in nodejs) &#38; 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 &#8230; <a href="https://medienstudio.net/development/building-static-websites-with-azure-functions-mustache-azure-storage/" class="more-link"><span class="screen-reader-text">Building static websites with Azure Functions, mustache &#038; Azure Storage</span> weiterlesen</a></p>
<p>The post <a href="https://medienstudio.net/development/building-static-websites-with-azure-functions-mustache-azure-storage/">Building static websites with Azure Functions, mustache &#038; Azure Storage</a> appeared first on <a href="https://medienstudio.net">medienstudio.net</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>In diesem Artikel zeige ich, wie man dynamische Daten mithilfe einer zeitgesteuerten Azure Function (in nodejs) &amp; <a href="https://mustache.github.io/">mustache</a> eine statische Website generieren kann, welche in Azure Storage gehostet wird.</p>



<p>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.</p>



<span id="more-1603"></span>



<p>Folgendes wollen wir erreichen:</p>



<ul class="wp-block-list"><li>Die mustache-Templates anlegen</li><li>Die Template-Dateien in der Azure Function laden und vorbereiten</li><li>Die Daten aus der WordPress REST API abrufen</li><li>Das HTML rendern und in Azure Storage hochladen</li></ul>



<h2 class="wp-block-heading">Die Mustache Templates</h2>



<p>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&nbsp;<code>{{&gt; header.mustache}}</code> und&nbsp;<code>{{&gt; footer.mustache}}</code> lassen sich die entsprechenden HTML-Teile in der Index-Datei einbinden.</p>



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



<figure class="wp-block-embed is-type-rich is-provider-handler-einbetten"><div class="wp-block-embed__wrapper">
<style>.gist table { margin-bottom: 0; }</style><div style="tab-size: 8" id="gist91037767" class="gist">
    <div class="gist-file" translate="no" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        
<div class="js-gist-file-update-container js-task-list-container">
      <div id="file-index-mustache" class="file my-2">
    
    <div itemprop="text"
      class="Box-body p-0 blob-wrapper data type-mustache  "
      style="overflow: auto" tabindex="0" role="region"
      aria-label="index.mustache content, created by ThomasPe on 11:19AM on July 30, 2018."
    >

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  <template class="js-file-alert-template">
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  <svg aria-hidden="true" data-component="Octicon" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-alert">
    <path d="M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z"></path>
</svg>
    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div></template>
<template class="js-line-alert-template">
  <span aria-label="This line has hidden Unicode characters" data-view-component="true" class="line-alert tooltipped tooltipped-e">
    <svg aria-hidden="true" data-component="Octicon" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-alert">
    <path d="M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z"></path>
</svg>
</span></template>

  <table data-hpc class="highlight tab-size js-file-line-container" data-tab-size="4" data-paste-markdown-skip data-tagsearch-path="index.mustache">
        <tr>
          <td id="file-index-mustache-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-index-mustache-LC1" class="blob-code blob-code-inner js-file-line">{{&gt; header.mustache}}</td>
        </tr>
        <tr>
          <td id="file-index-mustache-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-index-mustache-LC2" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-index-mustache-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-index-mustache-LC3" class="blob-code blob-code-inner js-file-line">&lt;h1&gt;WindowsArea.de News&lt;/h1&gt;</td>
        </tr>
        <tr>
          <td id="file-index-mustache-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-index-mustache-LC4" class="blob-code blob-code-inner js-file-line">&lt;ul&gt;</td>
        </tr>
        <tr>
          <td id="file-index-mustache-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-index-mustache-LC5" class="blob-code blob-code-inner js-file-line">{{#news}}</td>
        </tr>
        <tr>
          <td id="file-index-mustache-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-index-mustache-LC6" class="blob-code blob-code-inner js-file-line">    &lt;li&gt;&lt;a href=&quot;{{link}}&quot; target=&quot;_blank&quot;&gt;{{title.rendered}}&lt;/a&gt;&lt;/li&gt;</td>
        </tr>
        <tr>
          <td id="file-index-mustache-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-index-mustache-LC7" class="blob-code blob-code-inner js-file-line">{{/news}}</td>
        </tr>
        <tr>
          <td id="file-index-mustache-L8" class="blob-num js-line-number js-blob-rnum" data-line-number="8"></td>
          <td id="file-index-mustache-LC8" class="blob-code blob-code-inner js-file-line">&lt;/ul&gt;</td>
        </tr>
        <tr>
          <td id="file-index-mustache-L9" class="blob-num js-line-number js-blob-rnum" data-line-number="9"></td>
          <td id="file-index-mustache-LC9" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-index-mustache-L10" class="blob-num js-line-number js-blob-rnum" data-line-number="10"></td>
          <td id="file-index-mustache-LC10" class="blob-code blob-code-inner js-file-line">&lt;h1&gt;My Links&lt;/h1&gt;</td>
        </tr>
        <tr>
          <td id="file-index-mustache-L11" class="blob-num js-line-number js-blob-rnum" data-line-number="11"></td>
          <td id="file-index-mustache-LC11" class="blob-code blob-code-inner js-file-line">&lt;ul&gt;</td>
        </tr>
        <tr>
          <td id="file-index-mustache-L12" class="blob-num js-line-number js-blob-rnum" data-line-number="12"></td>
          <td id="file-index-mustache-LC12" class="blob-code blob-code-inner js-file-line">{{#links}}</td>
        </tr>
        <tr>
          <td id="file-index-mustache-L13" class="blob-num js-line-number js-blob-rnum" data-line-number="13"></td>
          <td id="file-index-mustache-LC13" class="blob-code blob-code-inner js-file-line">    &lt;li&gt;&lt;a href=&quot;{{url}}&quot; target=&quot;_blank&quot;&gt;{{url}}&lt;/a&gt;&lt;/li&gt;</td>
        </tr>
        <tr>
          <td id="file-index-mustache-L14" class="blob-num js-line-number js-blob-rnum" data-line-number="14"></td>
          <td id="file-index-mustache-LC14" class="blob-code blob-code-inner js-file-line">{{/links}}</td>
        </tr>
        <tr>
          <td id="file-index-mustache-L15" class="blob-num js-line-number js-blob-rnum" data-line-number="15"></td>
          <td id="file-index-mustache-LC15" class="blob-code blob-code-inner js-file-line">&lt;/ul&gt;</td>
        </tr>
        <tr>
          <td id="file-index-mustache-L16" class="blob-num js-line-number js-blob-rnum" data-line-number="16"></td>
          <td id="file-index-mustache-LC16" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-index-mustache-L17" class="blob-num js-line-number js-blob-rnum" data-line-number="17"></td>
          <td id="file-index-mustache-LC17" class="blob-code blob-code-inner js-file-line">{{&gt; footer.mustache}}</td>
        </tr>
  </table>
</div>


    </div>

  </div>

</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/ThomasPe/22a82c93dc43335dcd8838acd6a67b07/raw/a7307828b55957332b96e230d808d4d77feb7d8a/index.mustache" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/ThomasPe/22a82c93dc43335dcd8838acd6a67b07#file-index-mustache" class="Link--inTextBlock">
          index.mustache
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>

</div></figure>



<p>Diese drei Dateien legen wir in einem eigenen &#8222;templates&#8220;-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.</p>



<figure class="wp-block-image alignnone wp-image-1591 size-full"><img loading="lazy" decoding="async" width="826" height="255" src="https://medienstudio.net/wp-content/uploads/2018/07/azure-storage-explorer-templates-directory.png" alt="" class="wp-image-1591" srcset="https://medienstudio.net/wp-content/uploads/2018/07/azure-storage-explorer-templates-directory.png 826w, https://medienstudio.net/wp-content/uploads/2018/07/azure-storage-explorer-templates-directory-300x93.png 300w, https://medienstudio.net/wp-content/uploads/2018/07/azure-storage-explorer-templates-directory-768x237.png 768w, https://medienstudio.net/wp-content/uploads/2018/07/azure-storage-explorer-templates-directory-200x62.png 200w, https://medienstudio.net/wp-content/uploads/2018/07/azure-storage-explorer-templates-directory-400x123.png 400w, https://medienstudio.net/wp-content/uploads/2018/07/azure-storage-explorer-templates-directory-600x185.png 600w, https://medienstudio.net/wp-content/uploads/2018/07/azure-storage-explorer-templates-directory-800x247.png 800w" sizes="auto, (max-width: 826px) 100vw, 826px" /><figcaption>Das &#8222;templates&#8220; Verzeichnis in Azure Blob Storage (Azure Storage Explorer)</figcaption></figure>



<h2 class="wp-block-heading">Die Azure Function</h2>



<p>Wie oben erwähnt nutzen wir JavaScript für die Logik der Function. Diese habe ich mit&nbsp;<a href="https://code.visualstudio.com/">Visual Studio Code</a>&nbsp;erstellt und ein paar npm Packages hinzugefügt, die wir benötigen:</p>



<ul class="wp-block-list"><li><a href="https://www.npmjs.com/package/request">request</a> &#8211; für den Download der News</li><li><a href="https://github.com/raycmorgan/Mu">mu2</a> &#8211; eine Node.js Mustache engine</li><li><a href="https://www.npmjs.com/package/azure-storage">azure-storage</a> &#8211; das SDK um die fertigen HTML Dateien in den Azure Blob Storage hochzuladen</li></ul>



<p>Fangen wir damit an, die News-Artikel in der&nbsp;<code>mynews</code>&nbsp;Variable zu speichern:</p>



<figure class="wp-block-embed is-type-rich is-provider-handler-einbetten"><div class="wp-block-embed__wrapper">
<style>.gist table { margin-bottom: 0; }</style><div style="tab-size: 8" id="gist91038048" class="gist">
    <div class="gist-file" translate="no" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        
<div class="js-gist-file-update-container js-task-list-container">
      <div id="file-getdata-js" class="file my-2">
    
    <div itemprop="text"
      class="Box-body p-0 blob-wrapper data type-javascript  "
      style="overflow: auto" tabindex="0" role="region"
      aria-label="getData.js content, created by ThomasPe on 11:37AM on July 30, 2018."
    >

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  <template class="js-file-alert-template">
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  <svg aria-hidden="true" data-component="Octicon" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-alert">
    <path d="M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z"></path>
</svg>
    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div></template>
<template class="js-line-alert-template">
  <span aria-label="This line has hidden Unicode characters" data-view-component="true" class="line-alert tooltipped tooltipped-e">
    <svg aria-hidden="true" data-component="Octicon" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-alert">
    <path d="M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z"></path>
</svg>
</span></template>

  <table data-hpc class="highlight tab-size js-file-line-container" data-tab-size="4" data-paste-markdown-skip data-tagsearch-path="getData.js">
        <tr>
          <td id="file-getdata-js-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-getdata-js-LC1" class="blob-code blob-code-inner js-file-line">function getData(){</td>
        </tr>
        <tr>
          <td id="file-getdata-js-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-getdata-js-LC2" class="blob-code blob-code-inner js-file-line">    context.log(&#39;get data&#39;);</td>
        </tr>
        <tr>
          <td id="file-getdata-js-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-getdata-js-LC3" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-getdata-js-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-getdata-js-LC4" class="blob-code blob-code-inner js-file-line">    var options = {</td>
        </tr>
        <tr>
          <td id="file-getdata-js-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-getdata-js-LC5" class="blob-code blob-code-inner js-file-line">        url: &#39;https://windowsarea.de/wp-json/wp/v2/posts&#39;,</td>
        </tr>
        <tr>
          <td id="file-getdata-js-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-getdata-js-LC6" class="blob-code blob-code-inner js-file-line">        method: &#39;GET&#39;,</td>
        </tr>
        <tr>
          <td id="file-getdata-js-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-getdata-js-LC7" class="blob-code blob-code-inner js-file-line">        json: true</td>
        </tr>
        <tr>
          <td id="file-getdata-js-L8" class="blob-num js-line-number js-blob-rnum" data-line-number="8"></td>
          <td id="file-getdata-js-LC8" class="blob-code blob-code-inner js-file-line">    }</td>
        </tr>
        <tr>
          <td id="file-getdata-js-L9" class="blob-num js-line-number js-blob-rnum" data-line-number="9"></td>
          <td id="file-getdata-js-LC9" class="blob-code blob-code-inner js-file-line">    request(options, function (error, response, data) {</td>
        </tr>
        <tr>
          <td id="file-getdata-js-L10" class="blob-num js-line-number js-blob-rnum" data-line-number="10"></td>
          <td id="file-getdata-js-LC10" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-getdata-js-L11" class="blob-num js-line-number js-blob-rnum" data-line-number="11"></td>
          <td id="file-getdata-js-LC11" class="blob-code blob-code-inner js-file-line">        context.log(&quot;request callback&quot;);</td>
        </tr>
        <tr>
          <td id="file-getdata-js-L12" class="blob-num js-line-number js-blob-rnum" data-line-number="12"></td>
          <td id="file-getdata-js-LC12" class="blob-code blob-code-inner js-file-line">        if (error) {</td>
        </tr>
        <tr>
          <td id="file-getdata-js-L13" class="blob-num js-line-number js-blob-rnum" data-line-number="13"></td>
          <td id="file-getdata-js-LC13" class="blob-code blob-code-inner js-file-line">            context.log(error);</td>
        </tr>
        <tr>
          <td id="file-getdata-js-L14" class="blob-num js-line-number js-blob-rnum" data-line-number="14"></td>
          <td id="file-getdata-js-LC14" class="blob-code blob-code-inner js-file-line">        }</td>
        </tr>
        <tr>
          <td id="file-getdata-js-L15" class="blob-num js-line-number js-blob-rnum" data-line-number="15"></td>
          <td id="file-getdata-js-LC15" class="blob-code blob-code-inner js-file-line">        else {</td>
        </tr>
        <tr>
          <td id="file-getdata-js-L16" class="blob-num js-line-number js-blob-rnum" data-line-number="16"></td>
          <td id="file-getdata-js-LC16" class="blob-code blob-code-inner js-file-line">            context.log(data.length);</td>
        </tr>
        <tr>
          <td id="file-getdata-js-L17" class="blob-num js-line-number js-blob-rnum" data-line-number="17"></td>
          <td id="file-getdata-js-LC17" class="blob-code blob-code-inner js-file-line">            mynews = data;</td>
        </tr>
        <tr>
          <td id="file-getdata-js-L18" class="blob-num js-line-number js-blob-rnum" data-line-number="18"></td>
          <td id="file-getdata-js-LC18" class="blob-code blob-code-inner js-file-line">        }</td>
        </tr>
        <tr>
          <td id="file-getdata-js-L19" class="blob-num js-line-number js-blob-rnum" data-line-number="19"></td>
          <td id="file-getdata-js-LC19" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-getdata-js-L20" class="blob-num js-line-number js-blob-rnum" data-line-number="20"></td>
          <td id="file-getdata-js-LC20" class="blob-code blob-code-inner js-file-line">        prepareTemplates();</td>
        </tr>
        <tr>
          <td id="file-getdata-js-L21" class="blob-num js-line-number js-blob-rnum" data-line-number="21"></td>
          <td id="file-getdata-js-LC21" class="blob-code blob-code-inner js-file-line">    });</td>
        </tr>
        <tr>
          <td id="file-getdata-js-L22" class="blob-num js-line-number js-blob-rnum" data-line-number="22"></td>
          <td id="file-getdata-js-LC22" class="blob-code blob-code-inner js-file-line">}</td>
        </tr>
  </table>
</div>


    </div>

  </div>

</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/ThomasPe/5fc8e43dd888f378234d3a7d84e74ce3/raw/79aef0265267f0c95266a1668b7b7328eb57e102/getData.js" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/ThomasPe/5fc8e43dd888f378234d3a7d84e74ce3#file-getdata-js" class="Link--inTextBlock">
          getData.js
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>

</div></figure>



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



<p>Anschließend rufen wir die&nbsp;<code>prepareTemplates()</code>&nbsp;Methode auf, welche die Header- und Footer-Dateien zur weiteren Verwendung mit mustache vorbereitet. Die Dateien können wir&nbsp; direkt über das context.bindings Object aufrufen. Ein Blick in die&nbsp;<a href="https://github.com/ThomasPe/MyStartPage-Azure-Functions-Storage/blob/master/StartPageGenerator/function.json">functions.json</a>&nbsp;Datei verrät, wie die Azure Function konfiguriert wurde.</p>



<figure class="wp-block-embed is-type-rich is-provider-handler-einbetten"><div class="wp-block-embed__wrapper">
<style>.gist table { margin-bottom: 0; }</style><div style="tab-size: 8" id="gist91038238" class="gist">
    <div class="gist-file" translate="no" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        
<div class="js-gist-file-update-container js-task-list-container">
      <div id="file-preparetemplates-js" class="file my-2">
    
    <div itemprop="text"
      class="Box-body p-0 blob-wrapper data type-javascript  "
      style="overflow: auto" tabindex="0" role="region"
      aria-label="prepareTemplates.js content, created by ThomasPe on 11:47AM on July 30, 2018."
    >

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  <template class="js-file-alert-template">
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  <svg aria-hidden="true" data-component="Octicon" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-alert">
    <path d="M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z"></path>
</svg>
    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div></template>
<template class="js-line-alert-template">
  <span aria-label="This line has hidden Unicode characters" data-view-component="true" class="line-alert tooltipped tooltipped-e">
    <svg aria-hidden="true" data-component="Octicon" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-alert">
    <path d="M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z"></path>
</svg>
</span></template>

  <table data-hpc class="highlight tab-size js-file-line-container" data-tab-size="4" data-paste-markdown-skip data-tagsearch-path="prepareTemplates.js">
        <tr>
          <td id="file-preparetemplates-js-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-preparetemplates-js-LC1" class="blob-code blob-code-inner js-file-line">function prepareTemplates(){</td>
        </tr>
        <tr>
          <td id="file-preparetemplates-js-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-preparetemplates-js-LC2" class="blob-code blob-code-inner js-file-line">    context.log(&#39;prepare Templates&#39;);</td>
        </tr>
        <tr>
          <td id="file-preparetemplates-js-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-preparetemplates-js-LC3" class="blob-code blob-code-inner js-file-line">    mu.compileText(&#39;header.mustache&#39;, context.bindings.headerTemplate, function () {</td>
        </tr>
        <tr>
          <td id="file-preparetemplates-js-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-preparetemplates-js-LC4" class="blob-code blob-code-inner js-file-line">        mu.compileText(&#39;footer.mustache&#39;, context.bindings.footerTemplate, function () {</td>
        </tr>
        <tr>
          <td id="file-preparetemplates-js-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-preparetemplates-js-LC5" class="blob-code blob-code-inner js-file-line">            renderSite();</td>
        </tr>
        <tr>
          <td id="file-preparetemplates-js-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-preparetemplates-js-LC6" class="blob-code blob-code-inner js-file-line">        });</td>
        </tr>
        <tr>
          <td id="file-preparetemplates-js-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-preparetemplates-js-LC7" class="blob-code blob-code-inner js-file-line">    });</td>
        </tr>
        <tr>
          <td id="file-preparetemplates-js-L8" class="blob-num js-line-number js-blob-rnum" data-line-number="8"></td>
          <td id="file-preparetemplates-js-LC8" class="blob-code blob-code-inner js-file-line">}</td>
        </tr>
        <tr>
          <td id="file-preparetemplates-js-L9" class="blob-num js-line-number js-blob-rnum" data-line-number="9"></td>
          <td id="file-preparetemplates-js-LC9" class="blob-code blob-code-inner js-file-line">    </td>
        </tr>
  </table>
</div>


    </div>

  </div>

</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/ThomasPe/97d7ba44fc03a41d959ebfa0018d72d9/raw/cf1eab890aaa47128e6bbdabc8b944e63f194e08/prepareTemplates.js" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/ThomasPe/97d7ba44fc03a41d959ebfa0018d72d9#file-preparetemplates-js" class="Link--inTextBlock">
          prepareTemplates.js
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>

</div></figure>



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



<figure class="wp-block-embed is-type-rich is-provider-handler-einbetten"><div class="wp-block-embed__wrapper">
<style>.gist table { margin-bottom: 0; }</style><div style="tab-size: 8" id="gist91038293" class="gist">
    <div class="gist-file" translate="no" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        
<div class="js-gist-file-update-container js-task-list-container">
      <div id="file-rendersite-js" class="file my-2">
    
    <div itemprop="text"
      class="Box-body p-0 blob-wrapper data type-javascript  "
      style="overflow: auto" tabindex="0" role="region"
      aria-label="renderSite.js content, created by ThomasPe on 11:50AM on July 30, 2018."
    >

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  <template class="js-file-alert-template">
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  <svg aria-hidden="true" data-component="Octicon" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-alert">
    <path d="M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z"></path>
</svg>
    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div></template>
<template class="js-line-alert-template">
  <span aria-label="This line has hidden Unicode characters" data-view-component="true" class="line-alert tooltipped tooltipped-e">
    <svg aria-hidden="true" data-component="Octicon" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-alert">
    <path d="M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z"></path>
</svg>
</span></template>

  <table data-hpc class="highlight tab-size js-file-line-container" data-tab-size="4" data-paste-markdown-skip data-tagsearch-path="renderSite.js">
        <tr>
          <td id="file-rendersite-js-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-rendersite-js-LC1" class="blob-code blob-code-inner js-file-line">function renderSite(){</td>
        </tr>
        <tr>
          <td id="file-rendersite-js-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-rendersite-js-LC2" class="blob-code blob-code-inner js-file-line">    context.log(&quot;render site&quot;);</td>
        </tr>
        <tr>
          <td id="file-rendersite-js-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-rendersite-js-LC3" class="blob-code blob-code-inner js-file-line">    var pageFile = &quot;&quot;;</td>
        </tr>
        <tr>
          <td id="file-rendersite-js-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-rendersite-js-LC4" class="blob-code blob-code-inner js-file-line">    mu.compileText(&#39;index.mustache&#39;, context.bindings.indexTemplate, function (err, parsed) {</td>
        </tr>
        <tr>
          <td id="file-rendersite-js-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-rendersite-js-LC5" class="blob-code blob-code-inner js-file-line">        var renderstream = mu.render(parsed, { news: mynews, links: mylinks });</td>
        </tr>
        <tr>
          <td id="file-rendersite-js-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-rendersite-js-LC6" class="blob-code blob-code-inner js-file-line">        renderstream.on(&#39;data&#39;, function (data) {</td>
        </tr>
        <tr>
          <td id="file-rendersite-js-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-rendersite-js-LC7" class="blob-code blob-code-inner js-file-line">            pageFile += data;</td>
        </tr>
        <tr>
          <td id="file-rendersite-js-L8" class="blob-num js-line-number js-blob-rnum" data-line-number="8"></td>
          <td id="file-rendersite-js-LC8" class="blob-code blob-code-inner js-file-line">        });</td>
        </tr>
        <tr>
          <td id="file-rendersite-js-L9" class="blob-num js-line-number js-blob-rnum" data-line-number="9"></td>
          <td id="file-rendersite-js-LC9" class="blob-code blob-code-inner js-file-line">        renderstream.on(&#39;end&#39;, function (data) {</td>
        </tr>
        <tr>
          <td id="file-rendersite-js-L10" class="blob-num js-line-number js-blob-rnum" data-line-number="10"></td>
          <td id="file-rendersite-js-LC10" class="blob-code blob-code-inner js-file-line">            context.log(&quot;done creating index.html&quot;);</td>
        </tr>
        <tr>
          <td id="file-rendersite-js-L11" class="blob-num js-line-number js-blob-rnum" data-line-number="11"></td>
          <td id="file-rendersite-js-LC11" class="blob-code blob-code-inner js-file-line">            uploadSite(pageFile);</td>
        </tr>
        <tr>
          <td id="file-rendersite-js-L12" class="blob-num js-line-number js-blob-rnum" data-line-number="12"></td>
          <td id="file-rendersite-js-LC12" class="blob-code blob-code-inner js-file-line">        });</td>
        </tr>
        <tr>
          <td id="file-rendersite-js-L13" class="blob-num js-line-number js-blob-rnum" data-line-number="13"></td>
          <td id="file-rendersite-js-LC13" class="blob-code blob-code-inner js-file-line">    });</td>
        </tr>
        <tr>
          <td id="file-rendersite-js-L14" class="blob-num js-line-number js-blob-rnum" data-line-number="14"></td>
          <td id="file-rendersite-js-LC14" class="blob-code blob-code-inner js-file-line">}</td>
        </tr>
  </table>
</div>


    </div>

  </div>

</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/ThomasPe/02e0c55c0a925c5492b7c5c026963e08/raw/a4313554317a8b189711910cf708e10615f4cc2f/renderSite.js" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/ThomasPe/02e0c55c0a925c5492b7c5c026963e08#file-rendersite-js" class="Link--inTextBlock">
          renderSite.js
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>

</div></figure>



<p>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, <a href="https://medienstudio.net/development/richtiger-content-type-bei-statischen-websites-in-azure-storage/">was in manchen Browsern zu Problemen führt</a>.</p>



<p>Zunächst legen wir aber noch das benötigte&nbsp;<code>$web</code> Verzeichnis an und speichern dort dann unsere HTML-Datei als neuen Blob. Hier lässt sich dann auch der richtige Content Type &#8218;text/html&#8216; mit übergeben.</p>



<figure class="wp-block-embed is-type-rich is-provider-handler-einbetten"><div class="wp-block-embed__wrapper">
<style>.gist table { margin-bottom: 0; }</style><div style="tab-size: 8" id="gist91038338" class="gist">
    <div class="gist-file" translate="no" data-color-mode="light" data-light-theme="light">
      <div class="gist-data">
        
<div class="js-gist-file-update-container js-task-list-container">
      <div id="file-uploadsite-js" class="file my-2">
    
    <div itemprop="text"
      class="Box-body p-0 blob-wrapper data type-javascript  "
      style="overflow: auto" tabindex="0" role="region"
      aria-label="uploadSite.js content, created by ThomasPe on 11:53AM on July 30, 2018."
    >

        
<div class="js-check-hidden-unicode js-blob-code-container blob-code-content">

  <template class="js-file-alert-template">
  <div data-view-component="true" class="flash flash-warn flash-full d-flex flex-items-center">
  <svg aria-hidden="true" data-component="Octicon" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-alert">
    <path d="M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z"></path>
</svg>
    <span>
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      <a class="Link--inTextBlock" href="https://github.co/hiddenchars" target="_blank">Learn more about bidirectional Unicode characters</a>
    </span>


  <div data-view-component="true" class="flash-action">        <a href="{{ revealButtonHref }}" data-view-component="true" class="btn-sm btn">    Show hidden characters
</a>
</div>
</div></template>
<template class="js-line-alert-template">
  <span aria-label="This line has hidden Unicode characters" data-view-component="true" class="line-alert tooltipped tooltipped-e">
    <svg aria-hidden="true" data-component="Octicon" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-alert">
    <path d="M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z"></path>
</svg>
</span></template>

  <table data-hpc class="highlight tab-size js-file-line-container" data-tab-size="4" data-paste-markdown-skip data-tagsearch-path="uploadSite.js">
        <tr>
          <td id="file-uploadsite-js-L1" class="blob-num js-line-number js-blob-rnum" data-line-number="1"></td>
          <td id="file-uploadsite-js-LC1" class="blob-code blob-code-inner js-file-line">function uploadSite(page){</td>
        </tr>
        <tr>
          <td id="file-uploadsite-js-L2" class="blob-num js-line-number js-blob-rnum" data-line-number="2"></td>
          <td id="file-uploadsite-js-LC2" class="blob-code blob-code-inner js-file-line">    context.log(&quot;Upload site &quot;);</td>
        </tr>
        <tr>
          <td id="file-uploadsite-js-L3" class="blob-num js-line-number js-blob-rnum" data-line-number="3"></td>
          <td id="file-uploadsite-js-LC3" class="blob-code blob-code-inner js-file-line">    var blobService = storage.createBlobService();</td>
        </tr>
        <tr>
          <td id="file-uploadsite-js-L4" class="blob-num js-line-number js-blob-rnum" data-line-number="4"></td>
          <td id="file-uploadsite-js-LC4" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-uploadsite-js-L5" class="blob-num js-line-number js-blob-rnum" data-line-number="5"></td>
          <td id="file-uploadsite-js-LC5" class="blob-code blob-code-inner js-file-line">    // create $web container</td>
        </tr>
        <tr>
          <td id="file-uploadsite-js-L6" class="blob-num js-line-number js-blob-rnum" data-line-number="6"></td>
          <td id="file-uploadsite-js-LC6" class="blob-code blob-code-inner js-file-line">    blobService.createContainerIfNotExists(&#39;$web&#39;, function(){</td>
        </tr>
        <tr>
          <td id="file-uploadsite-js-L7" class="blob-num js-line-number js-blob-rnum" data-line-number="7"></td>
          <td id="file-uploadsite-js-LC7" class="blob-code blob-code-inner js-file-line">
</td>
        </tr>
        <tr>
          <td id="file-uploadsite-js-L8" class="blob-num js-line-number js-blob-rnum" data-line-number="8"></td>
          <td id="file-uploadsite-js-LC8" class="blob-code blob-code-inner js-file-line">        // upload index.html to $web container</td>
        </tr>
        <tr>
          <td id="file-uploadsite-js-L9" class="blob-num js-line-number js-blob-rnum" data-line-number="9"></td>
          <td id="file-uploadsite-js-LC9" class="blob-code blob-code-inner js-file-line">        const options = { contentSettings: { contentType: &#39;text/html&#39; } }</td>
        </tr>
        <tr>
          <td id="file-uploadsite-js-L10" class="blob-num js-line-number js-blob-rnum" data-line-number="10"></td>
          <td id="file-uploadsite-js-LC10" class="blob-code blob-code-inner js-file-line">        blobService.createBlockBlobFromText(&quot;$web&quot;, &quot;index.html&quot;, page, options, function (error) {</td>
        </tr>
        <tr>
          <td id="file-uploadsite-js-L11" class="blob-num js-line-number js-blob-rnum" data-line-number="11"></td>
          <td id="file-uploadsite-js-LC11" class="blob-code blob-code-inner js-file-line">            context.log(&quot;uploaded&quot;);</td>
        </tr>
        <tr>
          <td id="file-uploadsite-js-L12" class="blob-num js-line-number js-blob-rnum" data-line-number="12"></td>
          <td id="file-uploadsite-js-LC12" class="blob-code blob-code-inner js-file-line">            context.done();</td>
        </tr>
        <tr>
          <td id="file-uploadsite-js-L13" class="blob-num js-line-number js-blob-rnum" data-line-number="13"></td>
          <td id="file-uploadsite-js-LC13" class="blob-code blob-code-inner js-file-line">        });</td>
        </tr>
        <tr>
          <td id="file-uploadsite-js-L14" class="blob-num js-line-number js-blob-rnum" data-line-number="14"></td>
          <td id="file-uploadsite-js-LC14" class="blob-code blob-code-inner js-file-line">    });</td>
        </tr>
        <tr>
          <td id="file-uploadsite-js-L15" class="blob-num js-line-number js-blob-rnum" data-line-number="15"></td>
          <td id="file-uploadsite-js-LC15" class="blob-code blob-code-inner js-file-line">}</td>
        </tr>
  </table>
</div>


    </div>

  </div>

</div>

      </div>
      <div class="gist-meta">
        <a href="https://gist.github.com/ThomasPe/49f7621769e1ff501dc9060ea4529dcd/raw/15bfad7151038e087bd48a3a0a81a636476651c4/uploadSite.js" style="float:right" class="Link--inTextBlock">view raw</a>
        <a href="https://gist.github.com/ThomasPe/49f7621769e1ff501dc9060ea4529dcd#file-uploadsite-js" class="Link--inTextBlock">
          uploadSite.js
        </a>
        hosted with &#10084; by <a class="Link--inTextBlock" href="https://github.com">GitHub</a>
      </div>
    </div>
</div>

</div></figure>



<p>Und das wars. <a href="https://github.com/ThomasPe/MyStartPage-Azure-Functions-Storage">Der gesamte Code kann auf Github gefunden werden</a>. Hier findet ihr das finale, wunderbar gestylte Ergebnis.</p>
<p>The post <a href="https://medienstudio.net/development/building-static-websites-with-azure-functions-mustache-azure-storage/">Building static websites with Azure Functions, mustache &#038; Azure Storage</a> appeared first on <a href="https://medienstudio.net">medienstudio.net</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://medienstudio.net/development/building-static-websites-with-azure-functions-mustache-azure-storage/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1603</post-id>	</item>
		<item>
		<title>Richtiger Content Type bei statischen Websites in Azure Storage</title>
		<link>https://medienstudio.net/development/richtiger-content-type-bei-statischen-websites-in-azure-storage/</link>
					<comments>https://medienstudio.net/development/richtiger-content-type-bei-statischen-websites-in-azure-storage/#respond</comments>
		
		<dc:creator><![CDATA[Thomas]]></dc:creator>
		<pubDate>Wed, 25 Jul 2018 16:03:01 +0000</pubDate>
				<category><![CDATA[Allgemein]]></category>
		<category><![CDATA[Development]]></category>
		<category><![CDATA[azure]]></category>
		<guid isPermaLink="false">https://medienstudio.net/?p=1566</guid>

					<description><![CDATA[<p>Aktuell experimentiere ich mit dem neuen Static website hosting for Azure Storage Feature, welches seit kurzem als Public Preview verfügbar ist. Hiermit lässt sich statischer Webcontent (HTML, CSS, JS, Bilder) direkt aus einem speziellen Azure Storage Blob-Verzeichnis aufrufen, ohne dass man hierfür noch irgendwelche Proxies einrichten muss. Der große Vorteil hierbei ist, dass man sich nicht &#8230; <a href="https://medienstudio.net/development/richtiger-content-type-bei-statischen-websites-in-azure-storage/" class="more-link"><span class="screen-reader-text">Richtiger Content Type bei statischen Websites in Azure Storage</span> weiterlesen</a></p>
<p>The post <a href="https://medienstudio.net/development/richtiger-content-type-bei-statischen-websites-in-azure-storage/">Richtiger Content Type bei statischen Websites in Azure Storage</a> appeared first on <a href="https://medienstudio.net">medienstudio.net</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>Aktuell experimentiere ich mit dem neuen <a href="https://azure.microsoft.com/en-us/blog/azure-storage-static-web-hosting-public-preview/?WT.mc_id=staticwebsites-twitter-antchu">Static website hosting for Azure Storage</a> Feature, welches seit kurzem als Public Preview verfügbar ist. Hiermit lässt sich statischer Webcontent (HTML, CSS, JS, Bilder) direkt aus einem speziellen Azure Storage Blob-Verzeichnis aufrufen, ohne dass man hierfür noch irgendwelche Proxies einrichten muss.</p>
<p><span id="more-1566"></span></p>
<p>Der große Vorteil hierbei ist, dass man sich nicht mehr um die typischen Webhosting-Fragen kümmern muss. Wie viele Kerne oder RAM brauche ich? Wie viele Instanzen? Und an welchen Standorten? Das alles kann man auch einfach Azure automatisch managen lassen. <strong> Serverless all the way!</strong></p>
<p>Nachdem ich also das Preview-Feature aktiviert und meine statische Website im $web Verzeichnis meines Storage Accounts abgelegt habe, hat die Seite auch direkt in Microsoft Edge funktioniert. In Chrome wurde jedoch statt die Seite anzuzeigen nur ein nicht weiter definiertes File heruntergeladen. Ein Blick mit dem Azure Storage Explorer in das Verzeichnis verriet, dass meine Dateien als <code>application/octet-stream</code> gespeichert wurden.</p>
<p>Richtig wäre in diesem Fall allerdings <code>text/html</code>. Das zu ändern geht am einfachsten erneut über den Azure Storage Explorer:</p>
<p>Rechtsklick auf die Datei =&gt; Properties&#8230; Hier kann das Content Type Feld dann entsprechend angepasst werden.</p>
<p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-1559" src="https://medienstudio.net/wp-content/uploads/2018/07/azure-storage-explorer-content-type.jpg" alt="" width="506" height="593" srcset="https://medienstudio.net/wp-content/uploads/2018/07/azure-storage-explorer-content-type.jpg 506w, https://medienstudio.net/wp-content/uploads/2018/07/azure-storage-explorer-content-type-256x300.jpg 256w, https://medienstudio.net/wp-content/uploads/2018/07/azure-storage-explorer-content-type-200x234.jpg 200w, https://medienstudio.net/wp-content/uploads/2018/07/azure-storage-explorer-content-type-400x469.jpg 400w" sizes="auto, (max-width: 506px) 100vw, 506px" /></p>
<p>Für meine Zwecke war dies allerdings nicht ausreichend, da meine Seite täglich via  Azure Node SDK neu hochgeladen wird. Die von mir dabei benutzte Methode <code>createBlockBlobFromText</code> (docs) akzeptiert zusätzlich noch diverse Optionen, über die auch der Content Type richtig gesetzt werden kann:</p>
<p><code>const options = { contentSettings: { contentType: 'text/html' } }</code></p>
<p><strong>In letzter Zeit hat sich im SDK die Struktur dieses Options-Objekts geändert, sodass es noch zahlreiche veraltete Beispiele zu finden gibt. Leider wirft ein falsches Options-Objekt keinen Fehler sondern wird einfach ignoriert.</strong></p>
<p>Übrigens finde ich, dass Azure Storage durchaus den Content Type automatisch richtig anhand der Dateiendung setzten könnte&#8230;</p>
<p>The post <a href="https://medienstudio.net/development/richtiger-content-type-bei-statischen-websites-in-azure-storage/">Richtiger Content Type bei statischen Websites in Azure Storage</a> appeared first on <a href="https://medienstudio.net">medienstudio.net</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://medienstudio.net/development/richtiger-content-type-bei-statischen-websites-in-azure-storage/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1566</post-id>	</item>
		<item>
		<title>CSS Background-Url Performance Probleme (TIL)</title>
		<link>https://medienstudio.net/development/css-background-url-performance-probleme-til/</link>
					<comments>https://medienstudio.net/development/css-background-url-performance-probleme-til/#respond</comments>
		
		<dc:creator><![CDATA[Thomas]]></dc:creator>
		<pubDate>Tue, 19 Dec 2017 16:24:31 +0000</pubDate>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[css]]></category>
		<category><![CDATA[til]]></category>
		<guid isPermaLink="false">https://medienstudio.net/?p=1310</guid>

					<description><![CDATA[<p>Aktuell bauen mein Kollege und ich eine Angular 4 + Cordova App für Android und iOS. Eine Kernfunktion ist hier der Fotoupload, welcher mehrere Bilder entgegennimmt und diese als Previews anzeigt, während der Upload läuft. Die Bilder werden in einer Liste als quadratische Kacheln angezeigt, indem die Foto-URL als background image mit background-size: cover geladen &#8230; <a href="https://medienstudio.net/development/css-background-url-performance-probleme-til/" class="more-link"><span class="screen-reader-text">CSS Background-Url Performance Probleme (TIL)</span> weiterlesen</a></p>
<p>The post <a href="https://medienstudio.net/development/css-background-url-performance-probleme-til/">CSS Background-Url Performance Probleme (TIL)</a> appeared first on <a href="https://medienstudio.net">medienstudio.net</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>Aktuell bauen mein Kollege und ich eine Angular 4 + Cordova App für Android und iOS. Eine Kernfunktion ist hier der Fotoupload, welcher mehrere Bilder entgegennimmt und diese als Previews anzeigt, während der Upload läuft.</p>



<span id="more-1310"></span>



<p>Die Bilder werden in einer Liste als quadratische Kacheln angezeigt, indem die Foto-URL als background image mit <i>background-size</i><span style="display: inline !important; float: none; background-color: transparent; color: #333333; cursor: text; font-family: -apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',sans-serif; font-size: 16px; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; -webkit-text-stroke-width: 0px; white-space: normal; word-spacing: 0px;">: </span><em>cover </em>geladen wird. Allerdings können diese Fotos dank neuster Smartphonekameras schnell über 3MB groß werden, was zu enormen Performanceproblemen geführt hat. Vor allem die Navigation innerhalb des Angular Material Tab Controls dauerte auf schwächerer Hardware schnell mehrere Sekunden.</p>



<p>Die Fotos im normalen <em>img</em>-Tag hingegen konnten trotz ihrer Größe problemlos angezeigt werden &#8211; allerdings auch nur, wenn man auf&nbsp;<i>object-fit: cover;</i> verzichtet. Offensichtlich ist hier die Neuberechnung der Bildgröße das Problem.</p>



<p>Das Resultat ist jetzt eine Liste mit Bildern im Original-Seitenverhältnis. Nicht ganz so schön, aber dafür läuft&#8217;s butterweich. <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f642.png" alt="🙂" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<p>The post <a href="https://medienstudio.net/development/css-background-url-performance-probleme-til/">CSS Background-Url Performance Probleme (TIL)</a> appeared first on <a href="https://medienstudio.net">medienstudio.net</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://medienstudio.net/development/css-background-url-performance-probleme-til/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1310</post-id>	</item>
		<item>
		<title>Fehlercode 7:3:0:0:1 &#8211; Amazon Echo &#038; Unitymedia WLAN Probelme beheben</title>
		<link>https://medienstudio.net/allgemein/fehlercode-73001-alexa-unitymedia-wlan-probelme-beheben/</link>
					<comments>https://medienstudio.net/allgemein/fehlercode-73001-alexa-unitymedia-wlan-probelme-beheben/#comments</comments>
		
		<dc:creator><![CDATA[Thomas]]></dc:creator>
		<pubDate>Wed, 12 Jul 2017 15:01:04 +0000</pubDate>
				<category><![CDATA[Allgemein]]></category>
		<category><![CDATA[Alexa]]></category>
		<category><![CDATA[Amazon Echo]]></category>
		<category><![CDATA[Unitymedia]]></category>
		<guid isPermaLink="false">https://medienstudio.net/?p=1102</guid>

					<description><![CDATA[<p>Heute kam Alexa bzw. mein neuer Amazon Echo an, welcher leider beim Setup Probleme bei der Verbindung zum WLAN (Unitymedia Router) hatte. Die App hat bei jedem Verbindungsversuch nur den Fehlercode&#160;7:3:0:0:1 ausgegeben. Lösung des Problems war, die Verschlüsselung des WLANs in den Router-Einstellungen von WPA-PSK/WPA2-PSK auf WPA2-PSK umzustellen. Nach einem Neustart des Routers konnte die &#8230; <a href="https://medienstudio.net/allgemein/fehlercode-73001-alexa-unitymedia-wlan-probelme-beheben/" class="more-link"><span class="screen-reader-text">Fehlercode 7:3:0:0:1 &#8211; Amazon Echo &#038; Unitymedia WLAN Probelme beheben</span> weiterlesen</a></p>
<p>The post <a href="https://medienstudio.net/allgemein/fehlercode-73001-alexa-unitymedia-wlan-probelme-beheben/">Fehlercode 7:3:0:0:1 &#8211; Amazon Echo &#038; Unitymedia WLAN Probelme beheben</a> appeared first on <a href="https://medienstudio.net">medienstudio.net</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>Heute kam Alexa bzw. mein neuer Amazon Echo an, welcher leider beim Setup Probleme bei der Verbindung zum WLAN (Unitymedia Router) hatte. Die App hat bei jedem Verbindungsversuch nur den Fehlercode&nbsp;7:3:0:0:1 ausgegeben.</p>



<p>Lösung des Problems war, die Verschlüsselung des WLANs in den Router-Einstellungen von <strong>WPA-PSK/WPA2-PSK</strong> auf <strong>WPA2-PSK</strong> umzustellen. Nach einem Neustart des Routers konnte die Verbindung auf Anhieb hergestellt werden.</p>
<p>The post <a href="https://medienstudio.net/allgemein/fehlercode-73001-alexa-unitymedia-wlan-probelme-beheben/">Fehlercode 7:3:0:0:1 &#8211; Amazon Echo &#038; Unitymedia WLAN Probelme beheben</a> appeared first on <a href="https://medienstudio.net">medienstudio.net</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://medienstudio.net/allgemein/fehlercode-73001-alexa-unitymedia-wlan-probelme-beheben/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1102</post-id>	</item>
		<item>
		<title>.NET Standard Nuget Packages in VSTS builden &#038; auf nuget.org veröffentlichen</title>
		<link>https://medienstudio.net/development/net-standard-nuget-packages-in-vsts-builden-auf-nuget-org-veroeffentlichen/</link>
					<comments>https://medienstudio.net/development/net-standard-nuget-packages-in-vsts-builden-auf-nuget-org-veroeffentlichen/#comments</comments>
		
		<dc:creator><![CDATA[Thomas]]></dc:creator>
		<pubDate>Wed, 24 May 2017 13:04:17 +0000</pubDate>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[.netstandard]]></category>
		<category><![CDATA[nuget]]></category>
		<category><![CDATA[vsts]]></category>
		<guid isPermaLink="false">https://medienstudio.net/?p=1051</guid>

					<description><![CDATA[<p>Vor einiger Zeit habe ich damit begonnen, einen C# Wrapper (WordPressPCL) für die neue WordPress REST API (verfügbar seit Version 4.7) zu schreiben. Zunächst war das ganze als normale Portable Class Library geplant (daher auch der Name). Mit der Veröffentlichung von .NET Standard habe das Projekt hierauf umgestellt. Eine super Einleitung in dieses Thema gibt &#8230; <a href="https://medienstudio.net/development/net-standard-nuget-packages-in-vsts-builden-auf-nuget-org-veroeffentlichen/" class="more-link"><span class="screen-reader-text">.NET Standard Nuget Packages in VSTS builden &#038; auf nuget.org veröffentlichen</span> weiterlesen</a></p>
<p>The post <a href="https://medienstudio.net/development/net-standard-nuget-packages-in-vsts-builden-auf-nuget-org-veroeffentlichen/">.NET Standard Nuget Packages in VSTS builden &#038; auf nuget.org veröffentlichen</a> appeared first on <a href="https://medienstudio.net">medienstudio.net</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>Vor einiger Zeit habe ich damit begonnen, einen C# Wrapper (<a href="https://github.com/wp-net/WordPressPCL">WordPressPCL</a>) für die neue WordPress REST API (verfügbar seit Version 4.7) zu schreiben. Zunächst war das ganze als normale Portable Class Library geplant (daher auch der Name). Mit der Veröffentlichung von .NET Standard habe das Projekt hierauf umgestellt. <a href="https://www.youtube.com/watch?v=YI4MurjfMn8&amp;list=PLRAdsfhKI4OWx321A_pr-7HhRNk7wOLLY">Eine super Einleitung in dieses Thema</a> gibt es auf YouTube von <a href="https://twitter.com/terrajobst">Immo Landwerth</a>.</p>



<p>Nachdem das Projekt in letzter Zeit vermehrt Pull Requests erhält, habe ich mich entschieden, den Build &amp; Publish Prozess zu automatisieren, um nicht immer manuell die neuste Version des NuGet-Packages hochladen zu müssen. Hierfür nutze ich die kostenlosen Visual Studio Team Services.</p>



<span id="more-1051"></span>



<p>Der grobe Plan hierfür sieht wie folgt aus:</p>



<ul class="wp-block-list"><li>Code von GitHub holen</li><li>.dll mit Visual Studio builden</li><li>NuGet Package erstellen</li><li>Package auf nuget.org veröffentlichen</li></ul>



<p>Continuous Integration ist in meinem Fall nicht notwendig, da ich die volle Kontrolle darüber behalten möchte, wann Packages gebaut und veröffentlich werden. Auch die Versionierung wird manuell gepflegt. <a href="https://liftcodeplay.com/2017/03/23/how-to-build-and-host-a-nuget-package-using-vs2017-and-net-standard-in-vsts/">Wie man die Version automatisch bei jedem Build erhöhen kann</a>, zeigt dieser Blogpost von <a href="https://twitter.com/bcnzer">Ben Chartrand</a>.</p>



<h2 class="wp-block-heading">Step 0: Projekt für NuGet konfigurieren</h2>



<p>Bevor man beginnt den Build-Prozess in VSTS zu erstellen, wird zunächst das Projekt in Visual Studio 2017 geöffnet. In den Projekteigenschaften (Project properties) muss in der Package-Sektion die Option <em>Generate NuGet package on build</em> aktiviert werden. In den restlichen Feldern werden die Informationen zum NuGet-Package eingetragen. Aus diesen Feldern wird beim Build die NuGet-Spezifikation erstellt. Anschließend nicht vergessen, den Code einzuchecken.</p>



<figure class="wp-block-image"><img loading="lazy" decoding="async" width="1500" height="1008" src="https://medienstudio.net/wp-content/uploads/2017/05/visual-studio-nuget.jpg" alt="Create NuGet Package in Visual Studio 2017" class="wp-image-1039" srcset="https://medienstudio.net/wp-content/uploads/2017/05/visual-studio-nuget.jpg 1500w, https://medienstudio.net/wp-content/uploads/2017/05/visual-studio-nuget-300x202.jpg 300w, https://medienstudio.net/wp-content/uploads/2017/05/visual-studio-nuget-768x516.jpg 768w, https://medienstudio.net/wp-content/uploads/2017/05/visual-studio-nuget-1024x688.jpg 1024w, https://medienstudio.net/wp-content/uploads/2017/05/visual-studio-nuget-200x134.jpg 200w, https://medienstudio.net/wp-content/uploads/2017/05/visual-studio-nuget-400x269.jpg 400w, https://medienstudio.net/wp-content/uploads/2017/05/visual-studio-nuget-600x403.jpg 600w, https://medienstudio.net/wp-content/uploads/2017/05/visual-studio-nuget-800x538.jpg 800w, https://medienstudio.net/wp-content/uploads/2017/05/visual-studio-nuget-1200x806.jpg 1200w" sizes="auto, (max-width: 1500px) 100vw, 1500px" /></figure>



<h2 class="wp-block-heading">Step 1: Build Definition erstellen</h2>



<figure class="wp-block-image"><img loading="lazy" decoding="async" width="1079" height="769" src="https://medienstudio.net/wp-content/uploads/2017/05/vsts-nuget-1.png" alt="Create new VSTS Build Definition" class="wp-image-1029" srcset="https://medienstudio.net/wp-content/uploads/2017/05/vsts-nuget-1.png 1079w, https://medienstudio.net/wp-content/uploads/2017/05/vsts-nuget-1-300x214.png 300w, https://medienstudio.net/wp-content/uploads/2017/05/vsts-nuget-1-768x547.png 768w, https://medienstudio.net/wp-content/uploads/2017/05/vsts-nuget-1-1024x730.png 1024w, https://medienstudio.net/wp-content/uploads/2017/05/vsts-nuget-1-200x143.png 200w, https://medienstudio.net/wp-content/uploads/2017/05/vsts-nuget-1-400x285.png 400w, https://medienstudio.net/wp-content/uploads/2017/05/vsts-nuget-1-600x428.png 600w, https://medienstudio.net/wp-content/uploads/2017/05/vsts-nuget-1-800x570.png 800w" sizes="auto, (max-width: 1079px) 100vw, 1079px" /></figure>



<p>Als Basis eignet sich hier das normale Visual Studio Template. Einige der Steps kann man direkt wieder entfernen (siehe nächstes Foto).</p>



<h2 class="wp-block-heading">Step 2: Code holen</h2>



<figure class="wp-block-image"><img loading="lazy" decoding="async" width="1079" height="769" src="https://medienstudio.net/wp-content/uploads/2017/05/vsts-nuget-2.png" alt="Get source code from GitHub" class="wp-image-1030" srcset="https://medienstudio.net/wp-content/uploads/2017/05/vsts-nuget-2.png 1079w, https://medienstudio.net/wp-content/uploads/2017/05/vsts-nuget-2-300x214.png 300w, https://medienstudio.net/wp-content/uploads/2017/05/vsts-nuget-2-768x547.png 768w, https://medienstudio.net/wp-content/uploads/2017/05/vsts-nuget-2-1024x730.png 1024w, https://medienstudio.net/wp-content/uploads/2017/05/vsts-nuget-2-200x143.png 200w, https://medienstudio.net/wp-content/uploads/2017/05/vsts-nuget-2-400x285.png 400w, https://medienstudio.net/wp-content/uploads/2017/05/vsts-nuget-2-600x428.png 600w, https://medienstudio.net/wp-content/uploads/2017/05/vsts-nuget-2-800x570.png 800w" sizes="auto, (max-width: 1079px) 100vw, 1079px" /></figure>



<p>Der Code kann nahezu jeder beliebigen Source Control (VSTS, GitHub oder jedes andere Git-Repository) geladen werden. In meinem Fall kommt der Code via GitHub, allerdings funktioniert der GitHub-Connector noch nicht mit Organisations-Repos, weshalb ich den Umweg über eine generische Git-Anbindung gehen musste.</p>



<h2 class="wp-block-heading">Step 3: Setup NuGet restore</h2>



<figure class="wp-block-image"><img loading="lazy" decoding="async" width="1079" height="769" src="https://medienstudio.net/wp-content/uploads/2017/05/vsts-nuget-3.png" alt="" class="wp-image-1032" srcset="https://medienstudio.net/wp-content/uploads/2017/05/vsts-nuget-3.png 1079w, https://medienstudio.net/wp-content/uploads/2017/05/vsts-nuget-3-300x214.png 300w, https://medienstudio.net/wp-content/uploads/2017/05/vsts-nuget-3-768x547.png 768w, https://medienstudio.net/wp-content/uploads/2017/05/vsts-nuget-3-1024x730.png 1024w, https://medienstudio.net/wp-content/uploads/2017/05/vsts-nuget-3-200x143.png 200w, https://medienstudio.net/wp-content/uploads/2017/05/vsts-nuget-3-400x285.png 400w, https://medienstudio.net/wp-content/uploads/2017/05/vsts-nuget-3-600x428.png 600w, https://medienstudio.net/wp-content/uploads/2017/05/vsts-nuget-3-800x570.png 800w" sizes="auto, (max-width: 1079px) 100vw, 1079px" /></figure>



<p>Damit die NuGet-Packages problemlos geladen werden, muss die NuGet Version 4.0.0 im Schritt <em>NuGet restore</em> gewählt werden.</p>



<h2 class="wp-block-heading">Step 4: Build solution</h2>



<figure class="wp-block-image"><img loading="lazy" decoding="async" width="1079" height="769" src="https://medienstudio.net/wp-content/uploads/2017/05/vsts-nuget-4.png" alt="Configure Build Step in VSTS" class="wp-image-1034" srcset="https://medienstudio.net/wp-content/uploads/2017/05/vsts-nuget-4.png 1079w, https://medienstudio.net/wp-content/uploads/2017/05/vsts-nuget-4-300x214.png 300w, https://medienstudio.net/wp-content/uploads/2017/05/vsts-nuget-4-768x547.png 768w, https://medienstudio.net/wp-content/uploads/2017/05/vsts-nuget-4-1024x730.png 1024w, https://medienstudio.net/wp-content/uploads/2017/05/vsts-nuget-4-200x143.png 200w, https://medienstudio.net/wp-content/uploads/2017/05/vsts-nuget-4-400x285.png 400w, https://medienstudio.net/wp-content/uploads/2017/05/vsts-nuget-4-600x428.png 600w, https://medienstudio.net/wp-content/uploads/2017/05/vsts-nuget-4-800x570.png 800w" sizes="auto, (max-width: 1079px) 100vw, 1079px" /></figure>



<p>Im Schritt Build Solution war es in diesem Fall notwendig, statt dem Platzhalter <em>**\*.sln</em> den Pfad zur tatsächlichen .csproj, also <em>WordPressPCL\WordPressPCL.csproj</em> einzutragen. Hierzu muss zunächst das Feld &#8222;unlinked&#8220; werden.</p>



<h2 class="wp-block-heading">Step 5: NuGet Publisher</h2>



<figure class="wp-block-image"><img decoding="async" src="https://medienstudio.net/wp-content/uploads/2017/05/vsts-nuget-6.png" alt="Configure NuGet Publisher Step" class="wp-image-1044"/></figure>



<p>Nun zum eigentlich interessanten Teil. Als finaler Schritt wird nun der <em>NuGet Publisher</em> Task hinzugefügt. Den NuGet Packager benötigen wir nicht, da das NuGet-Paket ja bereits von Visual Studio fertig ausgespuckt wird.</p>



<p>In diesem Schritt muss nun der NuGet Server Endpoint konfiguriert werden, was man über das kleine Zahnrad rechts neben dem Eingabefeld erledigen kann.<br>Über <em>New Service Endpoint -&gt; Generic</em> wird dem Endpoint ein Name, <em>https://nuget.org/</em> als die Server URL und der nuget.org Username eingetragen.</p>



<p>Unter <a href="https://www.nuget.org/account">nuget.org/account</a> muss zudem ein neuer API Schlüssel erstellt werden, den man als Password/Token Key verwendet.</p>



<p>Nachdem diese Einstellungen gespeichert wurden, kann man den neuen Endpoint im NuGet Publisher Schritt aus dem Dropdown-Menü auswählen. Auch in diesem Schritt stellen wir die NuGet Version auf 4.0.0.</p>



<p>Das wars. Die .NET Standard Bibliothek sollte jetzt als NuGet Package im öffentlichen nuget.org Feed veröffentlicht werden.</p>
<p>The post <a href="https://medienstudio.net/development/net-standard-nuget-packages-in-vsts-builden-auf-nuget-org-veroeffentlichen/">.NET Standard Nuget Packages in VSTS builden &#038; auf nuget.org veröffentlichen</a> appeared first on <a href="https://medienstudio.net">medienstudio.net</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://medienstudio.net/development/net-standard-nuget-packages-in-vsts-builden-auf-nuget-org-veroeffentlichen/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1051</post-id>	</item>
		<item>
		<title>DragonBoard GPIO-ports erweitern mit MCP23017-ESP und Windows 10 IoT Core</title>
		<link>https://medienstudio.net/development/dragonboard-gpio-ports-erweitern-mit-mcp23017-esp-windows-10-iot-core/</link>
					<comments>https://medienstudio.net/development/dragonboard-gpio-ports-erweitern-mit-mcp23017-esp-windows-10-iot-core/#respond</comments>
		
		<dc:creator><![CDATA[Thomas]]></dc:creator>
		<pubDate>Thu, 16 Mar 2017 09:09:13 +0000</pubDate>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[DragonBoard]]></category>
		<category><![CDATA[MCP23017]]></category>
		<category><![CDATA[UWP]]></category>
		<category><![CDATA[Windows 10 IoT Core]]></category>
		<guid isPermaLink="false">https://medienstudio.net/?p=916</guid>

					<description><![CDATA[<p>Die letzten Tage habe ich mit dem Versuch verbracht,&#160;mithilfe eines MCP23017-E/SP-Chips einem DragonBoard 410c zusätzliche 16 GPIO-Ports hinzuzufügen und diese&#160;über Windows 10 IoT Core zu steuern. Eigentlich nicht so schwer, wenn man die &#8222;kleinen&#8220; Stolpersteine kennt. Wiring MCP23017-E/SP &#38; DragonBoard 410c Die Verkabelung von DragonBoard und Port Expander ist relativ simpel, vor allem wenn nur &#8230; <a href="https://medienstudio.net/development/dragonboard-gpio-ports-erweitern-mit-mcp23017-esp-windows-10-iot-core/" class="more-link"><span class="screen-reader-text">DragonBoard GPIO-ports erweitern mit MCP23017-ESP und Windows 10 IoT Core</span> weiterlesen</a></p>
<p>The post <a href="https://medienstudio.net/development/dragonboard-gpio-ports-erweitern-mit-mcp23017-esp-windows-10-iot-core/">DragonBoard GPIO-ports erweitern mit MCP23017-ESP und Windows 10 IoT Core</a> appeared first on <a href="https://medienstudio.net">medienstudio.net</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>Die letzten Tage habe ich mit dem Versuch verbracht,&nbsp;mithilfe eines MCP23017-E/SP-Chips einem <a href="https://developer.qualcomm.com/hardware/dragonboard-410c">DragonBoard 410c</a> zusätzliche 16 GPIO-Ports hinzuzufügen und diese&nbsp;über Windows 10 IoT Core zu steuern. Eigentlich nicht so schwer, wenn man die &#8222;kleinen&#8220; Stolpersteine kennt.</p>



<span id="more-916"></span>



<h2 class="wp-block-heading">Wiring MCP23017-E/SP &amp; DragonBoard 410c</h2>



<figure class="wp-block-image alignnone wp-image-897 size-full"><img loading="lazy" decoding="async" width="1000" height="446" src="https://medienstudio.net/wp-content/uploads/2017/03/DragonBoard-410c-MCP23017.jpg" alt="DragonBoard 410 &amp; MCP23107 Pin Mappings" class="wp-image-897" srcset="https://medienstudio.net/wp-content/uploads/2017/03/DragonBoard-410c-MCP23017.jpg 1000w, https://medienstudio.net/wp-content/uploads/2017/03/DragonBoard-410c-MCP23017-300x134.jpg 300w, https://medienstudio.net/wp-content/uploads/2017/03/DragonBoard-410c-MCP23017-768x343.jpg 768w, https://medienstudio.net/wp-content/uploads/2017/03/DragonBoard-410c-MCP23017-200x89.jpg 200w, https://medienstudio.net/wp-content/uploads/2017/03/DragonBoard-410c-MCP23017-400x178.jpg 400w, https://medienstudio.net/wp-content/uploads/2017/03/DragonBoard-410c-MCP23017-600x268.jpg 600w, https://medienstudio.net/wp-content/uploads/2017/03/DragonBoard-410c-MCP23017-800x357.jpg 800w" sizes="auto, (max-width: 1000px) 100vw, 1000px" /><figcaption>DragonBoard 410c Pin Mappings | <a href="https://developer.microsoft.com/en-us/windows/iot/docs/pinmappingsdb" target="_blank" rel="noopener noreferrer">mehr Infos</a></figcaption></figure>



<p>Die Verkabelung von DragonBoard und Port Expander ist relativ simpel, vor allem wenn nur ein einzelner&nbsp;MCP23017-Chip angeschlossen werden muss:</p>



<ul class="wp-block-list"><li>Verbinde&nbsp;<strong>SCL</strong> (12)&nbsp;Pin&nbsp;mit DragonBoard I2C0 <strong>SCL</strong> (15) Pin</li><li>Verbinde&nbsp;<strong>SDA</strong> (13) Pin&nbsp;mit DragonBoard <strong>SDA</strong> (17) Pin</li><li>Verbinde&nbsp;<strong>RESET&nbsp;</strong>(18)&nbsp;und <strong>VDD</strong> (9)&nbsp;mit DragonBoard <strong>1.8V PWR</strong> (35) Pin</li><li>Verbinde&nbsp;<strong>A0, A1, A2</strong>&nbsp;(15, 16, 17)&nbsp;und&nbsp;<strong>VSS</strong> (10)&nbsp;mit <strong>Ground / GND</strong> (1, 2, 39 or 40)</li></ul>



<p><img loading="lazy" decoding="async" class="alignnone wp-image-902 size-full" src="https://medienstudio.net/wp-content/uploads/2017/03/dragonboard-mcp23017_Wiring.png" alt="DragonBoard 410c &amp; MCP23017 Breadboard Wiring" width="1755" height="1263" srcset="https://medienstudio.net/wp-content/uploads/2017/03/dragonboard-mcp23017_Wiring.png 1755w, https://medienstudio.net/wp-content/uploads/2017/03/dragonboard-mcp23017_Wiring-300x216.png 300w, https://medienstudio.net/wp-content/uploads/2017/03/dragonboard-mcp23017_Wiring-768x553.png 768w, https://medienstudio.net/wp-content/uploads/2017/03/dragonboard-mcp23017_Wiring-1024x737.png 1024w, https://medienstudio.net/wp-content/uploads/2017/03/dragonboard-mcp23017_Wiring-200x144.png 200w, https://medienstudio.net/wp-content/uploads/2017/03/dragonboard-mcp23017_Wiring-400x288.png 400w, https://medienstudio.net/wp-content/uploads/2017/03/dragonboard-mcp23017_Wiring-600x432.png 600w, https://medienstudio.net/wp-content/uploads/2017/03/dragonboard-mcp23017_Wiring-800x576.png 800w, https://medienstudio.net/wp-content/uploads/2017/03/dragonboard-mcp23017_Wiring-1200x864.png 1200w" sizes="auto, (max-width: 1755px) 100vw, 1755px" /><br>Die <a href="http://amzn.to/2na8yrD">MCP23017 Chips von Amazon</a>&nbsp;sind an sich OK, die Sockel hielten allerdings nur schlecht auf meinem Breadboard, sodass ich auch keine Verbindung bekam.&nbsp;Bessere Sockel erkennt man an den runden Pins&nbsp;(<a href="https://www.amazon.de/sourcingmap-Pitch-%C3%9Cbergangsnippel-L%C3%B6tversion-Sockel/dp/B0079SJGVU/">wie diese z.B.</a>), mit denen dann auch die Verbindung stabil war.</p>



<h2 class="wp-block-heading">Verbindung mit Windows 10 IoT Core zu MCP23017-E/SP via I2C herstellen</h2>



<p>Der &#8222;<span class="nn">Windows.Devices.I2c&#8220;&nbsp;Namespace nimmt einem bereits viel Arbeit bei der Herstellung einer Verbindung via I2C ab. Wie das beispielsweise aussehen kann zeigt </span><span class="nn"><a href="https://developer.microsoft.com/en-us/windows/iot/docs/pinmappingsdb#i2c-sample">dieses Code-Beispiel von Microsoft</a>. </span>Allerdings ist es meist nicht ganz so einfach.</p>



<p>Fast alle Tutorials die man zu Windows 10 IoT Core und I2C-Kommunikation findet beziehen sich auf ein Setup mit Raspberry Pi-Geräten. Diese versorgen den Expander-Chip mit 3.3V, das DragonBoard kann allerdings nur mit 1.8V arbeiten. An sich kein Problem, da der MCP23017 prinzipiell mit 1.8V &#8211; 5.5V funktioniert.</p>



<h3 class="wp-block-heading">ABER</h3>



<p>Es ist nicht möglich, bei 1.8V mit <span class="n">I2cBusSpeed</span><span class="p">.</span><span class="n">FastMode (400kHz) zu arbeiten. Stattdessen muss hier<strong> I2CBusSpeed.StandardMode (100 kHz)</strong> verwendet werden,&nbsp;da man sonst den Fehler &#8222;<strong>Slave address was not acknowledged.</strong>&#8220; erhält, der alles und nichts sagt.</span></p>



<p>Wichtig ist auch, den Chip richtig zu adressieren. Das r/w-bit wird am Ende weggelassen, sodass man eine die 7-bit Adresse 0x20 erhält (sofern die Adress-Ports a0, a1 &amp;&nbsp;a2 geerdet sind).</p>



<h2 class="wp-block-heading">Mit MCP23017 produktiv arbeiten</h2>



<p>Bei den Recherchen zum MCP23017-Chip bin ich auf eine sehr praktische <a href="https://github.com/mphacker/RaspberryPiComponents/blob/master/RaspberryPiComponents/MCP23017.cs">Helper-Bibliothek für Raspberry Pi</a> von <a href="https://twitter.com/mphacker">Michael Hacker</a>&nbsp;gestoßen, die ich geringfügig&nbsp;<a href="https://github.com/ThomasPe/DragonBoardComponents/blob/master/DragonBoardComponents/MCP23017.cs">für das&nbsp;DragonBoard angepasst habe</a>. Diese nimmt einem&nbsp;nochmal einiges an Arbeit&nbsp;zu Initialisierung des Chips ab. Außerdem kann man&nbsp;so statt anstatt von Hex-Adressen mit schönen Konstanten und Enums arbeiten.</p>



<p>Beispielsweise lässt sich damit&nbsp;so in wenigen Zeilen eine LED am GPB0-Port&nbsp;steuern:</p>



<pre class="wp-block-preformatted">private MCP23017 i2cPortExpanderClient = new MCP23017("I2C0");
await i2cPortExpanderClient.Init();
i2cPortExpanderClient.SetDriveMode(MCP23017.Pin.GPB0, MCP23017.PinMode.Ouput);
i2cPortExpanderClient.Write(MCP23017.Pin.GPB0, MCP23017.PinValue.High);
i2cPortExpanderClient.Write(MCP23017.Pin.GPB0, MCP23017.PinValue.Low);</pre>
<p>The post <a href="https://medienstudio.net/development/dragonboard-gpio-ports-erweitern-mit-mcp23017-esp-windows-10-iot-core/">DragonBoard GPIO-ports erweitern mit MCP23017-ESP und Windows 10 IoT Core</a> appeared first on <a href="https://medienstudio.net">medienstudio.net</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://medienstudio.net/development/dragonboard-gpio-ports-erweitern-mit-mcp23017-esp-windows-10-iot-core/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">916</post-id>	</item>
		<item>
		<title>Webseiten scrapen mit Azure WebJobs, Node.js, jQuery, Async.js &#038; gizp</title>
		<link>https://medienstudio.net/development/webseiten-scrapen-crawlen-azure-webjobs-nodejs-gzip-async/</link>
					<comments>https://medienstudio.net/development/webseiten-scrapen-crawlen-azure-webjobs-nodejs-gzip-async/#respond</comments>
		
		<dc:creator><![CDATA[Thomas]]></dc:creator>
		<pubDate>Sat, 30 Apr 2016 21:35:44 +0000</pubDate>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[Async.js]]></category>
		<category><![CDATA[Azure WebJobs]]></category>
		<category><![CDATA[cheerio]]></category>
		<category><![CDATA[gizp]]></category>
		<category><![CDATA[jQuery]]></category>
		<category><![CDATA[Node.js]]></category>
		<guid isPermaLink="false">https://medienstudio.net/?p=539</guid>

					<description><![CDATA[<p>In diesem Artikel zeige ich, wie man relativ einfach eine Seite regelmäßig mit Azure WebJobs &#38; Node.js (jQuery bzw. cheerio) crawlen und die Daten als JSON ausgeben kann. Im konkreten Beispiel geht es darum, wöchentlich aus dem Windows Store die aktuellen &#8222;Red Stripe Deals&#8220;-Angebote auszulesen und die Informationen als JSON-Datei auf dem Server abzulegen, wo diese &#8230; <a href="https://medienstudio.net/development/webseiten-scrapen-crawlen-azure-webjobs-nodejs-gzip-async/" class="more-link"><span class="screen-reader-text">Webseiten scrapen mit Azure WebJobs, Node.js, jQuery, Async.js &#038; gizp</span> weiterlesen</a></p>
<p>The post <a href="https://medienstudio.net/development/webseiten-scrapen-crawlen-azure-webjobs-nodejs-gzip-async/">Webseiten scrapen mit Azure WebJobs, Node.js, jQuery, Async.js &#038; gizp</a> appeared first on <a href="https://medienstudio.net">medienstudio.net</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>In diesem Artikel zeige ich, wie man relativ einfach eine Seite regelmäßig mit Azure WebJobs &amp; Node.js (jQuery bzw. cheerio) crawlen und die Daten als JSON ausgeben kann.</p>
<p>Im konkreten Beispiel geht es darum, wöchentlich aus dem Windows Store die aktuellen &#8222;Red Stripe Deals&#8220;-Angebote auszulesen und die Informationen als JSON-Datei auf dem Server abzulegen, wo diese z.B. von einer App weiterverwendet werden können.</p>
<p><span id="more-539"></span></p>
<h2>Verantwortungsbewusst Crawlen &amp; Scrapen</h2>
<p>Ich möchte an dieser Stelle keine große Ethik-Diskussion über das scrapen von Daten vom Zaun brechen. Dazu gibt es im Web schon mehr als genug Diskussionen und Meinungen. Allerdings möchte ich anmerken, dass man selbst nur davon profitiert, wenn man nicht rücksichtslos vorgeht. Gerade wenn mehrere, unter Umständen tausende Seiten aufgerufen werden sollen, ist es durchaus im eigenen Interesse, den Server nicht mit zu vielen gleichzeitigen Requests in die Knie zu zwingen. Zudem möchte man den Seitenbetreiber ja nicht dazu ermuntern, Gegenmaßnahmen zu ergreifen.</p>
<p>Aus diesem Grund verwenden wir Tools wie <a href="https://github.com/caolan/async">Async.js</a> und die in Node.js enthaltene Library zlib, um möglichst ressourcenschonend an die Daten zu kommen.</p>
<h2>Verwendete Tools</h2>
<ul>
<li><a href="https://azure.microsoft.com/en-us/">Windows Azure Account</a></li>
<li><a href="https://nodejs.org/en/">NodeJS</a></li>
<li><a href="https://www.microsoft.com/web/webmatrix/">Microsoft Webmatrix</a></li>
</ul>
<h2>Vorbereitung</h2>
<p>Zunächst benötigen wir eine neue Web App, unter der wir den WebJob ausführen können. Hierfür reicht erst mal auch die kostenlose <strong>F1 Free</strong>-Instanz des App Service Plans.</p>
<p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-545" src="https://medienstudio.net/wp-content/uploads/2016/04/azure-new-web-app.jpg" alt="azure new web app" width="888" height="697" srcset="https://medienstudio.net/wp-content/uploads/2016/04/azure-new-web-app.jpg 888w, https://medienstudio.net/wp-content/uploads/2016/04/azure-new-web-app-300x235.jpg 300w, https://medienstudio.net/wp-content/uploads/2016/04/azure-new-web-app-768x603.jpg 768w, https://medienstudio.net/wp-content/uploads/2016/04/azure-new-web-app-200x157.jpg 200w, https://medienstudio.net/wp-content/uploads/2016/04/azure-new-web-app-400x314.jpg 400w, https://medienstudio.net/wp-content/uploads/2016/04/azure-new-web-app-600x471.jpg 600w, https://medienstudio.net/wp-content/uploads/2016/04/azure-new-web-app-800x628.jpg 800w" sizes="auto, (max-width: 888px) 100vw, 888px" /></p>
<h2>Projekt anlegen</h2>
<p>Um ein neues Projekt anzulegen erstellen wir einen neuen Ordner, welcher am Ende alle benötigten Files enthalten wird und den wir dann einfach als .zip auf Azure hochladen können.</p>
<p>In dem noch leeren Ordner öffnen wir die cmd (im Ordner <strong>Shift + Rechtsklick -&gt; Eingabeaufforderung hier öffnen</strong>) und beginnen damit, die benötigten Node.js-Pakete herunterzuladen.</p>
<pre>npm install request</pre>
<p><a href="https://www.npmjs.com/package/request">request</a> ist ein HTTP request client, über den wir die benötigten Seitenaufrufe ausführen können.</p>
<pre>npm install cheerio</pre>
<p><a href="https://github.com/cheeriojs/cheerio">cheerio</a> ist quasi eine für Node.js abgespeckte Variante von jQuery, die es ermöglicht, Informationen mit gewohnt simplen Selektoren aus dem DOM-Baum zu extrahieren.</p>
<pre>npm install async</pre>
<p><a href="https://github.com/caolan/async">async</a> ermöglicht es uns, Warteschlangen für unseren Crawler anzulegen und so die HTTP-Anfragen zu drosseln.</p>
<p>Neben diesen externen Paketen benötigen wir noch zwei bereits in Node.js enthaltene Module:</p>
<p>Mit <a href="https://github.com/caolan/async">zlib</a> können wir die Webseiten gzip-komprimiert aufrufen und entpacken, was uns vor allem Traffic und Zeit spart.<br />
Mit <a href="http://www.tutorialspoint.com/nodejs/nodejs_file_system.htm">fs</a> greifen wir am Ende auf das Dateisystem zu und speichern unsere Daten als .json-Dokument.</p>
<p>In unserem Stammverzeichnis legen wir nun eine leere<em> app.js</em> Datei an, in der wir all diese Module laden:</p>
<pre title="Node.js Module" class="lang:js decode:true ">var request = require('request');
var cheerio = require('cheerio');
var fs = require('fs');
var async = require('async');
var zlib = require('zlib');
</pre>
<p>Anschließend brauchen wir noch ein paar Konfigurations-Variablen sowie ein output-Array, in welches wir die erhaltenen Apps pushen.</p>
<pre class="lang:js decode:true">var output = [fusion_builder_container hundred_percent="yes" overflow="visible"][fusion_builder_row][fusion_builder_column type="1_1" background_position="left top" background_color="" border_size="" border_color="" border_style="solid" spacing="yes" background_image="" background_repeat="no-repeat" padding="" margin_top="0px" margin_bottom="0px" class="" id="" animation_type="" animation_speed="0.3" animation_direction="left" hide_on_mobile="no" center_content="no" min_height="none"][];
var dealsUri = "https://www.microsoft.com/en-us/store/collections/redstripedeals/pc";
var baseUri = "http://microsoft.com";
var output = [];
var outputFilename = "redstripedeals.json";
//var outputFilename = "d:\\home\\site\\wwwroot\\redstripedeals2.json";</pre>
<p><em>dealsUri</em> ist die Einstiegsseite, über die wir die Links zu den Apps erhalten.<br />
<em>baseUri</em> benötigen wir, um aus den relativen Links absolute URLs bauen zu können.<br />
Über <em>outputFilename</em> geben wir an, wo unsere Deals am Ende gespeichert werden. Für den lokalen Test soll die Datei einfach im Stammverzeichnis abgelegt werden. In Azure wird das Script allerdings <a href="http://stackoverflow.com/questions/21769095/uri-of-azure-web-jobs-output-file-with-node-js">in einem temporären Verzeichnis ausgeführt</a>, so dass hierfür ein absoluter Pfad angegeben werden sollte.</p>
<h2>Der Crawler</h2>
<p>Das Script soll folgendermaßen vorgehen:</p>
<ol>
<li>Die Red-Stripe-Deals-Seite in gzip-Form mit den Links zu allen Apps herunterladen
<pre title="Loading and de-zipping Deals-Page " class="lang:js decode:true ">var loadDeals = function () {
    var options = { 
        url: dealsUri,
        port: 443,
        headers: {
            'User-Agent': 'Mozilla/5.0 (Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko',
            'Accept-Language': 'en-us',
            'Content-Language': 'en-us',
			'Accept-Encoding': 'gzip'
        },
        timeout: 0,
        encoding: null
    };
    request(options, function (err, resp, body) {
        if (err) {
            console.log("error loading page");
        } 
        if (!err) {
            if (resp.headers['content-encoding'] == 'gzip') {
                zlib.gunzip(body, function (err, dezipped) {
                    redstripedealsParser(dezipped.toString());
                });
            } else {
                redstripedealsParser(body);
            }
        }
    });
}
</pre>
</li>
<li>Die Links mit cheerio (jQuery) extrahieren und in die Warteschlange / Queue <em>qDetails</em> einfügen
<pre title="Loading page into cheerio and extracting all app links" class="lang:js decode:true">var redstripedealsParser = function (body, lang)
{
    $ = cheerio.load(body);
    $('figure h4 a').each(function ()
    {
        var appUri = baseUri + $(this).attr("href");
        qDetails.push(appUri);
    });
}</pre>
</li>
<li>Die Warteschlange einzeln abfragen und die App-Details-Seiten entpackt and den Parser weitergeben.<br />
Hier lässt sich über den letzten Parameter von async.queue() die Anzahl der parallel laufenden Tasks anpassen (in diesem Fall 1).</p>
<pre title="Downloading and de-zipping the app details pages" class="lang:js decode:true ">var qDetails = async.queue(function (task, callback) {
    console.log("get details: " + task);
	var options = {
		url: task,
		headers: {
			'User-Agent': 'Mozilla/5.0 (Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko',
			'Accept-Language': 'en-us',
			'Content-Language': 'en-us',
			'Accept-Encoding': 'gzip'
		},
		timeout: 0,
        encoding: null
	};
	request(options, function (err, resp, body) {
        if (err)
        {
            console.log("error: " + task);
            throw err;
        }
		if(resp.headers['content-encoding'] == 'gzip'){
			zlib.gunzip(body, function(err, dezipped) {
				GetDetails(dezipped.toString(), task, callback);
			});
		} else {
			GetDetails(body, task, callback);
		}
	}); 
}, 1);
</pre>
</li>
<li>App-Details mit cheerio extrahieren und in das <em>output</em>-Array schreiben<br />
Am Ende nicht den callback() für die async-Queue vergessen.</p>
<pre title="Extract app details using cheerio" class="lang:js decode:true ">var GetDetails = function (body, task, callback) {
    console.log("parse details: " + task);
    $ = cheerio.load(body);

    var app = {};
    
    app["title"] = $('#page-title').text();
    app["image"] = "http:" + $('.ph-logo &gt; img').first().attr('src').toString();
    app["description"] = $('.showmore  &gt; p').first().text();
    app["price"] = $('.srv_price &gt; span').first().text();
    app["ratingValue"] = $('.srv_ratingsScore.win-rating-average').first().text();
    app["ratingCount"] = $('.win-rating-total').first().text().replace(/\D/g, '');
    app["packageSize"] = $('.metadata-list-content &gt; div').eq(2).text().replace("\r\n", "").trim();
    app["publisher"] = $('.metadata-list-content &gt; div').eq(0).text().replace("\r\n", "").trim();

    output.push(app);
    callback();
}</pre>
</li>
<li>Wenn die Warteschlange leer ist, das <em>output</em>-Array als JSON-Dokument auf der Festplatte ablegen
<pre title="Saving deals to json when queue is empty" class="lang:js decode:true ">qDetails.drain = function ()
{
	fs.writeFile(outputFilename, JSON.stringify(output, null), function (err)
	{
		if (err)
		{
			console.log(err);
		} else
		{
			console.log("JSON saved to " + outputFilename);
		}
	});
}</pre>
</li>
<li>Script starten
<pre title="Start script" class="lang:js decode:true ">loadDeals();</pre>
</li>
</ol>
<p>Das gesamte Script findet ihr hier nochmal als <a href="https://gist.github.com/ThomasPe/a6df2e4135ba1815b30c49a9495755f0">Github Gist</a>.<br />
<script src="https://gist.github.com/ThomasPe/a6df2e4135ba1815b30c49a9495755f0.js"></script><br />
Das Script lässt sich über die Kommandozeile mit folgenden Befehl ausführen:</p>
<pre>node app.js</pre>
<h2>Script veröffentlichen und Zeitplan einrichten</h2>
<p>Sofern das Script lokal fehlerfrei durchläuft, kommentieren wir den output-Pfad für Azure ein (und den lokalen aus) und packen den ganzen Ordner anschließend als normale .zip-Datei.</p>
<p>Um einen WebJob &#8222;gemäß einem Zeitplan&#8220; ausführen zu können, müssen wir uns jetzt <a href="https://manage.windowsazure.com">im alten Azure Portal</a> anmelden. Dort können wir nun in der zuvor angelegten Web App unter <strong>Webaufträge </strong>einen neuen WebJob hinzufügen. Hierzu wird lediglich ein Name und die .zip-Datei benötigt. Außerdem wird die Region abgefragt, in welcher der Job ausgeführt werden soll.</p>
<p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-563" src="https://medienstudio.net/wp-content/uploads/2016/04/webjobs-schedule-medienstudio.net_..jpg" alt="webjobs-schedule-medienstudio.net." width="1414" height="817" srcset="https://medienstudio.net/wp-content/uploads/2016/04/webjobs-schedule-medienstudio.net_..jpg 1414w, https://medienstudio.net/wp-content/uploads/2016/04/webjobs-schedule-medienstudio.net_.-300x173.jpg 300w, https://medienstudio.net/wp-content/uploads/2016/04/webjobs-schedule-medienstudio.net_.-768x444.jpg 768w, https://medienstudio.net/wp-content/uploads/2016/04/webjobs-schedule-medienstudio.net_.-1024x592.jpg 1024w, https://medienstudio.net/wp-content/uploads/2016/04/webjobs-schedule-medienstudio.net_.-200x116.jpg 200w, https://medienstudio.net/wp-content/uploads/2016/04/webjobs-schedule-medienstudio.net_.-400x231.jpg 400w, https://medienstudio.net/wp-content/uploads/2016/04/webjobs-schedule-medienstudio.net_.-600x347.jpg 600w, https://medienstudio.net/wp-content/uploads/2016/04/webjobs-schedule-medienstudio.net_.-800x462.jpg 800w, https://medienstudio.net/wp-content/uploads/2016/04/webjobs-schedule-medienstudio.net_.-1200x693.jpg 1200w" sizes="auto, (max-width: 1414px) 100vw, 1414px" /></p>
<p>Im zweiten Schritt lässt sich dann planen, wann genau das Script ausgeführt werden soll. Nachdem sich in unserem Fall die Red Stripe Deals nur einmal die Woche ändern, muss der WebJob auch nicht öfters laufen.</p>
<p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-562" src="https://medienstudio.net/wp-content/uploads/2016/04/webjobs-schedule-2-medienstudio.net_.jpg" alt="webjobs-schedule-2-medienstudio.net" width="1414" height="817" srcset="https://medienstudio.net/wp-content/uploads/2016/04/webjobs-schedule-2-medienstudio.net_.jpg 1414w, https://medienstudio.net/wp-content/uploads/2016/04/webjobs-schedule-2-medienstudio.net_-300x173.jpg 300w, https://medienstudio.net/wp-content/uploads/2016/04/webjobs-schedule-2-medienstudio.net_-768x444.jpg 768w, https://medienstudio.net/wp-content/uploads/2016/04/webjobs-schedule-2-medienstudio.net_-1024x592.jpg 1024w, https://medienstudio.net/wp-content/uploads/2016/04/webjobs-schedule-2-medienstudio.net_-200x116.jpg 200w, https://medienstudio.net/wp-content/uploads/2016/04/webjobs-schedule-2-medienstudio.net_-400x231.jpg 400w, https://medienstudio.net/wp-content/uploads/2016/04/webjobs-schedule-2-medienstudio.net_-600x347.jpg 600w, https://medienstudio.net/wp-content/uploads/2016/04/webjobs-schedule-2-medienstudio.net_-800x462.jpg 800w, https://medienstudio.net/wp-content/uploads/2016/04/webjobs-schedule-2-medienstudio.net_-1200x693.jpg 1200w" sizes="auto, (max-width: 1414px) 100vw, 1414px" /></p>
<p>Sobald der Job eingerichtet wurde, können wir das Script zum ersten Mal ausführen. Wenn wir alles richtig gemacht haben, sollten wir nach kurzer Wartezeit ein &#8222;Success&#8220; unter &#8222;Ergebnis der letzten Ausführung&#8220; sehen.</p>
<h2>WebJob Script nachträglich anpassen</h2>
<p>Wenn wir nachträglich noch Änderungen am WebJobs Script vornehmen wollen, ist die vermutlich einfachste Variante, dies direkt via Webmatrix auf dem Server zu tun &#8211; natürlich NIE bei produktiven Systemen! <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f609.png" alt="😉" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Meldet man sich im Editor mit seinem Microsoft Account an, lässt sich die Web-App samt WebJobs direkt remote öffnen und editieren.</p>
<p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-569" src="https://medienstudio.net/wp-content/uploads/2016/04/webmatrix-webjobs-medienstudio.net_.jpg" alt="webmatrix-webjobs-medienstudio.net" width="1250" height="847" srcset="https://medienstudio.net/wp-content/uploads/2016/04/webmatrix-webjobs-medienstudio.net_.jpg 1250w, https://medienstudio.net/wp-content/uploads/2016/04/webmatrix-webjobs-medienstudio.net_-300x203.jpg 300w, https://medienstudio.net/wp-content/uploads/2016/04/webmatrix-webjobs-medienstudio.net_-768x520.jpg 768w, https://medienstudio.net/wp-content/uploads/2016/04/webmatrix-webjobs-medienstudio.net_-1024x694.jpg 1024w, https://medienstudio.net/wp-content/uploads/2016/04/webmatrix-webjobs-medienstudio.net_-200x136.jpg 200w, https://medienstudio.net/wp-content/uploads/2016/04/webmatrix-webjobs-medienstudio.net_-400x271.jpg 400w, https://medienstudio.net/wp-content/uploads/2016/04/webmatrix-webjobs-medienstudio.net_-600x407.jpg 600w, https://medienstudio.net/wp-content/uploads/2016/04/webmatrix-webjobs-medienstudio.net_-800x542.jpg 800w, https://medienstudio.net/wp-content/uploads/2016/04/webmatrix-webjobs-medienstudio.net_-1200x813.jpg 1200w" sizes="auto, (max-width: 1250px) 100vw, 1250px" /></p>
<h2>JSON abrufen</h2>
<p>Zu guter Letzt möchte man natürlich auch noch die erstellte JSON-Datei mit all den Deals in irgendeiner Weise (z.B. in einer mobilen App) weiterverarbeiten. Hier gibt es allerdings einen kleinen Stolperstein, vor dem man gewarnt sein sollte. Denn von Haus aus liefern Azure Web-Anwendungen kein statischen JSON-Dateien aus. Dies ist allerdings leicht behoben, indem man folgenden Code innerhalb der Web.config im Root-Verzeichnis ablegt:</p>
<pre class="lang:default decode:true ">&lt;?xml version="1.0"?&gt;
 
&lt;configuration&gt;
    &lt;system.webServer&gt;
        &lt;staticContent&gt;
            &lt;mimeMap fileExtension=".json" mimeType="application/json" /&gt;
     &lt;/staticContent&gt;
    &lt;/system.webServer&gt;
&lt;/configuration&gt;</pre>
<p>via <a href="https://blogs.msdn.microsoft.com/africaapps/2013/06/07/how-to-serve-static-json-files-from-a-windows-azure-website/">Microsoft Developer</a></p>
<p>Es mag auf den ersten Blick etwas viel Aufwand sein, letztendlich ist das Ganze allerdings gar nicht so tragisch. Fragen und Anmerkungen zur Anleitung und zum Code gern in den Kommentaren. <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f609.png" alt="😉" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<p>&nbsp;[/fusion_builder_column][/fusion_builder_row][/fusion_builder_container]</p>
<p>The post <a href="https://medienstudio.net/development/webseiten-scrapen-crawlen-azure-webjobs-nodejs-gzip-async/">Webseiten scrapen mit Azure WebJobs, Node.js, jQuery, Async.js &#038; gizp</a> appeared first on <a href="https://medienstudio.net">medienstudio.net</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://medienstudio.net/development/webseiten-scrapen-crawlen-azure-webjobs-nodejs-gzip-async/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">539</post-id>	</item>
		<item>
		<title>WordPress &#038; Cloudflare Flexible SSL: ERR_TOO_MANY_REDIRECTS Fehler beheben</title>
		<link>https://medienstudio.net/wordpress/wordpress-cloudflare-flexible-ssl-err_too_many_redirects-fehler-beheben/</link>
					<comments>https://medienstudio.net/wordpress/wordpress-cloudflare-flexible-ssl-err_too_many_redirects-fehler-beheben/#respond</comments>
		
		<dc:creator><![CDATA[Thomas]]></dc:creator>
		<pubDate>Mon, 11 Apr 2016 13:49:45 +0000</pubDate>
				<category><![CDATA[WordPress]]></category>
		<category><![CDATA[cloudflare]]></category>
		<category><![CDATA[ERR_TOO_MANY_REDIRECTS]]></category>
		<category><![CDATA[flexible ssl]]></category>
		<category><![CDATA[hosteurope]]></category>
		<category><![CDATA[https]]></category>
		<guid isPermaLink="false">https://medienstudio.net/?p=522</guid>

					<description><![CDATA[<p>Mein alter Windows Phone Blog WP7App.de wurde nach meinem Wechsel zu WindowsArea.de kurzerhand zum Testkaninchen für verschiedene WordPress-Tools umfunktioniert. Gestern habe ich zum ersten Mal die kostenlose, &#8222;Flexible SSL&#8220; Verschlüsselung (HTTPS) via Cloudflare aktiviert. Abgesehen von ein paar &#8222;Mixed Content Fehlern&#8220; sah alles erst mal ganz gut aus. Allerdings konnte ich mich nicht mehr in &#8230; <a href="https://medienstudio.net/wordpress/wordpress-cloudflare-flexible-ssl-err_too_many_redirects-fehler-beheben/" class="more-link"><span class="screen-reader-text">WordPress &#038; Cloudflare Flexible SSL: ERR_TOO_MANY_REDIRECTS Fehler beheben</span> weiterlesen</a></p>
<p>The post <a href="https://medienstudio.net/wordpress/wordpress-cloudflare-flexible-ssl-err_too_many_redirects-fehler-beheben/">WordPress &#038; Cloudflare Flexible SSL: ERR_TOO_MANY_REDIRECTS Fehler beheben</a> appeared first on <a href="https://medienstudio.net">medienstudio.net</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>Mein alter Windows Phone Blog <a href="https://wp7app.de">WP7App.de</a> wurde nach meinem Wechsel zu <a href="http://windowsarea.de">WindowsArea.de</a> kurzerhand zum Testkaninchen für verschiedene WordPress-Tools umfunktioniert.<br />
Gestern habe ich zum ersten Mal die kostenlose, &#8222;Flexible SSL&#8220; Verschlüsselung (HTTPS) via <a href="https://www.cloudflare.com">Cloudflare</a> aktiviert. Abgesehen von ein paar &#8222;Mixed Content Fehlern&#8220; sah alles erst mal ganz gut aus. Allerdings konnte ich mich nicht mehr in den Admin-Bereich einloggen.</p>
<p><span id="more-522"></span></p>
<p>Alle Aufrufe auf <code>/wp-login.php</code>, <code>/wp-admin/</code> und <code>/wp-admin/index.php</code> lieferten nur einen <strong>Error 310: ERR_TOO_MANY_REDIRECTS</strong> Fehler zurück. Diverse versuche, dies mit diversen Hacks via wp-config.php, plugins und .htaccess zu lösen blieben anfangs vergeblich.</p>
<p>Nach knapp einem Tag bin ich dann hier auf die richtige Lösung gestoßen. Demnach müssen WordPress Seiten hinter Load Balancers oder Reverse Proxies aktiv die Verwendung von HTTPS in der $_SERVER[fusion_builder_container hundred_percent=&#8220;yes&#8220; overflow=&#8220;visible&#8220;][fusion_builder_row][fusion_builder_column type=&#8220;1_1&#8243; background_position=&#8220;left top&#8220; background_color=&#8220;&#8220; border_size=&#8220;&#8220; border_color=&#8220;&#8220; border_style=&#8220;solid&#8220; spacing=&#8220;yes&#8220; background_image=&#8220;&#8220; background_repeat=&#8220;no-repeat&#8220; padding=&#8220;&#8220; margin_top=&#8220;0px&#8220; margin_bottom=&#8220;0px&#8220; class=&#8220;&#8220; id=&#8220;&#8220; animation_type=&#8220;&#8220; animation_speed=&#8220;0.3&#8243; animation_direction=&#8220;left&#8220; hide_on_mobile=&#8220;no&#8220; center_content=&#8220;no&#8220; min_height=&#8220;none&#8220;][&#8218;HTTPS&#8216;] Variable setzen. Der bei mir funktionierende Code sieht dementsprechend wie folgt aus:</p>
<pre title="wp-config.php" class="lang:default decode:true ">if ( isset( $_SERVER['HTTP_X_FORWARDED_PROTO'] ) 
            &amp;&amp; 'https' == $_SERVER['HTTP_X_FORWARDED_PROTO'] ) 
{
    $_SERVER['HTTPS'] = 'on';
}</pre>
<p>&nbsp;</p>
<p>Dieser Schnipsel muss einfach in die wp-config.php (z.B. über der &#8222;/* That&#8217;s all, stop editing! Happy blogging. */&#8220; Zeile) eingefügt werden.[/fusion_builder_column][/fusion_builder_row][/fusion_builder_container]</p>
<p>The post <a href="https://medienstudio.net/wordpress/wordpress-cloudflare-flexible-ssl-err_too_many_redirects-fehler-beheben/">WordPress &#038; Cloudflare Flexible SSL: ERR_TOO_MANY_REDIRECTS Fehler beheben</a> appeared first on <a href="https://medienstudio.net">medienstudio.net</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://medienstudio.net/wordpress/wordpress-cloudflare-flexible-ssl-err_too_many_redirects-fehler-beheben/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">522</post-id>	</item>
	</channel>
</rss>
