<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Bemn</title>
    <description>The latest articles on Forem by Bemn (@bemnlam).</description>
    <link>https://forem.com/bemnlam</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F49345%2Fcad8df6b-57f9-4ed1-bc06-c678294e6978.JPG</url>
      <title>Forem: Bemn</title>
      <link>https://forem.com/bemnlam</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/bemnlam"/>
    <language>en</language>
    <item>
      <title>Fixing AjaxControlTookit Unexpected Page Reload in ASP.NET Web Form Application</title>
      <dc:creator>Bemn</dc:creator>
      <pubDate>Fri, 12 Feb 2021 04:44:24 +0000</pubDate>
      <link>https://forem.com/bemnlam/fixing-ajaxcontroltookit-unexpected-page-reload-in-asp-net-web-form-application-417j</link>
      <guid>https://forem.com/bemnlam/fixing-ajaxcontroltookit-unexpected-page-reload-in-asp-net-web-form-application-417j</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--oJHqgIqN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.eternalrecurrence.space/posts/fixing-ajaxcontroltoolkit-unexpected-page-reload-in-aspnet-webform-application/thumbnail.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--oJHqgIqN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.eternalrecurrence.space/posts/fixing-ajaxcontroltoolkit-unexpected-page-reload-in-aspnet-webform-application/thumbnail.jpg" alt="" width="880" height="585"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Table of Contents&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;😨 Problem&lt;/li&gt;
&lt;li&gt;
Expected vs Actual Behavior
&lt;ul&gt;
&lt;li&gt;Expected&lt;/li&gt;
&lt;li&gt;Actual&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
😀 Solution (Part 1): Updating &lt;code&gt;AjaxControlToolkit&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;Nuget Package Update&lt;/li&gt;
&lt;li&gt;Some Breaking Changes&lt;/li&gt;
&lt;li&gt;Different &lt;code&gt;__ doPostBack()&lt;/code&gt; in Firefox?&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
😀 Solution (Part 2): Adding &lt;code&gt;&amp;lt;asp:AsyncPostBackTrigger&amp;gt;&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;The Working Code Snippet&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Summary&lt;/li&gt;
&lt;/ul&gt;



&lt;h2&gt;
  
  
  😨 Problem
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;TL;DR: see Summary and the working code snippet if you want to try it right away.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In my company there is an old Web Form Website running .NET Framework 3.5. I migrated it to an ASP.NET Web Form Application running .NET Framework 4.7.2 few months ago. The website is running just like the old one but recently a user reported that:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;a &lt;code&gt;&amp;lt;select&amp;gt;&lt;/code&gt; list which should triggers an AJAX call and refresh part of the page does not work. Instead, the page is reloaded everytime when the item in the &lt;code&gt;&amp;lt;select&amp;gt;&lt;/code&gt; list is changed.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I used almost a day to figure out what’s (probably) happened and I hope that this article will be helpful to someone in the future who (still) encounter this problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  Expected vs Actual Behavior
&lt;/h2&gt;

&lt;p&gt;There is a &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt; containing a &lt;code&gt;&amp;lt;select&amp;gt;&lt;/code&gt; list (generated by &lt;a href="https://docs.microsoft.com/en-us/dotnet/api/system.web.ui.webcontrols.dropdownlist?view=netframework-4.8"&gt;&lt;code&gt;&amp;lt;asp:DropDownList&amp;gt;&lt;/code&gt;&lt;/a&gt;). When the selected option is changed, an AJAX call will be fired and new items will be fetched. Besides, there is a text area for user to input remarks. A submit button will submit the form.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--WPLIDF8l--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.eternalrecurrence.space/posts/fixing-ajaxcontroltoolkit-unexpected-page-reload-in-aspnet-webform-application/img/image-20210212150215986.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WPLIDF8l--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.eternalrecurrence.space/posts/fixing-ajaxcontroltoolkit-unexpected-page-reload-in-aspnet-webform-application/img/image-20210212150215986.png" alt="image-20210212150215986" width="681" height="511"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Expected
&lt;/h3&gt;

&lt;p&gt;The page will not be reloaded when the selected option is changed. The content in the text area retains.&lt;/p&gt;

&lt;h3&gt;
  
  
  Actual
&lt;/h3&gt;

&lt;p&gt;Once the selected option is changed, a form POST is fired (instead of an AJAX call). The content in the text area is therefore being cleaned.&lt;/p&gt;

&lt;p&gt;This is related to AJAX calls so the first thing I checked was how the AJAX call was initiated.&lt;/p&gt;

&lt;h2&gt;
  
  
  😀 Solution (Part 1): Updating &lt;code&gt;AjaxControlToolkit&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;In the &lt;code&gt;.aspx&lt;/code&gt; file I saw a &lt;code&gt;&amp;lt;asp:UpdatePanel&amp;gt;&lt;/code&gt; and a &lt;code&gt;&amp;lt;asp:DropDownList&amp;gt;&lt;/code&gt;. This is the simplified structure of that part:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;asp:UpdatePanel&lt;/span&gt; &lt;span class="na"&gt;runat=&lt;/span&gt;&lt;span class="s"&gt;"server"&lt;/span&gt; &lt;span class="na"&gt;ID=&lt;/span&gt;&lt;span class="s"&gt;"UpdatePanel2"&lt;/span&gt; &lt;span class="na"&gt;UpdateMode=&lt;/span&gt;&lt;span class="s"&gt;"Conditional"&lt;/span&gt; &lt;span class="na"&gt;ChildrenAsTriggers=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;ContentTemplate&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;asp:DropDownList&lt;/span&gt; &lt;span class="na"&gt;runat=&lt;/span&gt;&lt;span class="s"&gt;"server"&lt;/span&gt; &lt;span class="na"&gt;ID=&lt;/span&gt;&lt;span class="s"&gt;"ddItemTypes"&lt;/span&gt; &lt;span class="na"&gt;AutoPostBack=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt; &lt;span class="na"&gt;OnSelectedIndexChanged=&lt;/span&gt;&lt;span class="s"&gt;"ddItemTypes_OnSelectedIndexChanged"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/ContentTemplate&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/asp:UpdatePanel&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;AutoPostBack&lt;/code&gt; is there so the app should use &lt;code&gt;AjaxControlToolkit&lt;/code&gt; to make AJAX calls (I guess that’s the standard right?).&lt;/p&gt;

&lt;h3&gt;
  
  
  Nuget Package Update
&lt;/h3&gt;

&lt;p&gt;I checked the &lt;code&gt;package.config&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;package&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"AjaxControlToolkit"&lt;/span&gt; &lt;span class="na"&gt;version=&lt;/span&gt;&lt;span class="s"&gt;"4.1.50508"&lt;/span&gt; &lt;span class="na"&gt;targetFramework=&lt;/span&gt;&lt;span class="s"&gt;"net35"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Looks like the &lt;code&gt;targetFramework&lt;/code&gt; is not right. Maybe it’s time to update the package too.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;package&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"AjaxControlToolkit"&lt;/span&gt; &lt;span class="na"&gt;version=&lt;/span&gt;&lt;span class="s"&gt;"20.1.0"&lt;/span&gt; &lt;span class="na"&gt;targetFramework=&lt;/span&gt;&lt;span class="s"&gt;"net472"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Some Breaking Changes
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/DevExpress/AjaxControlToolkit"&gt;AjaxControlToolkit&lt;/a&gt; has some breaking changes since version 15+. In short, you need to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Uninstall the old version and install the new version of &lt;a href="https://www.nuget.org/packages/AjaxControlToolkit"&gt;&lt;code&gt;AjaxControlToolkit&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Change &lt;code&gt;&amp;lt;asp:ToolkitScriptManager&amp;gt;&lt;/code&gt; to &lt;code&gt;&amp;lt;asp:ScriptManager&amp;gt;&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Remove some unused configs in &lt;code&gt;web.config&lt;/code&gt; (see &lt;a href="https://github.com/DevExpress/AjaxControlToolkit/wiki/Upgrading-from-v7.x-and-below#3---clean-up-webconfig"&gt;this&lt;/a&gt;)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;[Optional] Install &lt;a href="https://www.nuget.org/packages/AjaxControlToolkit.HtmlEditor.Sanitizer/"&gt;&lt;code&gt;AjaxControlToolkit.HtmlEditor.Sanitizer&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Change the namespace &lt;code&gt;AjaxControlToolkit.HTMLEditor&lt;/code&gt; to &lt;code&gt;AjaxControlToolkit.HtmlEditor&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Change the namespace &lt;code&gt;AjaxControlToolkit.HTMLEditor.ToolbarButton&lt;/code&gt; to &lt;code&gt;AjaxControlToolkit.HtmlEditor.ToolbarButtons&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You may also read&lt;a href="https://github.com/DevExpress/AjaxControlToolkit/wiki/Upgrading-from-v7.x-and-below#3---clean-up-webconfig"&gt;the official complete migration guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;After I upgrade the Nuget package, the AJAX calls was working partially: It only works on Chromium-base browsers like Chrome and Edge. However, &lt;strong&gt;it does not work on Firefox (85.0) and Safari (14.0.1)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;😐 😐 😐&lt;/p&gt;

&lt;h3&gt;
  
  
  Different &lt;code&gt;__doPostBack()&lt;/code&gt; in Firefox?
&lt;/h3&gt;

&lt;p&gt;This is what I got from Firefox’s dev tool:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--TMri9267--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.eternalrecurrence.space/posts/fixing-ajaxcontroltoolkit-unexpected-page-reload-in-aspnet-webform-application/img/image-20210212140706748.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--TMri9267--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.eternalrecurrence.space/posts/fixing-ajaxcontroltoolkit-unexpected-page-reload-in-aspnet-webform-application/img/image-20210212140706748.png" alt="call stack form Firefox image-20210212140706748" width="679" height="381"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The initiator is from &lt;code&gt;__doPostBack()&lt;/code&gt; in the &lt;code&gt;.aspx&lt;/code&gt; page. In Chrome, it’s from &lt;code&gt;ScriptResource.axd&lt;/code&gt;. Below is the expected call stack: the call is an XHR request fired from &lt;code&gt;ScriptResource.axd&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4Mi4oB7C--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.eternalrecurrence.space/posts/fixing-ajaxcontroltoolkit-unexpected-page-reload-in-aspnet-webform-application/img/image-20210212141300293.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4Mi4oB7C--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blog.eternalrecurrence.space/posts/fixing-ajaxcontroltoolkit-unexpected-page-reload-in-aspnet-webform-application/img/image-20210212141300293.png" alt="Expected call stack image-20210212141300293" width="681" height="423"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Looks like the stack items from bottom to &lt;code&gt;_doPostBack()&lt;/code&gt; are the same. I therefore dug into &lt;code&gt;_doPostBack()&lt;/code&gt; and eventually found this piece of code in &lt;code&gt;ScriptResource.axd&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_postBackSettings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onsubmit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_onsubmit&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_originalDoPostBack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;eventTarget&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;eventArgument&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onsubmit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;if &lt;code&gt;this._postBackSettings.async&lt;/code&gt; is &lt;code&gt;false&lt;/code&gt;, &lt;code&gt;_originaldoPostBack()&lt;/code&gt; will be called and that is the &lt;code&gt;__doPostBack()&lt;/code&gt; function defined in the &lt;code&gt;.aspx&lt;/code&gt; page. &lt;strong&gt;This will trigger a page reload instead of an AJAX call&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Furthermore, I found that the &lt;code&gt;asyncTarget&lt;/code&gt; in &lt;code&gt;_postBackSettings&lt;/code&gt; is &lt;code&gt;null&lt;/code&gt; but in Chrome that target is the &lt;code&gt;&amp;lt;select&amp;gt;&lt;/code&gt; list.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In Firefox (v85.0) the javascript event initiated is somehow keep propagating to a higher level than the &lt;code&gt;&amp;lt;select&amp;gt;&lt;/code&gt; list (probably to the form’s level) and therefore being treated as a form POST instead of an AJAX call.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  😀 Solution (Part 2): Adding &lt;code&gt;&amp;lt;asp:AsyncPostBackTrigger&amp;gt;&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;I did some research related to the Firefox-specific page reload and seems none of them were talking about the issue I met. I tried to study the fundamentals of this &lt;strong&gt;PostBack&lt;/strong&gt; behaviour.&lt;/p&gt;

&lt;p&gt;Finally I got this article &lt;a href="https://www.aspsnippets.com/Articles/Avoid-Prevent-Page-refresh-PostBack-after-SelectedIndexChanged-is-fired-in-ASPNet-DropDownList.aspx"&gt;&lt;strong&gt;Avoid (Prevent) Page refresh (PostBack) after SelectedIndexChanged is fired in ASP.Net DropDownList&lt;/strong&gt;&lt;/a&gt;. It suggested what I might missing is an &lt;code&gt;asp:AsyncPostBackTrigger&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Also, &lt;a href="https://stackoverflow.com/questions/728043/how-to-stop-updatepanel-from-causing-whole-page-postback/728061#728061"&gt;this StackOverflow answer&lt;/a&gt; menitoned the &lt;code&gt;asp:AsyncPostBackTrigger&lt;/code&gt; thing.&lt;/p&gt;

&lt;p&gt;So my understanding is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;When using the &lt;code&gt;ScriptManager&lt;/code&gt; (i.e. the one we introduced when updating the &lt;code&gt;AjaxControlToolkit&lt;/code&gt;), we need to define an &lt;code&gt;asp:AsyncPostBackTrigger&lt;/code&gt; &lt;code&gt;Trigger&lt;/code&gt; in order to make the call AJAX.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Therefore, I added a &lt;code&gt;Trigger&lt;/code&gt; section to the &lt;code&gt;.aspx&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;Triggers&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;asp:AsyncPostBackTrigger&lt;/span&gt; &lt;span class="na"&gt;ControlID=&lt;/span&gt;&lt;span class="s"&gt;"ddItemTypes"&lt;/span&gt; &lt;span class="na"&gt;EventName=&lt;/span&gt;&lt;span class="s"&gt;"SelectedIndexChanged"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/Triggers&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This &lt;code&gt;Trigger&lt;/code&gt; section should be placed under the same &lt;code&gt;&amp;lt;asp:UpdatePanel&amp;gt;&lt;/code&gt; with the &lt;code&gt;&amp;lt;asp:DropDownList&amp;gt;&lt;/code&gt;. Here is the complete snippet:&lt;/p&gt;

&lt;h3&gt;
  
  
  The Working Code Snippet
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;asp:UpdatePanel&lt;/span&gt; &lt;span class="na"&gt;runat=&lt;/span&gt;&lt;span class="s"&gt;"server"&lt;/span&gt; &lt;span class="na"&gt;ID=&lt;/span&gt;&lt;span class="s"&gt;"UpdatePanel2"&lt;/span&gt; &lt;span class="na"&gt;UpdateMode=&lt;/span&gt;&lt;span class="s"&gt;"Conditional"&lt;/span&gt; &lt;span class="na"&gt;ChildrenAsTriggers=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;ContentTemplate&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;asp:DropDownList&lt;/span&gt; &lt;span class="na"&gt;runat=&lt;/span&gt;&lt;span class="s"&gt;"server"&lt;/span&gt; &lt;span class="na"&gt;ID=&lt;/span&gt;&lt;span class="s"&gt;"ddItemTypes"&lt;/span&gt; &lt;span class="na"&gt;AutoPostBack=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt; &lt;span class="na"&gt;OnSelectedIndexChanged=&lt;/span&gt;&lt;span class="s"&gt;"ddItemTypes_OnSelectedIndexChanged"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/ContentTemplate&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;Triggers&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;asp:AsyncPostBackTrigger&lt;/span&gt; &lt;span class="na"&gt;ControlID=&lt;/span&gt;&lt;span class="s"&gt;"ddItemTypes"&lt;/span&gt; &lt;span class="na"&gt;EventName=&lt;/span&gt;&lt;span class="s"&gt;"SelectedIndexChanged"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/Triggers&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/asp:UpdatePanel&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;EventName&lt;/code&gt; is optional here but since I just want this trigger to listen the &lt;code&gt;SelectedIndexChanged&lt;/code&gt; event, I add them all.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;code&gt;ControlID&lt;/code&gt; has to be an exact match with the &lt;code&gt;ID&lt;/code&gt; of &lt;code&gt;&amp;lt;asp:DropDownList&amp;gt;&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;After I added the &lt;code&gt;&amp;lt;asp:AsyncPostBackTrigger&amp;gt;&lt;/code&gt;, all the browsers including Chrome, Edge, Firefox (85.0) and Safari (14.0.1) are working.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;When your .NET 4.5+ ASP.NET Web Form Application using &lt;code&gt;AjaxControlToolkit&lt;/code&gt; does not work as expected and cause the controls like &lt;code&gt;&amp;lt;asp:DropDownList&amp;gt;&lt;/code&gt; cannot fire an AJAX call (but triggers a form POST), make sure that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;The &lt;code&gt;AjaxControlToolkit&lt;/code&gt; Nuget package is up to date (and did the proper migration steps &lt;a href="https://github.com/DevExpress/AjaxControlToolkit/wiki/Upgrading-from-v7.x-and-below#3---clean-up-webconfig"&gt;here&lt;/a&gt;).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;code&gt;&amp;lt;asp:UpdatePanel&amp;gt;&lt;/code&gt; section should contain a &lt;code&gt;&amp;lt;Triggers&amp;gt;&lt;/code&gt; section. In that section there is an &lt;code&gt;&amp;lt;asp:AsyncPostBackTrigger&amp;gt;&lt;/code&gt; with &lt;code&gt;ControlID&lt;/code&gt; same as the &lt;code&gt;ID&lt;/code&gt; of your control (which is also placed under the same UpdatePanel section).&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>webdev</category>
      <category>aspnet</category>
      <category>webform</category>
      <category>ajaxcontroltoolkit</category>
    </item>
    <item>
      <title>Set Up Cloudflare for AWS Route 53 Domains</title>
      <dc:creator>Bemn</dc:creator>
      <pubDate>Thu, 31 Dec 2020 09:46:38 +0000</pubDate>
      <link>https://forem.com/bemnlam/set-up-cloudflare-for-aws-route-53-domains-2ag1</link>
      <guid>https://forem.com/bemnlam/set-up-cloudflare-for-aws-route-53-domains-2ag1</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--R6ztPHHS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bemnlam.github.io/img/set-up-cloudflare-for-route53-domains/thumbnail.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--R6ztPHHS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bemnlam.github.io/img/set-up-cloudflare-for-route53-domains/thumbnail.jpg" alt="" width="880" height="462"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;This article shows how to configure Cloudflare for an existing website using AWS Route53.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Table of Contents
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ul&gt;
    &lt;li&gt;Overview&lt;/li&gt;
    &lt;li&gt;
Domain Registrar
      &lt;ul&gt;
        &lt;li&gt;What about AWS Route53?&lt;/li&gt;
      &lt;/ul&gt;
    &lt;/li&gt;
    &lt;li&gt;
Cloudflare Account
      &lt;ul&gt;
        &lt;li&gt;Add a domain&lt;/li&gt;
        &lt;li&gt;Change your nameservers&lt;/li&gt;
        &lt;li&gt;Verify nameservers&lt;/li&gt;
      &lt;/ul&gt;
    &lt;/li&gt;
    &lt;li&gt;
Updating DNS Records
      &lt;ul&gt;
        &lt;li&gt;(Application) Load Balancers&lt;/li&gt;
        &lt;li&gt;Amazon SES&lt;/li&gt;
      &lt;/ul&gt;
    &lt;/li&gt;
    &lt;li&gt;Summary&lt;/li&gt;
  &lt;/ul&gt;

&lt;h2&gt;
  
  
  Domain Registrar
&lt;/h2&gt;

&lt;p&gt;In my opinion, &lt;strong&gt;domain registrar is the most confusing part when you configure Cloudflare&lt;/strong&gt;. To check the details of your domain, use the &lt;a href="https://lookup.icann.org/lookup"&gt;&lt;strong&gt;Domain Name Registration Data Lookup&lt;/strong&gt;&lt;/a&gt;. Here is the lookup result of &lt;code&gt;gitlab.com&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ptKhvGP6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bemnlam.github.io/img/set-up-cloudflare-for-route53-domains/image-20201231180715529.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ptKhvGP6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bemnlam.github.io/img/set-up-cloudflare-for-route53-domains/image-20201231180715529.png" alt="image-20201231180715529" width="862" height="1316"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Registrar Information section contains the information where you should change the nameservers in the next step.&lt;/p&gt;

&lt;h3&gt;
  
  
  What about AWS Route53?
&lt;/h3&gt;

&lt;p&gt;Route53 can be a domain registrar OR just managing the hosted zone. If you see AWS is the Registrar in this lookup, then you should update the config in AWS Route53.&lt;/p&gt;

&lt;h5&gt;
  
  
  Example 1: Domain purchased from GoDaddy
&lt;/h5&gt;

&lt;p&gt;For example, if you purchased your domain in &lt;strong&gt;GoDaddy&lt;/strong&gt; and having a &lt;strong&gt;hosted zone&lt;/strong&gt; in &lt;strong&gt;AWS Route 53&lt;/strong&gt; (That’s my case), &lt;strong&gt;GoDaddy is therefore your domain registrar and you should login GoDaddy’s account to change the nameserver, NOT AWS Route 53&lt;/strong&gt;&lt;/p&gt;

&lt;h5&gt;
  
  
  Example 2: Domain purchased from AWS Route 53
&lt;/h5&gt;

&lt;p&gt;Unfortunately, You can purchased a domain in AWS Route 53 too. In this case, &lt;strong&gt;AWS Route 53 is your domain registrar&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You should change the nameservers in &lt;a href="https://console.aws.amazon.com/route53/"&gt;the Route 53 console&lt;/a&gt; &amp;gt; &lt;strong&gt;Registered Domains&lt;/strong&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  What about NS Record?
&lt;/h4&gt;

&lt;p&gt;Changing a &lt;code&gt;NS Record&lt;/code&gt; is not the same as changing a &lt;code&gt;nameserver&lt;/code&gt;. From the article &lt;a href="https://www.cloudflare.com/learning/dns/dns-records/dns-ns-record/"&gt;DNS NS record&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The NS record indicates which DNS server is authoritative.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So, a &lt;code&gt;NS Record&lt;/code&gt; is similar to &lt;code&gt;MX Record&lt;/code&gt;, &lt;code&gt;CNAME Record&lt;/code&gt;, &lt;code&gt;A Record&lt;/code&gt;, etc. They are all the items under the domain.&lt;/p&gt;

&lt;p&gt;Nameserver of the domain is at a higher level and it’s controled by the domain registrar.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cloudflare Account
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Add a domain
&lt;/h3&gt;

&lt;p&gt;It’s free to create a Cloudflare account: &lt;a href="https://dash.cloudflare.com/sign-up"&gt;https://dash.cloudflare.com/sign-up&lt;/a&gt;. Cloudflare also offer paid plans and I won’t discuss them in this article.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--pdCLjoFb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bemnlam.github.io/img/set-up-cloudflare-for-route53-domains/image-20201231175523687.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--pdCLjoFb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bemnlam.github.io/img/set-up-cloudflare-for-route53-domains/image-20201231175523687.png" alt="image-20201231175523687" width="706" height="357"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5I-PDlmF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bemnlam.github.io/img/set-up-cloudflare-for-route53-domains/image-20201231175540349.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5I-PDlmF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bemnlam.github.io/img/set-up-cloudflare-for-route53-domains/image-20201231175540349.png" alt="image-20201231175540349" width="491" height="300"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Choose the free plan and then click &lt;strong&gt;Continue&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;After that, Cloudflare will do a DNS scan for the domain you’ve entered. Click &lt;strong&gt;Continue&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Not all the recoreds captured are correct. You will need to add/delete/update some of them later.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Change your nameservers
&lt;/h3&gt;

&lt;p&gt;The last step of setup is changing the nameservers. You will see the following instruction:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zzUBLXXN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bemnlam.github.io/img/set-up-cloudflare-for-route53-domains/image-20201231182416379.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zzUBLXXN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bemnlam.github.io/img/set-up-cloudflare-for-route53-domains/image-20201231182416379.png" alt="image-20201231182416379" width="551" height="1005"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  1. Log in to your registrar account
&lt;/h4&gt;

&lt;p&gt;In the previous section, you should know which service provider is your domain registrar. Login to your domain registrar and find out the nameserver config. Then, remove the existing nameservers (the entries shown in step 1). For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ns1.mydomain.com
ns2.mydomain.com
ns3.mydomain.com
ns4.mydomain.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  2. Replace with Cloudflare’s nameservers:
&lt;/h4&gt;

&lt;p&gt;When all the existing nameservers are removed, you have to add the Cloudflare nameservers in the domain registrar. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;autumn.ns.cloudflare.com
hans.ns.cloudflare.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note: you have to enter the 2 nameservers shown in step 2. Other (Cloudflare) nameservers won’t work.&lt;/p&gt;

&lt;h3&gt;
  
  
  Verify nameservers
&lt;/h3&gt;

&lt;p&gt;Again, lookup the domain details using &lt;a href="https://lookup.icann.org/lookup"&gt;&lt;strong&gt;Domain Name Registration Data Lookup&lt;/strong&gt;&lt;/a&gt;. This time you should check the details in the &lt;strong&gt;Domain Information&lt;/strong&gt; section.&lt;/p&gt;

&lt;p&gt;You should see the nameservers are replaced with the Cloudflare’s nameservers.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt;  &lt;strong&gt;Changing nameservers is quick&lt;/strong&gt; and you should be able to see the updated records within 5 minutes.&lt;/p&gt;

&lt;p&gt;On the other hand, changing the &lt;strong&gt;records&lt;/strong&gt; under this domain may take a longer time (e.g. 24-48 hours).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do not mix them up&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You may click the &lt;strong&gt;Done, check nameservers&lt;/strong&gt; button once you finished changing the nameservers in your domain registrar.&lt;/p&gt;

&lt;h2&gt;
  
  
  Updating DNS Records
&lt;/h2&gt;

&lt;p&gt;As mentioned in the above, we have to change some of the records in Cloudflare.&lt;/p&gt;

&lt;h3&gt;
  
  
  (Application) Load Balancers
&lt;/h3&gt;

&lt;p&gt;You need to replace those A Records into CNAME Records.&lt;/p&gt;

&lt;p&gt;In AWS Route 53 the load balancer records are in the forms of alias. Cloudflare will fetch the A Records of the load balancers.&lt;/p&gt;

&lt;p&gt;Get the CNAME of your load balancer under &lt;strong&gt;AWS EC2 Console&lt;/strong&gt; &amp;gt; &lt;strong&gt;Load balancers&lt;/strong&gt; &amp;gt; Select your load balancer and see the details. After that, delete the A Record in Cloudflare and add a new CNAME record for your load balancer.&lt;/p&gt;

&lt;p&gt;I used to keep the A Record in Cloudflare and it does work for few days. After that, the IP of that load balancer changed and the website cannot be connected.&lt;/p&gt;

&lt;h3&gt;
  
  
  Amazon SES
&lt;/h3&gt;

&lt;p&gt;I also found that the SPF and DKIM records for Amazon SES does not created in Cloudflare too. In this case you need to create those records manually in Cloudflare. Just follow the instruction provided by Amazon then you will know how to do:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/ses/latest/DeveloperGuide/send-email-authentication-spf.html"&gt;Authenticating Email with SPF in Amazon SES&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/ses/latest/DeveloperGuide/send-email-authentication-dkim.html"&gt;Authenticating Email with DKIM in Amazon SES&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;To use &lt;strong&gt;Cloudflare&lt;/strong&gt; , you need to understand what a &lt;strong&gt;domain registrar&lt;/strong&gt; is. By changing &lt;strong&gt;nameservers&lt;/strong&gt; in a domain registrar and updating some records related to AWS’s infrastructure, we can set up the Cloudflare domain for an existing website using &lt;strong&gt;AWS Route 53&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This is the end of this article (as well as the year 2020).&lt;/em&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>route53</category>
      <category>cloudflare</category>
    </item>
    <item>
      <title>Documentation Makes Easy With MkDocs and GitLab Pages</title>
      <dc:creator>Bemn</dc:creator>
      <pubDate>Sat, 03 Oct 2020 14:41:00 +0000</pubDate>
      <link>https://forem.com/bemnlam/documentation-makes-easy-with-mkdocs-and-gitlab-pages-27e8</link>
      <guid>https://forem.com/bemnlam/documentation-makes-easy-with-mkdocs-and-gitlab-pages-27e8</guid>
      <description>&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;I created a static website using &lt;strong&gt;MkDocs&lt;/strong&gt; and deployed to &lt;a href="https://docs.gitlab.com/ee/user/project/pages/" rel="noopener noreferrer"&gt;&lt;strong&gt;GitLab Pages&lt;/strong&gt;&lt;/a&gt;. This is what I did: &lt;a href="https://bemn-proof-of-concept.gitlab.io/mkdoc/" rel="noopener noreferrer"&gt;https://bemn-proof-of-concept.gitlab.io/mkdoc/&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Table of Content
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
MkDocs

&lt;ul&gt;
&lt;li&gt;Preparation&lt;/li&gt;
&lt;li&gt;Up and Run&lt;/li&gt;
&lt;li&gt;Add a new page&lt;/li&gt;
&lt;li&gt;Theming&lt;/li&gt;
&lt;li&gt;Custom Resources&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

GitLab Pages

&lt;ul&gt;
&lt;li&gt;.gitlab-ci.yml&lt;/li&gt;
&lt;li&gt;Build and Deploy&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Conclustion&lt;/li&gt;

&lt;/ul&gt;




&lt;h2&gt;
  
  
  MkDocs
&lt;/h2&gt;

&lt;p&gt;From &lt;a href="https://www.mkdocs.org/" rel="noopener noreferrer"&gt;MkDocs’ official website&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;MkDocs is a &lt;strong&gt;fast&lt;/strong&gt; , &lt;strong&gt;simple&lt;/strong&gt; and &lt;strong&gt;downright gorgeous&lt;/strong&gt; static site generator that’s geared towards building project documentation. Documentation source files are written in Markdown, and configured with a single YAML configuration file.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In my opinion, setting up a static site using MkDocs is really straightforward. It’s easy to change the theme and insert custom styles/scripts. &lt;a href="https://en.m.wikipedia.org/wiki/KISS_principle" rel="noopener noreferrer"&gt;Keep it simple, stupid&lt;/a&gt;. That’s what I need.&lt;/p&gt;

&lt;h3&gt;
  
  
  Preparation
&lt;/h3&gt;

&lt;p&gt;Make sure that you have the following tools:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;python 3.x&lt;/strong&gt; and &lt;strong&gt;pip&lt;/strong&gt;. Install it via &lt;a href="https://www.python.org/downloads/" rel="noopener noreferrer"&gt;executable&lt;/a&gt; from the official website, or using tools like &lt;a href="https://brew.sh/" rel="noopener noreferrer"&gt;Homebrew&lt;/a&gt;.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;python &lt;span class="nt"&gt;--version&lt;/span&gt;
Python 3.8.2
&lt;span class="nv"&gt;$ &lt;/span&gt;pip &lt;span class="nt"&gt;--version&lt;/span&gt;
pip 20.0.2 from /usr/local/lib/python3.8/site-packages/pip &lt;span class="o"&gt;(&lt;/span&gt;python 3.8&lt;span class="o"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;a &lt;strong&gt;GitLab&lt;/strong&gt; account. You can use other service like &lt;a href="https://pages.github.com/" rel="noopener noreferrer"&gt;Github Pages&lt;/a&gt;, Amazon S3, etc.&lt;/li&gt;
&lt;li&gt;(optional) &lt;a href="https://www.markdownguide.org/cheat-sheet/" rel="noopener noreferrer"&gt;a markdown cheatsheet&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;(For me, the most difficult part is how to get the python and pip versions right. In Mac the built-in python version is 2.7 but we need python 3 to install &lt;code&gt;pip&lt;/code&gt;.)&lt;/p&gt;

&lt;h3&gt;
  
  
  Up and Run
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;mkdocs
&lt;span class="nv"&gt;$ &lt;/span&gt;mkdocs &lt;span class="nt"&gt;--version&lt;/span&gt;
mkdocs, version 1.1.2

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Assuming that your project folder is &lt;code&gt;doc-project&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;mkdocs new doc-project
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;doc-project

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A MkDocs project is then created with the following folder structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;doc-project
├── docs
│   └── index.md
├── img
│   └── favicon.ico
└── mkdocs.yml

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s run the site:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;mkdocs serve
INFO - Building documentation...
INFO - Cleaning site directory
INFO - Documentation built &lt;span class="k"&gt;in &lt;/span&gt;1.65 seconds
&lt;span class="o"&gt;[&lt;/span&gt;I 200612 23:30:19 server:334] Serving on http://127.0.0.1:8000

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And check it out at &lt;a href="http://127.0.0.1:8000" rel="noopener noreferrer"&gt;http://127.0.0.1:8000&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbemnlam.github.io%2Fimg%2Fdocumentation-makes-easy-with-mkdocs%2F01_welcome.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbemnlam.github.io%2Fimg%2Fdocumentation-makes-easy-with-mkdocs%2F01_welcome.png" alt="Running MkDocs"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Add a new page
&lt;/h3&gt;

&lt;h4&gt;
  
  
  The content
&lt;/h4&gt;

&lt;p&gt;Remember, all the documents should place under &lt;code&gt;docs&lt;/code&gt;. Let’s create an Overview page:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;docs
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;touch &lt;/span&gt;overview.md

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After that, add some content to &lt;code&gt;overview.md&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  The menu
&lt;/h4&gt;

&lt;p&gt;If you want the visitors to reach the overview page, you need to add an url in the menu. Let’s edit &lt;code&gt;mkdocs.yml&lt;/code&gt;, the site config file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# In mkdocs.yml...&lt;/span&gt;
&lt;span class="na"&gt;site_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Doc Project&lt;/span&gt; &lt;span class="c1"&gt;## change your site name here.&lt;/span&gt;
&lt;span class="na"&gt;nav&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Home&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;index.md&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Overview&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;overview.md&lt;/span&gt; &lt;span class="c1"&gt;## add this line.&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will see a new link called &lt;em&gt;Overview&lt;/em&gt; in the menu. If you want to set your site name now, change the value of &lt;code&gt;site_name&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So that’s it. That’s how to add a new page.&lt;/p&gt;

&lt;h3&gt;
  
  
  Theming
&lt;/h3&gt;

&lt;p&gt;You can apply a custom theme to MkDocs. &lt;a href="https://github.com/mkdocs/mkdocs/wiki/MkDocs-Themes" rel="noopener noreferrer"&gt;This page&lt;/a&gt; contains many themes contributed by the community.&lt;/p&gt;

&lt;p&gt;In this section, I will apply the &lt;a href="https://github.com/squidfunk/mkdocs-material" rel="noopener noreferrer"&gt;Material Design theme&lt;/a&gt; to my MkDocs site.&lt;/p&gt;

&lt;p&gt;Stop running your site and install the theme by:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;mkdocs-material

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Update the &lt;code&gt;mkdocs.yml&lt;/code&gt; again:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# In mkdocs.yml...&lt;/span&gt;
&lt;span class="na"&gt;site_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Doc Project&lt;/span&gt;

&lt;span class="na"&gt;nav&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Home&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;index.md&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Overview&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;overview.md&lt;/span&gt;

&lt;span class="c1"&gt;## Add this section&lt;/span&gt;
&lt;span class="na"&gt;theme&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;material&lt;/span&gt;
  &lt;span class="na"&gt;palette&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;## Add this sub-section if you want to change the theme color&lt;/span&gt;
    &lt;span class="na"&gt;scheme&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;amber&lt;/span&gt;
    &lt;span class="na"&gt;primary&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;amber&lt;/span&gt;
    &lt;span class="na"&gt;accent&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;orange&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Start the site:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;mkdocs serve

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will see MkDocs is dressing the amber Material Design theme.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbemnlam.github.io%2Fimg%2Fdocumentation-makes-easy-with-mkdocs%2F02_theming.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbemnlam.github.io%2Fimg%2Fdocumentation-makes-easy-with-mkdocs%2F02_theming.png" alt="Material Design theme with amber scheme"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Custom Resources
&lt;/h3&gt;

&lt;p&gt;Sometimes you may want to add custom CSS or JS. In MkDocs, you can do it by adding &lt;code&gt;extra_css&lt;/code&gt; and &lt;code&gt;extra_javascript&lt;/code&gt; sections in &lt;code&gt;mkdocs.yml&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I created a &lt;code&gt;catalog&lt;/code&gt; page which contains a table written in markdown. Also, I want to apply &lt;a href="https://www.datatables.net/" rel="noopener noreferrer"&gt;jQuery DataTables&lt;/a&gt; to make the table more interactive. Therefore, I added the related js libraries and related css file to MkDocs. You can see the result &lt;a href="https://bemn-proof-of-concept.gitlab.io/mkdoc/catalog/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Exercice:&lt;/strong&gt; create a &lt;code&gt;catalog.md&lt;/code&gt; file and add a markdown table in it.&lt;/p&gt;

&lt;p&gt;Next, add the custom resource sections to the config file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# In mkdocs.yml...&lt;/span&gt;
&lt;span class="na"&gt;site_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Doc Project&lt;/span&gt;
&lt;span class="na"&gt;nav&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Home&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;index.md&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Catalog&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;catalog.md&lt;/span&gt; &lt;span class="c1"&gt;## The catalog page you created in the exercise.&lt;/span&gt;

&lt;span class="na"&gt;theme&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;material&lt;/span&gt;
  &lt;span class="na"&gt;palette&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;scheme&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;amber&lt;/span&gt;
    &lt;span class="na"&gt;primary&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;amber&lt;/span&gt;
    &lt;span class="na"&gt;accent&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;orange&lt;/span&gt;

&lt;span class="c1"&gt;## Custom js files.&lt;/span&gt;
&lt;span class="na"&gt;extra_javascript&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;https://code.jquery.com/jquery-3.5.1.min.js&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;https://cdn.datatables.net/1.10.21/js/jquery.dataTables.min.js&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;scripts/site.js&lt;/span&gt;

&lt;span class="c1"&gt;## Custom css files.&lt;/span&gt;
&lt;span class="na"&gt;extra_css&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;https://cdn.datatables.net/1.10.21/css/jquery.dataTables.min.css&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a custom js file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;docs
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;scripts
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;scripts
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;touch &lt;/span&gt;site.js

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now in your &lt;code&gt;site.js&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;ready&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;table&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nc"&gt;DataTable&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Afer that, you should be able to see an interactive table in the catalog page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbemnlam.github.io%2Fimg%2Fdocumentation-makes-easy-with-mkdocs%2F03_with-custom-resources.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbemnlam.github.io%2Fimg%2Fdocumentation-makes-easy-with-mkdocs%2F03_with-custom-resources.png" alt="With jQuery Datatables"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  GitLab Pages
&lt;/h2&gt;

&lt;p&gt;Create a GitLab repository under your personal space or a project group. The url will be something like &lt;code&gt;https://gitlab.com/{username or project group name}/{repo name}&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Clone the repo you created and create the following items:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;src
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;touch&lt;/span&gt; .gitlab-ci.yml

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Place your MkDoc items (i.e. the &lt;code&gt;doc-project&lt;/code&gt; folder) under &lt;code&gt;src&lt;/code&gt;. See the following folder structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;(&lt;/span&gt;root of the repo&lt;span class="o"&gt;)&lt;/span&gt;
├── .gitlab-ci.yml
└── src
    └── doc-project

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  .gitlab-ci.yml
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;.gitlab-ci.yml&lt;/code&gt; is a GitLab-specific file. GitLab will read and run the steps in the file. We need to tell GitLab how to generate and deploy our static MkDocs website. In this file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# In .gitlab-ci.yml...&lt;/span&gt;
&lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;python:3.8-buster&lt;/span&gt; &lt;span class="c1"&gt;## MkDocs requies python.&lt;/span&gt;

&lt;span class="na"&gt;pages&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;deploy&lt;/span&gt; &lt;span class="c1"&gt;## tell GitLab to deploy the website in this step.&lt;/span&gt;
  &lt;span class="na"&gt;before_script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;pip install mkdocs&lt;/span&gt; &lt;span class="c1"&gt;## install MkDocs.&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;pip install mkdocs-material&lt;/span&gt; &lt;span class="c1"&gt;## install the theme.&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;cd ./src/doc-project&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;mkdocs build&lt;/span&gt; &lt;span class="c1"&gt;## built the site.&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;mv site ../../public&lt;/span&gt;
  &lt;span class="na"&gt;artifacts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;public&lt;/span&gt;
  &lt;span class="c1"&gt;## Uncomment the following section if you want to run this step under master branch only.&lt;/span&gt;
  &lt;span class="c1"&gt;# only:&lt;/span&gt;
  &lt;span class="c1"&gt;# - master &lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So basically it tells GitLab to install the tools required under python 3.8 environment and deploy the website after the site is generated.&lt;/p&gt;

&lt;p&gt;Try to run the following command if you want to generate the site locally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;mkdocs build

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The generated site is located under &lt;code&gt;doc-project/site/&lt;/code&gt; .&lt;/p&gt;

&lt;p&gt;In order to display the website properly, you need to place the generated site under the &lt;code&gt;public&lt;/code&gt; folder and let GitLab knows the &lt;strong&gt;artifact&lt;/strong&gt; inside is the website you want to deploy.&lt;/p&gt;

&lt;h3&gt;
  
  
  Build and Deploy
&lt;/h3&gt;

&lt;p&gt;Push the code to GitLab and the site should build automatically. Check out the status of the build under &lt;strong&gt;CI/CD&lt;/strong&gt; –&amp;gt; &lt;strong&gt;Pipelines&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbemnlam.github.io%2Fimg%2Fdocumentation-makes-easy-with-mkdocs%2F04_gitlab-ci-cd-panel.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbemnlam.github.io%2Fimg%2Fdocumentation-makes-easy-with-mkdocs%2F04_gitlab-ci-cd-panel.png" alt="CI/CD panel in GitLab"&gt;&lt;/a&gt;&lt;br&gt;
            &lt;h4&gt;A green tag showing passed for each successfully build.&lt;/h4&gt;
&lt;br&gt;
         &lt;/p&gt;

&lt;p&gt;Check out your website now. The url is &lt;code&gt;https://{username or project group name}.gitlab.io/{repo name}/&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this tutorial, we created a static site with custom theme, styles and js library by using MkDocs. After that, we set up a CI/CD pipeline on GitLab for building and deploying this static site to GitLab Pages.&lt;/p&gt;

</description>
      <category>gitlab</category>
      <category>mkdoc</category>
      <category>python</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Set Up an AWS EC2 with a GUI, RDP and more</title>
      <dc:creator>Bemn</dc:creator>
      <pubDate>Sat, 03 Oct 2020 05:40:48 +0000</pubDate>
      <link>https://forem.com/bemnlam/set-up-an-aws-ec2-with-a-gui-rdp-and-more-45f9</link>
      <guid>https://forem.com/bemnlam/set-up-an-aws-ec2-with-a-gui-rdp-and-more-45f9</guid>
      <description>&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;This is a guide on how to set up a AWS EC2 free tier with GUI. The EC2 instance will be able to connect via Remote Desktop Protocol (RDP) or SSH.&lt;/p&gt;

&lt;p&gt;You need to create a AWS account: &lt;a href="https://portal.aws.amazon.com/billing/signup"&gt;https://portal.aws.amazon.com/billing/signup&lt;/a&gt;. You may need to provide your credit card information during the signup process.&lt;/p&gt;

&lt;p&gt;Sign in to your AWS Account and go to the AWS EC2 Console: &lt;a href="https://aws.amazon.com/console/"&gt;https://aws.amazon.com/console/&lt;/a&gt;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Table of Contents
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ul&gt;
    &lt;li&gt;Overview&lt;/li&gt;
    &lt;li&gt;
Create an AWS EC2
      &lt;ul&gt;
        &lt;li&gt;Set up the security groups&lt;/li&gt;
        &lt;li&gt;Choose the OS, instance type and storage&lt;/li&gt;
        &lt;li&gt;Create and download a &lt;code&gt;.pem&lt;/code&gt; key file&lt;/li&gt;
      &lt;/ul&gt;
    &lt;/li&gt;
    &lt;li&gt;Connect to EC2 via SSH&lt;/li&gt;
    &lt;li&gt;
Install a GUI and RDP
      &lt;ul&gt;
        &lt;li&gt;GUI: &lt;code&gt;LXDE&lt;/code&gt;
&lt;/li&gt;
        &lt;li&gt;RDP: &lt;code&gt;XRDP&lt;/code&gt;
&lt;/li&gt;
        &lt;li&gt;Connect to EC2 via remote desktop&lt;/li&gt;
      &lt;/ul&gt;
    &lt;/li&gt;
    &lt;li&gt;
Bonus Materials
      &lt;ul&gt;
        &lt;li&gt;1. Install additional IBus input method&lt;/li&gt;
        &lt;li&gt;2. Install Midori browser from &lt;code&gt;.deb&lt;/code&gt; package&lt;/li&gt;
        &lt;li&gt;3. Set up a &lt;code&gt;swapfile&lt;/code&gt; for better performance&lt;/li&gt;
        &lt;li&gt;4. Use EC2 as a VPN server&lt;/li&gt;
      &lt;/ul&gt;
    &lt;/li&gt;
  &lt;/ul&gt;

&lt;h2&gt;
  
  
  Create an AWS EC2
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Set up the security groups
&lt;/h3&gt;

&lt;p&gt;Defining the security groups before setting up the instance. Setting up the security groups first can give you a better understanding on what inbound/outbound traffic is allowed in the instances you are going to create.&lt;/p&gt;

&lt;p&gt;Choose &lt;strong&gt;Security Groups&lt;/strong&gt; at the left menu of your &lt;strong&gt;EC2 Dashboard&lt;/strong&gt;. Create 2 security groups:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;rdp&lt;/code&gt;: Allow inbound traffic via port &lt;code&gt;3389&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ssh&lt;/code&gt;: Allow inbound traffic via port &lt;code&gt;22&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Allow all outbound traffic&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Choose the OS, instance type and storage
&lt;/h3&gt;

&lt;p&gt;Choose &lt;strong&gt;Services&lt;/strong&gt; &amp;gt; &lt;strong&gt;EC2&lt;/strong&gt; from the menu. At the &lt;strong&gt;EC2 Dashboard&lt;/strong&gt; , choose &lt;strong&gt;Instances&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Click the &lt;strong&gt;Launch Instance&lt;/strong&gt; and choose the suitable AMI (Amazon Machine Image). Here I choose a &lt;code&gt;t2.micro&lt;/code&gt; instance with &lt;code&gt;Ubuntu Server 18.04 LTS&lt;/code&gt; image. &lt;strong&gt;Choose the suitable Storage&lt;/strong&gt; as not all kinds of storage are free. In this example I set up a &lt;code&gt;30GB General Purpose&lt;/code&gt; storage.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ZloghaeC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bemnlam.github.io/img/set-up-an-aws-ec2-with-ubuntu-gui-rdp-and-more/image-20200702183719536.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ZloghaeC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bemnlam.github.io/img/set-up-an-aws-ec2-with-ubuntu-gui-rdp-and-more/image-20200702183719536.png" alt="Image: Ubuntu Server 18.04 LTS" width="859" height="683"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0wuDOeF0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bemnlam.github.io/img/set-up-an-aws-ec2-with-ubuntu-gui-rdp-and-more/image-20200702183754512.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0wuDOeF0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bemnlam.github.io/img/set-up-an-aws-ec2-with-ubuntu-gui-rdp-and-more/image-20200702183754512.png" alt="Instance Type: t2.micro" width="859" height="582"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ydhpk6FZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bemnlam.github.io/img/set-up-an-aws-ec2-with-ubuntu-gui-rdp-and-more/image-20200702183909326.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ydhpk6FZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bemnlam.github.io/img/set-up-an-aws-ec2-with-ubuntu-gui-rdp-and-more/image-20200702183909326.png" alt="Click Review and Launch" width="880" height="558"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mZa4OrgU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bemnlam.github.io/img/set-up-an-aws-ec2-with-ubuntu-gui-rdp-and-more/image-20200702183922739.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mZa4OrgU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bemnlam.github.io/img/set-up-an-aws-ec2-with-ubuntu-gui-rdp-and-more/image-20200702183922739.png" alt="Storage: 30GB General Purpose" width="880" height="558"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Allow SSH and RDP connections
&lt;/h4&gt;

&lt;p&gt;Choose the security groups created in the previous step:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Au0E6jcG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bemnlam.github.io/img/image-20200702184007198.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Au0E6jcG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bemnlam.github.io/img/image-20200702184007198.png" alt="Security Group: allow SSH and RDP" width="880" height="558"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ibk1ODDN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bemnlam.github.io/img/set-up-an-aws-ec2-with-ubuntu-gui-rdp-and-more/image-20200702184046233.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ibk1ODDN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bemnlam.github.io/img/set-up-an-aws-ec2-with-ubuntu-gui-rdp-and-more/image-20200702184046233.png" alt="Summary of the attached security groups" width="880" height="195"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Create and download a &lt;code&gt;.pem&lt;/code&gt; key file
&lt;/h3&gt;

&lt;p&gt;You should create the key file. If you don’t create this and link it to the instance, you will not be able to connect to the instance via SSH or RDP. Here I create a new key file named &lt;code&gt;ssh_private_key&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;After that, click &lt;strong&gt;Download Key Pair&lt;/strong&gt; and keep the key file safe.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Velh7g-k--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bemnlam.github.io/img/set-up-an-aws-ec2-with-ubuntu-gui-rdp-and-more/image-20200702184135610.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Velh7g-k--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bemnlam.github.io/img/set-up-an-aws-ec2-with-ubuntu-gui-rdp-and-more/image-20200702184135610.png" alt="Generate a key file" width="703" height="523"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h5&gt;
  
  
  Do a &lt;code&gt;chmod&lt;/code&gt; on the key file
&lt;/h5&gt;

&lt;p&gt;We better to make sure that the &lt;code&gt;ssh_private_key.pem&lt;/code&gt; downloaded is just read-only. Run &lt;code&gt;chmod&lt;/code&gt; to change the permission:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;❯ ll ~/Downloads/ | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"ssh_private_key.pem"&lt;/span&gt;
&lt;span class="nt"&gt;-rw-r--r--&lt;/span&gt;@ 1 BEN staff 1.7K Jul 2 18:41 ssh_private_key.pem

❯ &lt;span class="nb"&gt;chmod &lt;/span&gt;400 ~/Downloads/ssh_private_key.pem

❯ ll ~/Downloads/ | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"ssh_private_key.pem"&lt;/span&gt;
&lt;span class="nt"&gt;-r--------&lt;/span&gt;@ 1 BEN staff 1.7K Jul 2 18:41 ssh_private_key.pem

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Connect to EC2 via SSH
&lt;/h2&gt;

&lt;p&gt;Once you click the &lt;strong&gt;Launch instance&lt;/strong&gt; , you should be able to see your instance in &lt;strong&gt;running&lt;/strong&gt; state in the web console. Then, you can connect to the EC2 via ssh using the &lt;code&gt;pem&lt;/code&gt; key file. Assuming that your DNS name of your EC2 is &lt;code&gt;ec2-0-1-2-3.ap-northeast-1.compute.amazonaws.com&lt;/code&gt; and it is running at &lt;code&gt;ap-northeast-1&lt;/code&gt; region, you can connect to it using user &lt;code&gt;ubuntu&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;❯ ssh &lt;span class="nt"&gt;-i&lt;/span&gt; ~/Downloads/ssh_private_key.pem ubuntu@ec2-0-1-2-3.region-name.compute.amazonaws.com
The authenticity of host &lt;span class="s1"&gt;'ec2-0-1-2-3.region-name.compute.amazonaws.com (0.1.2.3)'&lt;/span&gt; can&lt;span class="s1"&gt;'t be established.
ECDSA key fingerprint is SHA256:8t8ey5IqoiGOKGc9WuDc5SXaYqFHPnKph1mRtzbQNTc.
Are you sure you want to continue connecting (yes/no/[fingerprint])?

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You need to confirm your fingerprint at the first connection attempt.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Welcome to Ubuntu 18.04.4 LTS &lt;span class="o"&gt;(&lt;/span&gt;GNU/Linux 5.3.0-1023-aws x86_64&lt;span class="o"&gt;)&lt;/span&gt;

 &lt;span class="k"&gt;*&lt;/span&gt; Documentation: https://help.ubuntu.com
 &lt;span class="k"&gt;*&lt;/span&gt; Management: https://landscape.canonical.com
 &lt;span class="k"&gt;*&lt;/span&gt; Support: https://ubuntu.com/advantage

  System information as of Thu Jul 2 10:54:37 UTC 2020

  System load: 0.0 Processes: 90
  Usage of /: 3.7% of 29.02GB Users logged &lt;span class="k"&gt;in&lt;/span&gt;: 0
  Memory usage: 16% IP address &lt;span class="k"&gt;for &lt;/span&gt;eth0: 172.31.4.8
  Swap usage: 0%

0 packages can be updated.
0 updates are security updates.

The programs included with the Ubuntu system are free software&lt;span class="p"&gt;;&lt;/span&gt;
the exact distribution terms &lt;span class="k"&gt;for &lt;/span&gt;each program are described &lt;span class="k"&gt;in &lt;/span&gt;the
individual files &lt;span class="k"&gt;in&lt;/span&gt; /usr/share/doc/&lt;span class="k"&gt;*&lt;/span&gt;/copyright.

Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by
applicable law.

To run a &lt;span class="nb"&gt;command &lt;/span&gt;as administrator &lt;span class="o"&gt;(&lt;/span&gt;user &lt;span class="s2"&gt;"root"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;, use &lt;span class="s2"&gt;"sudo &amp;lt;command&amp;gt;"&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
See &lt;span class="s2"&gt;"man sudo_root"&lt;/span&gt; &lt;span class="k"&gt;for &lt;/span&gt;details.

ubuntu@ip-172-31-4-8:~&lt;span class="err"&gt;$&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Install a GUI and RDP
&lt;/h2&gt;

&lt;h3&gt;
  
  
  GUI: &lt;code&gt;LXDE&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;I choose &lt;strong&gt;LXDE&lt;/strong&gt; as the GUI of the ubuntu server: &lt;a href="https://lxde.sourceforge.net/about.html"&gt;https://lxde.sourceforge.net/about.html&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;LXDE is a new project aimed to provide a new desktop environment which is lightweight and fast&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  RDP: &lt;code&gt;XRDP&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;xrdp is an open-source Remote Desktop Protocol server: &lt;a href="http://xrdp.org"&gt;http://xrdp.org&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;xrdp provides a graphical login to remote machines using RDP (Microsoft Remote Desktop Protocol)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Install the packages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get update &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install &lt;/span&gt;lxde &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install &lt;/span&gt;xrdp &lt;span class="nt"&gt;-y&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Set a password for your account
&lt;/h4&gt;

&lt;p&gt;You need to also set up a password for the user &lt;em&gt;ubuntu&lt;/em&gt;. This is for your later use in RDP as RDP only allows you to login an password-protected account.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;passwd ubuntu

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Connect to EC2 via remote desktop
&lt;/h3&gt;

&lt;p&gt;For mac user, you need to download the &lt;a href="https://apps.apple.com/us/app/microsoft-remote-desktop/id1295203466?mt=12"&gt;Microsoft Remote Desktop&lt;/a&gt;. If you are using Windows, you should already have the Remote Desktop application preinstalled.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://apps.apple.com/us/app/microsoft-remote-desktop/id1295203466?mt=12"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--So1nFOUk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bemnlam.github.io/img/image-20201003143752732.png" alt="Download Microsoft Remote Desktop" width="652" height="263"&gt;&lt;/a&gt;Launch the app and click &lt;strong&gt;Add PC&lt;/strong&gt; :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--MXb2wqQE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bemnlam.github.io/img/set-up-an-aws-ec2-with-ubuntu-gui-rdp-and-more/image-20200702192027044.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MXb2wqQE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bemnlam.github.io/img/set-up-an-aws-ec2-with-ubuntu-gui-rdp-and-more/image-20200702192027044.png" alt="Add PC in Microsoft Remote Desktop" width="607" height="499"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Set the PC name (i.e. the DNS name of your machine): &lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--GHpXD-J9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bemnlam.github.io/img/set-up-an-aws-ec2-with-ubuntu-gui-rdp-and-more/image-20200702192128973.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--GHpXD-J9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bemnlam.github.io/img/set-up-an-aws-ec2-with-ubuntu-gui-rdp-and-more/image-20200702192128973.png" alt="Set PC name" width="713" height="671"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Login with user &lt;code&gt;ubuntu&lt;/code&gt; and the password you set in the previous step.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--yLY4Jbsj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bemnlam.github.io/img/set-up-an-aws-ec2-with-ubuntu-gui-rdp-and-more/image-20200702192239827.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--yLY4Jbsj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bemnlam.github.io/img/set-up-an-aws-ec2-with-ubuntu-gui-rdp-and-more/image-20200702192239827.png" alt="Connect" width="662" height="343"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Bonus Materials
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Install additional IBus input method
&lt;/h3&gt;

&lt;p&gt;If you want to type Japanese, Chinese, etc. you may need to install an input method. For cangjie, you can install &lt;a href="http://cangjians.github.io/projects/ibus-cangjie/install.html"&gt;&lt;strong&gt;Cangjians&lt;/strong&gt;&lt;/a&gt; via the terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install &lt;/span&gt;ibus-cangjie

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Install Midori browser from &lt;code&gt;.deb&lt;/code&gt; package
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Midori Browser&lt;/strong&gt; is a browser web light, fast, secure, free software &amp;amp; open source&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Download .deb file: &lt;a href="https://astian.org/en/midori-browser/download/"&gt;https://astian.org/en/midori-browser/download/&lt;/a&gt; or using &lt;code&gt;wget&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ~/Downloads
&lt;span class="nb"&gt;sudo &lt;/span&gt;wget &lt;span class="nt"&gt;-qnc&lt;/span&gt; https://packages.astian.org/pool/main/m/midori/midori_9.0.0-1_amd64.deb


ubuntu@ip-172-31-4-8:~/Downloads&lt;span class="nv"&gt;$ &lt;/span&gt;ll
total 1492
drwxr-xr-x 2 ubuntu ubuntu 4096 Jul 2 11:29 ./
drwxr-xr-x 17 ubuntu ubuntu 4096 Jul 2 11:22 ../
&lt;span class="nt"&gt;-rw-r--r--&lt;/span&gt; 1 root root 1517328 Jan 11 23:26 midori_9.0.0-1_amd64.deb

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After that, install the &lt;code&gt;deb&lt;/code&gt; package using &lt;code&gt;dpkg&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;dpkg &lt;span class="nt"&gt;-i&lt;/span&gt; midori_9.0.0-1_amd64.deb

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You may see the &lt;code&gt;dependency problems&lt;/code&gt; like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ubuntu@ip-172-31-4-8:~/Downloads&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;dpkg &lt;span class="nt"&gt;--configure&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt;
dpkg: dependency problems prevent configuration of midori:
 midori depends on libpeas-1.0-0 &lt;span class="o"&gt;(&amp;gt;=&lt;/span&gt; 1.15&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; however:
  Package libpeas-1.0-0 is not installed.
 midori depends on libxml2-utils&lt;span class="p"&gt;;&lt;/span&gt; however:
  Package libxml2-utils is not installed.
 midori depends on gstreamer1.0-plugins-bad&lt;span class="p"&gt;;&lt;/span&gt; however:
  Package gstreamer1.0-plugins-bad is not installed.
 midori depends on gstreamer1.0-libav&lt;span class="p"&gt;;&lt;/span&gt; however:
  Package gstreamer1.0-libav is not installed.

dpkg: error processing package midori &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;--configure&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;:
 dependency problems - leaving unconfigured
Errors were encountered &lt;span class="k"&gt;while &lt;/span&gt;processing:
 midori

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this case, update the packages and reinstall midori:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;dpkg &lt;span class="nt"&gt;--configure&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="nb"&gt;install
sudo &lt;/span&gt;apt-get update
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install &lt;/span&gt;midori

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Set up a &lt;code&gt;swapfile&lt;/code&gt; for better performance
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ubuntu@ip-172-31-4-8:~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;fallocate &lt;span class="nt"&gt;-l&lt;/span&gt; 1G /swapfile
ubuntu@ip-172-31-4-8:~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo chmod &lt;/span&gt;600 /swapfile
ubuntu@ip-172-31-4-8:~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;mkswap /swapfile
Setting up swapspace version 1, size &lt;span class="o"&gt;=&lt;/span&gt; 1024 MiB &lt;span class="o"&gt;(&lt;/span&gt;1073737728 bytes&lt;span class="o"&gt;)&lt;/span&gt;
no label, &lt;span class="nv"&gt;UUID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;9a121c2c-cd6f-48bf-b928-e550095a3efd
ubuntu@ip-172-31-4-8:~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;swapon /swapfile
ubuntu@ip-172-31-4-8:~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;swapon &lt;span class="nt"&gt;--show&lt;/span&gt;
NAME TYPE SIZE USED PRIO
/swapfile file 1024M 0B &lt;span class="nt"&gt;-2&lt;/span&gt;
ubuntu@ip-172-31-4-8:~&lt;span class="err"&gt;$&lt;/span&gt;


&lt;span class="nb"&gt;sudo &lt;/span&gt;nano /etc/fstab


/swapfile swap swap defaults 0 0

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After that, reboot the machine.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Use EC2 as a VPN server
&lt;/h3&gt;

&lt;p&gt;Create a new security group allows all TCP and UDP inbound connections and add this security group to the EC2 instance:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6FCqAWH9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bemnlam.github.io/img/set-up-an-aws-ec2-with-ubuntu-gui-rdp-and-more/image-20200702200459525.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6FCqAWH9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bemnlam.github.io/img/set-up-an-aws-ec2-with-ubuntu-gui-rdp-and-more/image-20200702200459525.png" alt="EC2 instance inbound rules" width="880" height="422"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--k5ft7o51--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bemnlam.github.io/img/set-up-an-aws-ec2-with-ubuntu-gui-rdp-and-more/image-20200702200555846.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--k5ft7o51--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bemnlam.github.io/img/set-up-an-aws-ec2-with-ubuntu-gui-rdp-and-more/image-20200702200555846.png" alt="Assign a security group" width="459" height="372"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Install openvpn scripts
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://github.com/Nyr/openvpn-install"&gt;This repo&lt;/a&gt; contains scripts for VPN server set up and profile management. Install it into your EC2:&lt;/p&gt;

&lt;p&gt;Download the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt upgrade &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;span class="nb"&gt;mkdir&lt;/span&gt; ~/vpn &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; ~/vpn
wget https://git.io/vpn &lt;span class="nt"&gt;-O&lt;/span&gt; openvpn-install.sh


ubuntu@ip-172-31-4-8:~/vpn&lt;span class="nv"&gt;$ &lt;/span&gt;ll
total 32
drwxrwxr-x 2 ubuntu ubuntu 4096 Jul 2 12:11 ./
drwxr-xr-x 19 ubuntu ubuntu 4096 Jul 2 12:11 ../
&lt;span class="nt"&gt;-rw-rw-r--&lt;/span&gt; 1 ubuntu ubuntu 23085 Jul 2 12:11 openvpn-install.sh

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make the &lt;code&gt;openvpn-install.sh&lt;/code&gt; exectable in your EC2:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x openvpn-install.sh
&lt;span class="nb"&gt;sudo&lt;/span&gt; ./openvpn-install.sh

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add a new profile by running &lt;code&gt;openvpn-install.sh&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Welcome to this OpenVPN road warrior installer!

This server is behind NAT. What is the public IPv4 address or &lt;span class="nb"&gt;hostname&lt;/span&gt;?
Public IPv4 address / &lt;span class="nb"&gt;hostname&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;18.183.12.213]:

Which protocol should OpenVPN use?
   1&lt;span class="o"&gt;)&lt;/span&gt; UDP &lt;span class="o"&gt;(&lt;/span&gt;recommended&lt;span class="o"&gt;)&lt;/span&gt;
   2&lt;span class="o"&gt;)&lt;/span&gt; TCP
Protocol &lt;span class="o"&gt;[&lt;/span&gt;1]: 1

What port should OpenVPN listen to?
Port &lt;span class="o"&gt;[&lt;/span&gt;1194]:

Select a DNS server &lt;span class="k"&gt;for &lt;/span&gt;the clients:
   1&lt;span class="o"&gt;)&lt;/span&gt; Current system resolvers
   2&lt;span class="o"&gt;)&lt;/span&gt; Google
   3&lt;span class="o"&gt;)&lt;/span&gt; 1.1.1.1
   4&lt;span class="o"&gt;)&lt;/span&gt; OpenDNS
   5&lt;span class="o"&gt;)&lt;/span&gt; Quad9
   6&lt;span class="o"&gt;)&lt;/span&gt; AdGuard
DNS server &lt;span class="o"&gt;[&lt;/span&gt;1]: 1

Enter a name &lt;span class="k"&gt;for &lt;/span&gt;the first client:
Name &lt;span class="o"&gt;[&lt;/span&gt;client]: demo-ec2-client

OpenVPN installation is ready to begin.
Press any key to &lt;span class="k"&gt;continue&lt;/span&gt;...


Finished!

The client configuration is available &lt;span class="k"&gt;in&lt;/span&gt;: /home/ubuntu/demo-ec2-client.ovpn
New clients can be added by running this script again.

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use another Terminal to download the &lt;code&gt;.ovpn&lt;/code&gt; file. Assuming that the machine name is &lt;code&gt;ec2-0-1-2-3.ap-northeast-1.compute.amazonaws.com&lt;/code&gt; and the generated &lt;code&gt;.ovpn&lt;/code&gt; file is place under &lt;code&gt;~/&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;scp &lt;span class="nt"&gt;-i&lt;/span&gt; ssh_private_key.pem ubuntu@ec2-0-1-2-3.ap-northeast-1.compute.amazonaws.com:~/demo-ec2-client.ovpn ~/Downloads/demo-ec2-client.ovpn

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Connect to the VPN
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://client.pritunl.com/"&gt;Pritunl Client&lt;/a&gt; is a easy-to-use OpenVPN client. Downlaod and install it.&lt;/p&gt;

&lt;p&gt;Launch the client and click &lt;strong&gt;Import Profile&lt;/strong&gt; :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--uJJwF_36--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bemnlam.github.io/img/screenshot.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--uJJwF_36--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://bemnlam.github.io/img/screenshot.png" alt="Import ovpn profile" width="800" height="1166"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After that, click the burger menu of the profile and &lt;strong&gt;Connect&lt;/strong&gt;.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>ec2</category>
      <category>rdp</category>
      <category>ovpn</category>
    </item>
    <item>
      <title>Use Gravatar Everywhere</title>
      <dc:creator>Bemn</dc:creator>
      <pubDate>Sat, 18 Jul 2020 04:01:56 +0000</pubDate>
      <link>https://forem.com/bemnlam/use-gravatar-everywhere-acj</link>
      <guid>https://forem.com/bemnlam/use-gravatar-everywhere-acj</guid>
      <description>&lt;h3&gt;
  
  
  What is Gravatar
&lt;/h3&gt;

&lt;p&gt;Your public profile on the internet, provided by Wordpress. See &lt;a href="https://en.gravatar.com/"&gt;https://en.gravatar.com/&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Upload an image to your gravatar account
&lt;/h3&gt;

&lt;p&gt;If you don’t have the gravatar account, create one. Make sure that the email you used to create the account will be the email account you want to link your profile picture.&lt;/p&gt;

&lt;p&gt;After that, upload your profile picture to gravatar.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Use the image everywhere
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Get your md5 hash of email address
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Ask DuckDuckgo&lt;/strong&gt;&lt;br&gt;
You can get the md5 checksum using &lt;a href="https://duckduckgo.com"&gt;duckduckgo&lt;/a&gt;. Search &lt;code&gt;md5 {your email address}&lt;/code&gt; and then you will see the hash:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--g5PmmGST--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/D9oVAVX.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--g5PmmGST--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/D9oVAVX.png" alt="get a md5 hash on duckduckgo" width="731" height="242"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use Command Line&lt;/strong&gt;&lt;br&gt;
For MacOS user, you should have the md5 utility installed, go to the terminal and check:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;❯ which md5
/sbin/md5

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After that, generate the md5 hash of your email address:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;❯ md5 -s "myemall@address.com"
MD5 ("myemall@address.com") = ca49930cff2f87bd37bfe71ce21467f

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The gravatar image url is in this format:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://gravatar.com/avatar/{your md5 hash}?s={size}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want to have a 200px avatar: &lt;code&gt;https://gravatar.com/avatar/ca49930cff2f87bd37bfe71ce21467f?s=200&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Use this image url e.g. in a &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;img src="https://gravatar.com/avatar/ca49930cff2f87bd37bfe71ce21467f?s=200" /&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And you are good to go!&lt;/p&gt;

</description>
      <category>gravatar</category>
      <category>duckduckgo</category>
    </item>
    <item>
      <title>Displaying the Full Content in Hugo's RSS feed</title>
      <dc:creator>Bemn</dc:creator>
      <pubDate>Fri, 03 Jul 2020 01:53:38 +0000</pubDate>
      <link>https://forem.com/bemnlam/displaying-the-full-content-in-hugo-s-rss-feed-4jg2</link>
      <guid>https://forem.com/bemnlam/displaying-the-full-content-in-hugo-s-rss-feed-4jg2</guid>
      <description>&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;Recently I created a hugo blog and started writing articles. Later, I found that dev.to can &lt;a href="https://dev.to/settings/publishing-from-rss"&gt;publish articles (as draft) from a RSS feed&lt;/a&gt;. In that way I don’t have to copy-and-paste my articles manually in order to publish the same item on dev.to!&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Table of Contents
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ul&gt;
    &lt;li&gt;😨 Problem&lt;/li&gt;
    &lt;li&gt;
😀 Solution
      &lt;ul&gt;
        &lt;li&gt;Locating &lt;code&gt;rss.xml&lt;/code&gt;
&lt;/li&gt;
        &lt;li&gt;Rendering the full content&lt;/li&gt;
        &lt;li&gt;Regenerating the site&lt;/li&gt;
      &lt;/ul&gt;
    &lt;/li&gt;
    &lt;li&gt;Bonus: The difference between &lt;code&gt;{{&lt;/code&gt; and &lt;code&gt;{{-&lt;/code&gt; in Hugo&lt;/li&gt;
    &lt;li&gt;Conclusion&lt;/li&gt;
  &lt;/ul&gt;

&lt;h2&gt;
  
  
  😨 Problem
&lt;/h2&gt;

&lt;p&gt;Although dev.to’s puslishing from RSS feed service is great, my Hugo site is not that great (with the basic settings). &lt;strong&gt;When I was trying to sync my RSS feed items to dev.to, all items can be imported but only the beginning part of the content was fetched.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I know that should be something wrong with the RSS feeds generated by Hugo. The &lt;a href="https://github.com/gohugoio/hugo/blob/master/tpl/tplimpl/embedded/templates/_default/rss.xml"&gt;default Hugo RSS template&lt;/a&gt; only renders &lt;code&gt;Summary&lt;/code&gt; of an article and my Hugo theme is using that:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;As you can see in &lt;strong&gt;line 73&lt;/strong&gt; , the &lt;a href="https://validator.w3.org/feed/docs/rss2.html"&gt;&lt;code&gt;&amp;lt;description&amp;gt;&lt;/code&gt;&lt;/a&gt; in my RSS feeds contain article summary only but not the full content. Is there any ways to change the &lt;code&gt;Summary&lt;/code&gt; here into something else? Something else that represents the &lt;strong&gt;full content&lt;/strong&gt;?&lt;/p&gt;

&lt;h2&gt;
  
  
  😀 Solution
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Locating &lt;code&gt;rss.xml&lt;/code&gt; &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Usually, Hugo templates are placed under &lt;code&gt;{your hugo theme}/layouts/_defualt/&lt;/code&gt;. If such file does not exist, it’s time to create one. You can copy-and-paste the code from &lt;a href="https://github.com/gohugoio/hugo/blob/master/tpl/tplimpl/embedded/templates/_default/rss.xml"&gt;Hugo’s github&lt;/a&gt; or run the following &lt;code&gt;curl&lt;/code&gt; command under &lt;code&gt;/layouts/_default&lt;/code&gt; to download it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="s2"&gt;"https://raw.githubusercontent.com/gohugoio/hugo/master/tpl/tplimpl/embedded/templates/_default/rss.xml"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; rss.xml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Rendering the full content
&lt;/h3&gt;

&lt;p&gt;Rendering the full content of is really simple and straight-forward. What you need to do is just replacing this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;description&amp;gt;&lt;/span&gt;{{ .Summary | html }}&lt;span class="nt"&gt;&amp;lt;/description&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;description&amp;gt;&lt;/span&gt;{{- .Content | html -}}&lt;span class="nt"&gt;&amp;lt;/description&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Caution: use &lt;code&gt;{{-&lt;/code&gt; instead of &lt;code&gt;{{&lt;/code&gt;. Read the bonus part if you want to know what is the difference.&lt;/p&gt;

&lt;h3&gt;
  
  
  Regenerating the site
&lt;/h3&gt;

&lt;p&gt;Updating the RSS template &lt;strong&gt;will not&lt;/strong&gt; update your &lt;strong&gt;generated content&lt;/strong&gt; automatically. Therefore, you need to re-generate your Hugo site:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;hugo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see the changes in all RSS xml files (from git). After that, push all the code changes, remove all the fetched posts in dev.to and re-fetch them. That’s it.&lt;/p&gt;

&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Bonus: The difference between &lt;code&gt;{{&lt;/code&gt; and &lt;code&gt;{{-&lt;/code&gt; in Hugo
&lt;/h2&gt;

&lt;p&gt;You may notice that the template code is changed into &lt;code&gt;{{-&lt;/code&gt; from &lt;code&gt;{{&lt;/code&gt;. As a newbie in Hugo I checked &lt;a href="https://gohugo.io/templates/introduction/#whitespace"&gt;the Hugo’s official documentation about &lt;em&gt;whitespace&lt;/em&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;… the ability to trim the whitespace from either side of a Go tag …&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This &lt;code&gt;-&lt;/code&gt; notation is introduced in Go 1.6.&lt;/p&gt;

&lt;p&gt;The following example (from the documentation):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
  {{ .Title }}
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
  {{- .Title -}}
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which will output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
  Hello, World!
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;Hello, World!&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The default RSS feed in Hugo site shows &lt;em&gt;article abstract&lt;/em&gt; only. By defining a custom &lt;code&gt;rss.xml&lt;/code&gt; template and replacing &lt;code&gt;{{ .Summary | html }}&lt;/code&gt; with &lt;code&gt;{{- .Content | html -}}&lt;/code&gt; in the &lt;code&gt;&amp;lt;description&amp;gt;&lt;/code&gt; tag you will get the article’s full content in the re-generated RSS xml files.&lt;/p&gt;

</description>
      <category>hugo</category>
      <category>rss</category>
    </item>
    <item>
      <title>The Perks of Being a Mac (or FreeBSD) user</title>
      <dc:creator>Bemn</dc:creator>
      <pubDate>Sun, 28 Jun 2020 04:28:38 +0000</pubDate>
      <link>https://forem.com/bemnlam/the-perks-of-being-a-mac-or-freebsd-user-406</link>
      <guid>https://forem.com/bemnlam/the-perks-of-being-a-mac-or-freebsd-user-406</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;🚨 Note: This is not a technical reading. If you want to have some fun and practice your &lt;code&gt;ls&lt;/code&gt; and &lt;code&gt;cat&lt;/code&gt; skills, this article may suit you.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Table of Content
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
Misc. files of the OS

&lt;ul&gt;
&lt;li&gt;The meaning of flowers&lt;/li&gt;
&lt;li&gt;The birthday present guide&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Mac is a secret fan of…&lt;/li&gt;
&lt;li&gt;Bonus: the missing recipe&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Misc. Files of the OS
&lt;/h2&gt;

&lt;p&gt;I found some interesting files under &lt;code&gt;/usr/share/misc&lt;/code&gt; of my MacBook:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nt"&gt;-r--r--r--&lt;/span&gt; 1 root wheel 3.1K Dec 14 2019 ascii
&lt;span class="nt"&gt;-r--r--r--&lt;/span&gt; 1 root wheel 374B Dec 14 2019 birthtoken
&lt;span class="nt"&gt;-r--r--r--&lt;/span&gt; 1 root wheel 2.9K Dec 14 2019 eqnchar
&lt;span class="nt"&gt;-r--r--r--&lt;/span&gt; 1 root wheel 1.4K Dec 14 2019 flowers
&lt;span class="nt"&gt;-r--r--r--&lt;/span&gt; 1 root wheel 508B Dec 14 2019 getopt
&lt;span class="nt"&gt;-rw-r--r--&lt;/span&gt; 1 root wheel 941B Dec 14 2019 mail.help
&lt;span class="nt"&gt;-rw-r--r--&lt;/span&gt; 1 root wheel 1.3K Dec 14 2019 mail.tildehelp
&lt;span class="nt"&gt;-r--r--r--&lt;/span&gt; 1 root wheel 310B Dec 14 2019 man.template
&lt;span class="nt"&gt;-r--r--r--&lt;/span&gt; 1 root wheel 948B Dec 14 2019 mdoc.template
&lt;span class="nt"&gt;-r--r--r--&lt;/span&gt; 1 root wheel 425B Dec 14 2019 operator
&lt;span class="nt"&gt;-r--r--r--&lt;/span&gt; 1 root wheel 78K May 27 11:38 trace.codes
&lt;span class="nt"&gt;-rw-r--r--&lt;/span&gt; 1 root wheel 13K Dec 14 2019 units.lib
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Meaning of Flowers
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;/usr/share/misc/flowers&lt;/code&gt; lists out the meaning of flowers&lt;sup id="fnref:1"&gt;1&lt;/sup&gt;. Check this out if you are going to buy some flowers but you don’t know what you should buy.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Flower : Meaning
#   @(#)flowers 8.1 (Berkeley) 6/8/93
#
# Upside down reverses the meaning.
African violet:Such worth is rare.
Apple blossom:Preference.
Bachelor's button:Celibacy.
Bay leaf:I change but in death.
Camelia:Reflected loveliness.
Chrysanthemum, other color:Slighted love.
Chrysanthemum, red:I love.
Chrysanthemum, white:Truth.
Clover:Be mine.
Crocus:Abuse not.
Daffodil:Innocence.
Forget-me-not:True love.
Fuchsia:Fast.
Gardenia:Secret, untold love.
Honeysuckle:Bonds of love.
Ivy:Friendship, fidelity, marriage.
Jasmine:Amiability, transports of joy, sensuality.
Leaves (dead):Melancholy.
Lilac:Youthful innocence.
Lilly of the valley:Return of happiness.
Lilly:Purity, sweetness.
Magnolia:Dignity, perseverance.
Marigold:Jealousy.
Mint:Virtue.
Orange blossom:Your purity equals your loveliness.
Orchid:Beauty, magnificence.
Pansy:Thoughts.
Peach blossom:I am your captive.
Petunia:Your presence soothes me.
Poppy:Sleep.
Rose, any color:Love.
Rose, deep red:Bashful shame.
Rose, single, pink:Simplicity.
Rose, thornless, any color:Early attachment.
Rose, white:I am worthy of you.
Rose, yellow:Decrease of love, rise of jealousy.
Rosebud, white:Girlhood, and a heart ignorant of love.
Rosemary:Remembrance.
Sunflower:Haughtiness.
Tulip, red:Declaration of love.
Tulip, yellow:Hopeless love.
Violet, blue:Faithfulness.
Violet, white:Modesty.
Zinnia:Thoughts of absent friends.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Birthday Present Guide
&lt;/h2&gt;

&lt;p&gt;Check out the &lt;code&gt;/usr/share/misc/birthtoken&lt;/code&gt;&lt;sup id="fnref:2"&gt;2&lt;/sup&gt; and see which gem and flower (looks like someone in Berkeley knows about the flowers really well) you should buy for your friends!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;“Oh why you choose this gem and that flower as the present?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;“Um… that’s my computer’s idea…"&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  More About Perl and Ruby
&lt;/h3&gt;

&lt;p&gt;The creator of &lt;a href="https://www.ruby-lang.org"&gt;Ruby&lt;/a&gt; chose the name &lt;em&gt;Ruby&lt;/em&gt; because it was &lt;a href="https://ruby-doc.org/docs/ruby-doc-bundle/FAQ/FAQ.html"&gt;the birthstone of one of his colleagues&lt;/a&gt; and that is the gem right after Pearl (&lt;a href="https://www.perl.org/"&gt;Perl&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lesson learnt:&lt;/strong&gt; when you try to name a new language you invented, you should check this file out!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Birthday : Birth Stone : Birth Flower
#   @(#)birthtoken  8.1 (Berkeley) 6/8/93
January:Garnet:Carnation
February:Amethyst:Violet
March:Aquamarine:Jonquil
April:Diamond:Sweetpea
May:Emerald:Lily Of The Valley
June:Pearl:Rose
July:Ruby:Larkspur
August:Peridot:Gladiolus
September:Sapphire:Aster
October:Opal:Calendula
November:Topaz:Chrysanthemum
December:Turquoise:Narcissus
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Mac is a Secret Fan of…
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;/usr/share/calendar&lt;/code&gt; folder contains some useful calendars but this one really catches my eye:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nt"&gt;-rw-r--r--&lt;/span&gt; 1 root wheel 1.4K Dec 14 2019 calendar.lotr
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a timeline of the important events happened in &lt;em&gt;The Lord Of The Rings&lt;/em&gt;&lt;sup id="fnref:3"&gt;3&lt;/sup&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/*
 * Lord Of The Rings
 *
 * $FreeBSD: src/usr.bin/calendar/calendars/calendar.lotr,v 1.2 2003/10/09 00:31:48 grog Exp $
 */

#ifndef _calendar_lotr_
#define _calendar_lotr_

01/05   Fellowship enters Moria
01/09   Fellowship reaches Lorien
01/17   Passing of Gandalf
02/07   Fellowship leaves Lorien
02/17   Death of Boromir
02/20   Meriadoc &amp;amp; Pippin meet Treebeard
02/22   Passing of King Elessar
02/24   Ents destroy Isengard
02/26   Aragorn takes the Paths of the Dead
03/05   Frodo &amp;amp; Samwise encounter Shelob
03/08   Deaths of Denethor &amp;amp; Theoden
03/18   Destruction of the Ring
03/29   Flowering of the Mallorn
04/04   Gandalf visits Bilbo
04/17   An unexpected party
04/23   Crowning of King Elessar
05/19   Arwen leaves Lorien to wed King Elessar
06/11   Sauron attacks Osgiliath
06/13   Bilbo returns to Bag End
06/23   Wedding of Elessar &amp;amp; Arwen
07/04   Gandalf imprisoned by Saruman
07/24   The ring comes to Bilbo
07/26   Bilbo rescued from Wargs by Eagles
08/03   Funeral of King Theoden
08/29   Saruman enters the Shire
09/10   Gandalf escapes from Orthanc
09/14   Frodo &amp;amp; Bilbo's birthday
09/15   Black riders enter the Shire
09/18   Frodo and company rescued by Bombadil
09/28   Frodo wounded at Weathertop
10/05   Frodo crosses bridge of Mitheithel
10/16   Boromir reaches Rivendell
10/17   Council of Elrond
10/25   End of War of the Ring
11/16   Bilbo reaches the Lonely Mountain
12/05   Death of Smaug
12/16   Fellowship begins Quest

#endif /* !_calendar_lotr_ */
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Bonus: the missing recipe
&lt;/h2&gt;

&lt;p&gt;In the eariler versions of the macOS, &lt;strong&gt;a hidden cookie recipe&lt;/strong&gt; can be found by typing this command in the Terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Open /usr/share/emacs/22.1/etc/COOKIES
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Unforrunately in the latest macOS this file is gone (together with &lt;code&gt;/usr/share/emacs&lt;/code&gt;). We can only check this out &lt;a href="https://opensource.apple.com/source/emacs/emacs-96/emacs/etc/COOKIES.auto.html"&gt;on the Apple Open Source website&lt;/a&gt;&lt;sup id="fnref:4"&gt;4&lt;/sup&gt;.&lt;/p&gt;




&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;&lt;a href="https://github.com/lattera/freebsd/blob/master/share/misc/flowers"&gt;https://github.com/lattera/freebsd/blob/master/share/misc/flowers&lt;/a&gt; ↩︎&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;&lt;a href="https://github.com/lattera/freebsd/blob/master/share/misc/birthtoken"&gt;https://github.com/lattera/freebsd/blob/master/share/misc/birthtoken&lt;/a&gt; ↩︎&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:3"&gt;
&lt;p&gt;&lt;a href="https://opensource.apple.com/source/misc_cmds/misc_cmds-31/calendar/calendars/calendar.lotr.auto.html"&gt;https://opensource.apple.com/source/misc_cmds/misc_cmds-31/calendar/calendars/calendar.lotr.auto.html&lt;/a&gt; ↩︎&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:4"&gt;
&lt;p&gt;&lt;a href="https://opensource.apple.com/source/emacs/emacs-96/emacs/etc/COOKIES.auto.html"&gt;https://opensource.apple.com/source/emacs/emacs-96/emacs/etc/COOKIES.auto.html&lt;/a&gt; ↩︎&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>mac</category>
      <category>easteregg</category>
    </item>
    <item>
      <title>Battling With Gulp and Node</title>
      <dc:creator>Bemn</dc:creator>
      <pubDate>Sat, 27 Jun 2020 08:03:12 +0000</pubDate>
      <link>https://forem.com/bemnlam/battling-with-gulp-and-node-dl3</link>
      <guid>https://forem.com/bemnlam/battling-with-gulp-and-node-dl3</guid>
      <description>&lt;h2&gt;
  
  
  😨 Problem
&lt;/h2&gt;

&lt;p&gt;When I was building a website using &lt;code&gt;gulp@^3.9.0&lt;/code&gt; to compile sass on the build server with Node.js v12 installed, it failed.&lt;/p&gt;

&lt;p&gt;Here are (part of) the errors shown in the console:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;error   26-Jun-2020 08:35:02    gyp ERR! node -v v12.18.0
error   26-Jun-2020 08:35:02    gyp ERR! node-gyp -v v3.8.0
error   26-Jun-2020 08:35:02    gyp ERR! not ok 
error   26-Jun-2020 08:35:02    Build failed with error code: 1
error   26-Jun-2020 08:35:05    
error   26-Jun-2020 08:35:05    npm ERR! code ELIFECYCLE
error   26-Jun-2020 08:35:05    npm ERR! errno 1
error   26-Jun-2020 08:35:05    npm ERR! node-sass@4.9.3 postinstall: `node scripts/build.js`
error   26-Jun-2020 08:35:05    npm ERR! Exit status 1
error   26-Jun-2020 08:35:05    npm ERR! 
error   26-Jun-2020 08:35:05    npm ERR! Failed at the node-sass@4.9.3 postinstall script.
error   26-Jun-2020 08:35:05    npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(OK I know that’s not your fault, &lt;code&gt;npm&lt;/code&gt;.) Here is my &lt;code&gt;package.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "devDependencies": {
    "gulp": "^3.9.0",
    "gulp-concat": "^2.6.0",
    "gulp-minify-css": "^1.2.4",
    "gulp-rename": "^1.2.2",
    "gulp-sass": "^4.0.2",
    "gulp-sourcemaps": "*"
  }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  😐 &lt;code&gt;node-sass&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;I noticed that should be something wrong with &lt;code&gt;node-sass&lt;/code&gt;, which being used in &lt;code&gt;gulp-sass&lt;/code&gt;. I met this issues before and from my experience, &lt;a href="https://stackoverflow.com/a/45807410/13742790"&gt;&lt;code&gt;node-sass&lt;/code&gt; will try to download the corresponding prebuilt binary&lt;/a&gt; base on your OS or build it locally using &lt;code&gt;python&lt;/code&gt;, &lt;code&gt;MSBuild&lt;/code&gt;, etc… (That’s why you will met lots of questions in Stack Overflow asking &lt;strong&gt;&lt;code&gt;python2&lt;/code&gt; not found when installing &lt;code&gt;node-sass&lt;/code&gt;&lt;/strong&gt; , &lt;strong&gt;what’s wrong with my &lt;code&gt;node-sass&lt;/code&gt;&lt;/strong&gt; or &lt;strong&gt;I got a panic attack when dealing with &lt;code&gt;node-sass&lt;/code&gt; should I consult a developer or a doctor first?&lt;/strong&gt; ).&lt;/p&gt;

&lt;p&gt;For this &lt;code&gt;node-sass&lt;/code&gt; issue, &lt;a href="https://hisk.io/how-to-fix-node-js-gyp-err-cant-find-python-executable-python-on-windows/"&gt;you can try to run this on Windows&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install --global --production windows-build-tools
npm install node-gyp

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or try to delete &lt;code&gt;package-lock.json&lt;/code&gt; and &lt;code&gt;node_modules&lt;/code&gt; first and do a &lt;code&gt;npm install&lt;/code&gt; if you can install all packages successfully on let say Mac OS but failed on Windows.&lt;/p&gt;

&lt;p&gt;👆Those tricks saved me most of the time.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;del&gt;I just want to get my css files back and you told me I have to install this and that and download node npm python ms build tools some prebuilt binaries? Are you serious, node-sass?&lt;/del&gt;
&lt;/h4&gt;

&lt;h3&gt;
  
  
  😑 ReferenceError: primordials is not defined
&lt;/h3&gt;

&lt;p&gt;After the &lt;code&gt;node-sass&lt;/code&gt; issue was solved, the build server ran the build jobs again and got these errors:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;error   26-Jun-2020 08:53:06    fs.js:35
error   26-Jun-2020 08:53:06    } = primordials;
error   26-Jun-2020 08:53:06 ^
error   26-Jun-2020 08:53:06    
error   26-Jun-2020 08:53:06    ReferenceError: primordials is not defined
error   26-Jun-2020 08:53:06 at fs.js:35:5
## ( blah blah blah ) ##
error   26-Jun-2020 08:53:06 at Module._compile (internal/modules/cjs/loader.js:1138:30)
error   26-Jun-2020 08:53:06 at Object.Module._extensions..js (internal/modules/cjs/loader.js:1158:10)
error   26-Jun-2020 08:53:06 at Module.load (internal/modules/cjs/loader.js:986:32)
error   26-Jun-2020 08:53:06 at Function.Module._load (internal/modules/cjs/loader.js:879:14)
error   26-Jun-2020 08:53:06 at Module.require (internal/modules/cjs/loader.js:1026:19)
error   26-Jun-2020 08:53:06 at require (internal/modules/cjs/helpers.js:72:18)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://stackoverflow.com/a/55926692/13742790"&gt;This answer from Stack Overflow&lt;/a&gt; states that it is due to Node.js v12 does not compatible with Gulp v3 &lt;strong&gt;and you need to upgrade Gulp to v4&lt;/strong&gt;. I know &lt;strong&gt;I should&lt;/strong&gt; do that but I also know that I will meet the &lt;a href="https://github.com/sindresorhus/del/issues/45"&gt;Did you forget to signal async completion?&lt;/a&gt; issue which also will cause an epic fail of the build &lt;strong&gt;unless I re-write the gulp tasks&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I don’t want to change my &lt;code&gt;gulpfile.js&lt;/code&gt; and I don’t want to upgrade &lt;code&gt;gulp&lt;/code&gt;. Not now. That’s why I started searching for a solution without changing any configurations of the build server as well as the gulp setup in the project.&lt;/p&gt;

&lt;h2&gt;
  
  
  😀 Solution: adding a &lt;code&gt;npm-shrinkwrap.json&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Eventually I &lt;a href="https://stackoverflow.com/questions/55921442/how-to-fix-referenceerror-primordials-is-not-defined-in-node/58394828#58394828"&gt;found a solution&lt;/a&gt; on how to handle this “Gulp VS Node” situation. What we need is create a &lt;code&gt;npm-shrinkwrap.json&lt;/code&gt; file under the same directory with &lt;code&gt;package.json&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The content of the json file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "dependencies": {
    "graceful-fs": {
      "version": "4.2.3"
    }
  }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After that I can build the project and finish all the gulp tasks without errors 🎉.&lt;/p&gt;

&lt;h3&gt;
  
  
  🤔 So, what’s going on?
&lt;/h3&gt;

&lt;p&gt;From the npm’s official documentation on the &lt;a href="https://docs.npmjs.com/cli/shrinkwrap"&gt;&lt;code&gt;npm-shrinkwrap&lt;/code&gt; command&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This command repurposes package-lock.json into a publishable npm-shrinkwrap.json or simply creates a new one. The file created and updated by this command will then take precedence over any other existing or future package-lock.json files.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And from the documentation on the &lt;a href="https://docs.npmjs.com/files/shrinkwrap.json"&gt;&lt;code&gt;npm-shrinkwrap.json&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;… Additionally, if both &lt;code&gt;package-lock.json&lt;/code&gt; and &lt;code&gt;npm-shrinkwrap.json&lt;/code&gt; are present in a package root, &lt;code&gt;package-lock.json&lt;/code&gt; will be ignored in favor of this file.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In other words, this file has a &lt;em&gt;higher priority&lt;/em&gt; then &lt;code&gt;package-lock.json&lt;/code&gt;. However, why this file can solve the build error?&lt;/p&gt;

&lt;h4&gt;
  
  
  The&lt;code&gt;fs&lt;/code&gt; module
&lt;/h4&gt;

&lt;p&gt;Node’s &lt;code&gt;fs&lt;/code&gt; module got some changes since &lt;strong&gt;v11.15&lt;/strong&gt; which cause the &lt;code&gt;graceful-fs@^3.0.0&lt;/code&gt; package does not work anymore. Unfortunately, &lt;code&gt;gulp@3.9.1&lt;/code&gt; depends on &lt;code&gt;graceful-fs@^3.0.0&lt;/code&gt;. As a result, running the gulp tasks on Node.js v12 will cause the &lt;code&gt;primordials is not defined&lt;/code&gt; error.&lt;/p&gt;

&lt;h4&gt;
  
  
  The fix
&lt;/h4&gt;

&lt;p&gt;After we added the &lt;code&gt;npm-shrinkwrap.json&lt;/code&gt;, from my understanding it locked down the version of package(s) used by the execution environment to the version stated in that file (and ignore the setup in &lt;code&gt;package-lock.json&lt;/code&gt;.In the above case, the &lt;code&gt;npm-shrinkwrap.json&lt;/code&gt; tells Node.js 12 use &lt;code&gt;graceful-fs@4.2.3&lt;/code&gt; instead of &lt;code&gt;graceful-fs@^3.0.0&lt;/code&gt;. This combination works. Meanwhile, the &lt;code&gt;gulp&lt;/code&gt; package will still reference to the &lt;code&gt;package.json&lt;/code&gt; and &lt;code&gt;package-lock.json&lt;/code&gt; file and use the &lt;code&gt;graceful-fs@^3.0.0&lt;/code&gt; package. This combination also works.&lt;/p&gt;

&lt;h2&gt;
  
  
  🎯 Conclusion
&lt;/h2&gt;

&lt;p&gt;I got some build errors when using &lt;code&gt;gulp@^3.9.0&lt;/code&gt; and &lt;code&gt;gulp-sass&lt;/code&gt; under Node.js 12. After I delete the &lt;code&gt;package-lock.json&lt;/code&gt; and re-run &lt;code&gt;npm install&lt;/code&gt;, the sass problem solved. Next, I added a &lt;code&gt;npm-shrinkwrap.json&lt;/code&gt; to (temporarily) solve the incompatable issue with old gulp running on new Node.js.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;del&gt;Can I call this the Node version of &lt;strong&gt;dependency hell&lt;/strong&gt; &lt;sup id="fnref:1"&gt;1&lt;/sup&gt;?&lt;/del&gt;
&lt;/h4&gt;

&lt;h6&gt;
  
  
  🔗 references:
&lt;/h6&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://mejiblog.com/gulp-error/"&gt;ReferenceError: primordials is not defined の解決方法【備忘録】&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mejiblog.com/gulpfile-change/"&gt;Task function must be specified 解決方法【備忘録】&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/mafintosh/prebuildify-ci/issues/5#issuecomment-550867124"&gt;Node 12: Errors with ‘primordials is not defined’ #5&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://blog.icetutor.com/how-to-fix-referenceerror-primordials-is-not-defined-error/"&gt;How to fix “ReferenceError: primordials is not defined” error&lt;/a&gt;


&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;&lt;a href="https://en.m.wikipedia.org/wiki/Dependency_hell"&gt;&lt;/a&gt;&lt;a href="https://en.m.wikipedia.org/wiki/Dependency_hell"&gt;https://en.m.wikipedia.org/wiki/Dependency_hell&lt;/a&gt; ↩︎&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>node</category>
      <category>gulp</category>
      <category>sass</category>
    </item>
    <item>
      <title>🐞 GitVersioning: 'ThisAssembly' Is Inaccessible Due to Its Protection Level</title>
      <dc:creator>Bemn</dc:creator>
      <pubDate>Thu, 25 Jun 2020 09:10:24 +0000</pubDate>
      <link>https://forem.com/bemnlam/gitversioning-thisassembly-is-inaccessible-due-to-its-protection-level-30l8</link>
      <guid>https://forem.com/bemnlam/gitversioning-thisassembly-is-inaccessible-due-to-its-protection-level-30l8</guid>
      <description>&lt;h2&gt;
  
  
  😨 Problem
&lt;/h2&gt;

&lt;p&gt;After I installed the &lt;strong&gt;&lt;a href="https://www.nuget.org/packages/Nerdbank.GitVersioning"&gt;Nerdbank.GitVersioning&lt;/a&gt;&lt;/strong&gt; Nuget package in my .NET MVC app, the following error came out when I want to get the version using &lt;code&gt;ThisAssembly.AssemblyInformationalVersion&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;error CS0122: &lt;span class="s1"&gt;'ThisAssembly'&lt;/span&gt; is inaccessible due to its protection level
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I tried to install the package across all the projects in the same solution. It didn’t work.&lt;/p&gt;

&lt;p&gt;I tried to uninstall and re-ininstall the package. It didn’t work.&lt;/p&gt;

&lt;h2&gt;
  
  
  😀 Solution
&lt;/h2&gt;

&lt;h3&gt;
  
  
  .csproj
&lt;/h3&gt;

&lt;p&gt;Turns out there is something missing in my &lt;code&gt;.csproj&lt;/code&gt; file. I missed an &lt;code&gt;&amp;lt;Import&amp;gt;&lt;/code&gt; tag at the end of the file, just before the &lt;code&gt;&amp;lt;/Target&amp;gt;&lt;/code&gt; tag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;Import&lt;/span&gt; &lt;span class="na"&gt;Project=&lt;/span&gt;&lt;span class="s"&gt;"..\packages\Nerdbank.GitVersioning.3.1.91\build\Nerdbank.GitVersioning.targets"&lt;/span&gt; &lt;span class="na"&gt;Condition=&lt;/span&gt;&lt;span class="s"&gt;"Exists('..\packages\Nerdbank.GitVersioning.3.1.91\build\Nerdbank.GitVersioning.targets')"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And in the &lt;code&gt;EnsureNuGetPackageBuildImports&lt;/code&gt; target, add the following line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;Error&lt;/span&gt; &lt;span class="na"&gt;Condition=&lt;/span&gt;&lt;span class="s"&gt;"!Exists('..\packages\Nerdbank.GitVersioning.3.1.91\build\Nerdbank.GitVersioning.targets')"&lt;/span&gt; &lt;span class="na"&gt;Text=&lt;/span&gt;&lt;span class="s"&gt;"$([System.String]::Format('$(ErrorText)', '..\packages\Nerdbank.GitVersioning.3.1.91\build\Nerdbank.GitVersioning.targets'))"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In addition, the 1st &lt;code&gt;&amp;lt;PropertyGroup&amp;gt;&lt;/code&gt; should contain a pair of &lt;code&gt;NuGetPackageImportStamp&lt;/code&gt; tag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;NuGetPackageImportStamp&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/NuGetPackageImportStamp&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  AssemblyInfo.cs
&lt;/h3&gt;

&lt;p&gt;I also removed the following lines in &lt;code&gt;Properties/AssemblyInfo.cs&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;assembly&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;AssemblyVersion&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"1.0.0.0"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;assembly&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;AssemblyFileVersion&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"1.0.0.0"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  version.json
&lt;/h3&gt;

&lt;p&gt;At the root directory of the &lt;strong&gt;project&lt;/strong&gt;, I added a &lt;code&gt;version.json&lt;/code&gt; file with the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"$schema"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0.0"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After that, &lt;code&gt;ThisAssembly&lt;/code&gt; is back and I can read the git version info successfully.&lt;/p&gt;

&lt;h6&gt;
  
  
  🔗 references:
&lt;/h6&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/dotnet/Nerdbank.GitVersioning/blob/master/doc/dotnet.md"&gt;https://github.com/dotnet/Nerdbank.GitVersioning/blob/master/doc/dotnet.md&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/dotnet/Nerdbank.GitVersioning/issues/449"&gt;https://github.com/dotnet/Nerdbank.GitVersioning/issues/449&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/dotnet/Nerdbank.GitVersioning/issues/404"&gt;https://github.com/dotnet/Nerdbank.GitVersioning/issues/404&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h6&gt;
  
  
  🖼 cover image:
&lt;/h6&gt;

&lt;p&gt;&lt;a href="https://www.businessinsider.com.au/harvard-mark-i-grace-hopper-bug-2015-7"&gt;Grace Hopper’s operational logbook for the Harvard Mark II computer&lt;/a&gt;&lt;/p&gt;

</description>
      <category>c</category>
      <category>netcore</category>
      <category>nuget</category>
    </item>
    <item>
      <title>A Tale of Two Caches: Redis and the cache helper</title>
      <dc:creator>Bemn</dc:creator>
      <pubDate>Thu, 11 Jun 2020 13:06:59 +0000</pubDate>
      <link>https://forem.com/bemnlam/a-tale-of-two-caches-redis-and-the-cache-helper-n57</link>
      <guid>https://forem.com/bemnlam/a-tale-of-two-caches-redis-and-the-cache-helper-n57</guid>
      <description>&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;Recently our team started a new project: a showcase page under our main website. The website is read-only and the content won’t change frequently so we can have an aggressive caching policy.&lt;/p&gt;

&lt;p&gt;I built this &lt;strong&gt;MVC&lt;/strong&gt; web app using &lt;strong&gt;.NET Core 3.1&lt;/strong&gt; and deploy it as an IIS sub-site under the main website (which is a &lt;strong&gt;.NET Framework&lt;/strong&gt; web app running on the IIS).&lt;/p&gt;

&lt;h3&gt;
  
  
  Table of Content
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
Redis

&lt;ul&gt;
&lt;li&gt;NuGet package&lt;/li&gt;
&lt;li&gt;appsettings.json&lt;/li&gt;
&lt;li&gt;Startup.cs&lt;/li&gt;
&lt;li&gt;CacheService&lt;/li&gt;
&lt;li&gt;Controller&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
Cache Tag Helper

&lt;ul&gt;
&lt;li&gt;Example&lt;/li&gt;
&lt;li&gt;Explaination&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Bonus: A note on @helper and other HTML helpers&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Redis
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Why?
&lt;/h3&gt;

&lt;p&gt;We are using &lt;a href="https://redis.io/"&gt;Redis&lt;/a&gt; because it is simple, fast and we are already using it across all the main websites.&lt;/p&gt;

&lt;h3&gt;
  
  
  How?
&lt;/h3&gt;

&lt;p&gt;Here are some highlights:&lt;/p&gt;

&lt;h4&gt;
  
  
  1. NuGet packages
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class="na"&gt;Include=&lt;/span&gt;&lt;span class="s"&gt;"StackExchange.Redis"&lt;/span&gt; &lt;span class="na"&gt;Version=&lt;/span&gt;&lt;span class="s"&gt;"2.1.30"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class="na"&gt;Include=&lt;/span&gt;&lt;span class="s"&gt;"StackExchange.Redis.Extensions.Core"&lt;/span&gt; &lt;span class="na"&gt;Version=&lt;/span&gt;&lt;span class="s"&gt;"6.1.7"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class="na"&gt;Include=&lt;/span&gt;&lt;span class="s"&gt;"StackExchange.Redis.Extensions.Newtonsoft"&lt;/span&gt; &lt;span class="na"&gt;Version=&lt;/span&gt;&lt;span class="s"&gt;"6.1.7"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;PackageReference&lt;/span&gt; &lt;span class="na"&gt;Include=&lt;/span&gt;&lt;span class="s"&gt;"StackExchange.Redis.Extensions.AspNetCore"&lt;/span&gt; &lt;span class="na"&gt;Version=&lt;/span&gt;&lt;span class="s"&gt;"6.1.7"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;StackExchange.Redis.Extensions.Newtonsoft&lt;/code&gt; is optional. &lt;a href="https://devblogs.microsoft.com/dotnet/try-the-new-system-text-json-apis/"&gt;Start from .NET Core 3.0&lt;/a&gt; the default Json serializer will be &lt;code&gt;System.Text.Json&lt;/code&gt;. If you want to use &lt;code&gt;Newtonsoft.Json&lt;/code&gt; then you will need this package in your project.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;StackExchange.Redis.Extensions.Core&lt;/code&gt; and &lt;code&gt;StackExchange.Redis.Extensions.AspNetCore&lt;/code&gt; are the useful package to connect/read/write Redis easier. Read &lt;a href="https://stackexchange-redis-extensinos.gitbook.io/stackexchange-redis-extensions/"&gt;this documentation&lt;/a&gt; for more details.&lt;/p&gt;

&lt;h4&gt;
  
  
  2. appsettings.json
&lt;/h4&gt;

&lt;p&gt;A typical .NET Core project should have an &lt;code&gt;appsettings.json&lt;/code&gt;. Add the following section:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Redis"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"AllowAdmin"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Ssl"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ConnectTimeout"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;6000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ConnectRetry"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Database"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Hosts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"Host"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"my-secret-redis-host.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"Port"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"6379"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, &lt;code&gt;my-secret-redis-host.com&lt;/code&gt; is the Redis host and We are using the database no. &lt;code&gt;0&lt;/code&gt;. You can set multiple hosts. You can see a detailed configuration &lt;a href="https://stackexchange-redis-extensinos.gitbook.io/stackexchange-redis-extensions/configuration/json-configuration"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  3. Startup.cs
&lt;/h4&gt;

&lt;p&gt;Add the following code in &lt;code&gt;ConfigureServices()&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;redisConfiguration&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetSection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Redis"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;RedisConfiguration&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddStackExchangeRedisExtensions&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;NewtonsoftSerializer&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;redisConfiguration&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  5. CacheService
&lt;/h4&gt;

&lt;p&gt;I created a &lt;code&gt;CacheService.cs&lt;/code&gt; to help me reading/writing data in Redis. In this service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;CacheService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RedisConfiguration&lt;/span&gt; &lt;span class="n"&gt;redisConfiguration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ILogger&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;RedisCacheConnectionPoolManager&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;poolLogger&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;connectionPoolManager&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;RedisCacheConnectionPoolManager&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;redisConfiguration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;poolLogger&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;_redisClient&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;RedisCacheClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;connectionPoolManager&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;serializer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;redisConfiguration&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="cm"&gt;/* something wrong when connection to Redis servers. */&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;_cacheDuration&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;300&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// cache period in seconds&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We need a method to write data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;AddAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;object&lt;/span&gt; &lt;span class="k"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;added&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_redisClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetDbFromConfiguration&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;AddAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;DateTimeOffset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddSeconds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_cacheDuration&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;added&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="cm"&gt;/* something wrong when writing data to Redis */&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And we need a method to get cached data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;TryGetAsync&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_redisClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetDbFromConfiguration&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;ExistsAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_redisClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetDbFromConfiguration&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;GetAsync&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="cm"&gt;/* something wrong when writing data to Redis */&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I intentionally name this method &lt;code&gt;TryGetAsync()&lt;/code&gt; because the cache may not exist or already expired when you try to get it from Redis.&lt;/p&gt;

&lt;p&gt;After that, let’s go back to &lt;code&gt;Startup.cs&lt;/code&gt; and register this service in &lt;code&gt;ConfigureService()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddTransient&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CacheService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Remember to register this service &lt;strong&gt;after&lt;/strong&gt; &lt;code&gt;services.AddStackExchangeRedisExtensions()&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  5. Controller
&lt;/h4&gt;

&lt;p&gt;Inject the &lt;code&gt;CacheService&lt;/code&gt; to the controller:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;DemoController&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CacheService&lt;/span&gt; &lt;span class="n"&gt;cacheService&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;_cacheService&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cacheService&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IActionResult&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Demo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;cacheKey&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;$"DemoApp:&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Try to get cached value from Redis.&lt;/span&gt;
    &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;cachedResult&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_cacheService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TryGetAsync&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;cacheKey&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;cachedResult&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;View&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cachedResult&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Add a new entry to Redis before returning the message.&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;$"Hello, &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;sections&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;sections&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_cacheService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cacheKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;View&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Explain Like I’m Five&lt;/strong&gt; :&lt;/p&gt;

&lt;p&gt;You ask the shopkeeper in &lt;code&gt;Demo&lt;/code&gt; bookstore do they have a specific book &lt;code&gt;name&lt;/code&gt;. First, the shopkeeper looks for the book on the bookshelf named &lt;code&gt;Redis&lt;/code&gt;. If he finds that book, he takes it out and gives it to you.&lt;/p&gt;

&lt;p&gt;If your book does not exist in the &lt;code&gt;Redis&lt;/code&gt; bookstore, he has to go out and buy that book for you(!). However, he buys 2 identical copies. He gives you one and puts the other one on the &lt;code&gt;Redis&lt;/code&gt; bookshelf, just in case another customer want that book later.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Cache Tag Helper
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://docs.microsoft.com/en-us/aspnet/core/mvc/views/tag-helpers/built-in/cache-tag-helper?view=aspnetcore-3.1"&gt;Cache Tag Helper&lt;/a&gt; is a tag that you can use in a &lt;strong&gt;.NET Core MVC&lt;/strong&gt; app. Content encolsed by this &lt;code&gt;&amp;lt;cache&amp;gt;&lt;/code&gt; tag will be cached in the internal cache provider.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;cache&lt;/span&gt; &lt;span class="na"&gt;expires-after=&lt;/span&gt;&lt;span class="s"&gt;"@TimeSpan.FromSeconds(60)"&lt;/span&gt; 
       &lt;span class="na"&gt;vary-by-route=&lt;/span&gt;&lt;span class="s"&gt;"name"&lt;/span&gt; 
       &lt;span class="na"&gt;vary-by-user=&lt;/span&gt;&lt;span class="s"&gt;"false"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    @System.DateTime.Now
&lt;span class="nt"&gt;&amp;lt;/cache&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Explaination
&lt;/h3&gt;

&lt;p&gt;In the above example, some attributes is set in the &lt;code&gt;&amp;lt;cache&amp;gt;&lt;/code&gt; tag:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;expires-after&lt;/code&gt;: how long (in seconds) will this cache last for.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;vary-by-route&lt;/code&gt;: different copy will be cached when the route has a different value in the &lt;code&gt;name&lt;/code&gt;param.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;vary-by-user&lt;/code&gt;: different user will see different cached copies.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  How can I know if it is working?
&lt;/h3&gt;

&lt;p&gt;You will see the value rendered in the above example won’t change for 60 seconds even &lt;code&gt;System.DateTime.Now&lt;/code&gt; should show the current time.&lt;/p&gt;




&lt;h2&gt;
  
  
  Bonus: A note on &lt;code&gt;@helper&lt;/code&gt; and other HTML helpers
&lt;/h2&gt;

&lt;p&gt;In the old days we can define some &lt;code&gt;@helper&lt;/code&gt; functions in the razor view and (re)use it in the view. It’s being removed since .NET Core 3.0 because the design of &lt;code&gt;@helper&lt;/code&gt; function does not compatible with async Razor content anymore.&lt;/p&gt;

&lt;h3&gt;
  
  
  Successor of the HTML helpers?
&lt;/h3&gt;

&lt;p&gt;You can use the &lt;a href="https://docs.microsoft.com/en-us/aspnet/core/mvc/views/tag-helpers/intro?view=aspnetcore-3.1"&gt;&lt;strong&gt;Tag Helpers in ASP.NET Core&lt;/strong&gt;&lt;/a&gt;. Yes, the &lt;code&gt;&amp;lt;cache&amp;gt;&lt;/code&gt; Tag Helper is one of the &lt;a href="https://docs.microsoft.com/en-us/aspnet/core/mvc/views/tag-helpers/intro?view=aspnetcore-3.1#built-in-aspnet-core-tag-helpers"&gt;built-in Tag Helpers in .NET Core&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In addition, you can use the &lt;a href="https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.rendering.htmlhelperpartialextensions.partialasync?view=aspnetcore-3.1"&gt;&lt;code&gt;PartialAsync()&lt;/code&gt; method&lt;/a&gt; to render the partial HTML markup &lt;strong&gt;asynchronously&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;@await&lt;/span&gt; &lt;span class="n"&gt;Html&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;PartialAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"_PartialName"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;&lt;strong&gt;More references on the HTML helpers and Tag Helpers:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/aspnet/Mvc/issues/4127"&gt;What happened to the @helper directive in Razor ?&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/aspnet/Razor/issues/281"&gt;Remove the @helper directive from Razor&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dannyvanderkraan.wordpress.com/2016/04/19/asp-net-core-1-0-goodbye-html-helpers-and-hello-taghelpers/"&gt;ASP.NET Core 1.0: Goodbye HTML helpers and hello TagHelpers!&lt;/a&gt;&lt;/p&gt;

</description>
      <category>c</category>
      <category>aspnetcore</category>
      <category>redis</category>
      <category>cache</category>
    </item>
    <item>
      <title>Creating a user with limited privileges in Postgres</title>
      <dc:creator>Bemn</dc:creator>
      <pubDate>Wed, 15 Apr 2020 15:03:52 +0000</pubDate>
      <link>https://forem.com/bemnlam/creating-a-user-with-limited-privileges-in-postgres-5ee7</link>
      <guid>https://forem.com/bemnlam/creating-a-user-with-limited-privileges-in-postgres-5ee7</guid>
      <description>&lt;h2&gt;
  
  
  Situation
&lt;/h2&gt;

&lt;p&gt;I created a web app. It reads data from a PostgreSQL database. I want to create a user with read-only privilege and connect to the database.&lt;/p&gt;

&lt;h3&gt;
  
  
  Code
&lt;/h3&gt;

&lt;p&gt;Let say the user you want to create is &lt;code&gt;webapp_user&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;USER&lt;/span&gt; &lt;span class="n"&gt;webapp_user&lt;/span&gt; &lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="k"&gt;ENCRYPTED&lt;/span&gt; &lt;span class="n"&gt;PASSWORD&lt;/span&gt; &lt;span class="s1"&gt;'secure_passw0rd'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Grant the right to all public tables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;GRANT&lt;/span&gt; &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="k"&gt;ALL&lt;/span&gt; &lt;span class="n"&gt;TABLES&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="k"&gt;SCHEMA&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;webapp_user&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Connect the database using &lt;code&gt;webapp_user&lt;/code&gt; in the web app.&lt;/p&gt;

&lt;h2&gt;
  
  
  I want more power...
&lt;/h2&gt;

&lt;p&gt;Now my web app is not read-only anymore. Users can create/delete some entries in the tables too.&lt;/p&gt;

&lt;h3&gt;
  
  
  Code
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;GRANT&lt;/span&gt; &lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;INSERT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;UPDATE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;DELETE&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="k"&gt;ALL&lt;/span&gt; &lt;span class="n"&gt;TABLES&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="k"&gt;SCHEMA&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;webapp_user&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Looks simple and cool! But why I got the &lt;strong&gt;&lt;code&gt;permission denied for sequence XXX_id_seq to YYY&lt;/code&gt;&lt;/strong&gt; error while inserting new records to the tables?&lt;/p&gt;

&lt;p&gt;If your table has an &lt;em&gt;auto-increment&lt;/em&gt; field then you need some extra privileges: the privileges to use the sequence-related functions (e.g. &lt;code&gt;currval&lt;/code&gt; and &lt;code&gt;nextval&lt;/code&gt;)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;GRANT&lt;/span&gt; &lt;span class="k"&gt;USAGE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="k"&gt;ALL&lt;/span&gt; &lt;span class="n"&gt;SEQUENCES&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="k"&gt;SCHEMA&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;to&lt;/span&gt; &lt;span class="n"&gt;webapp_user&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example of an &lt;em&gt;auto-increment&lt;/em&gt; field: the id of the table&lt;/p&gt;

&lt;h3&gt;
  
  
  Extra
&lt;/h3&gt;

&lt;p&gt;What if I just want to apply the privilege(s) to a special table?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;GRANT&lt;/span&gt; &lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;INSERT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;UPDATE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;DELETE&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;special_table&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;webapp_user&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Reference:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.techonthenet.com/postgresql/grant_revoke.php"&gt;Grant Privileges on Table&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stackoverflow.com/q/9325017"&gt;ERROR: permission denied for sequence cities_id_seq using Postgres&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>postgres</category>
    </item>
  </channel>
</rss>
