<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title/><link>https://tonny0531.github.io/</link><description>Recent content on</description><generator>Hugo -- gohugo.io</generator><lastBuildDate>Tue, 01 Aug 2023 14:40:21 +0900</lastBuildDate><atom:link href="https://tonny0531.github.io/index.xml" rel="self" type="application/rss+xml"/><item><title>InputkeyEvent</title><link>https://tonny0531.github.io/post/angular/inputkeyevent/</link><pubDate>Tue, 01 Aug 2023 14:40:21 +0900</pubDate><guid>https://tonny0531.github.io/post/angular/inputkeyevent/</guid><description>&lt;h1 id="introduction">Introduction&lt;/h1>
&lt;p>I always forget how can i listen input event. So I write this post.&lt;/p>
&lt;h1 id="listening-event">Listening Event&lt;/h1>
&lt;h2 id="with-out-angular">With out Angular&lt;/h2>
&lt;p>With out Angular framework we have to use javascript to bind event like this code&lt;/p>
&lt;div class="highlight">&lt;pre class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">input&lt;/span> &lt;span class="na">type&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;text&amp;#34;&lt;/span> &lt;span class="na">id&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;myInput&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="nb">document&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">getElementById&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;myInput&amp;#39;&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">addEventListener&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;keyup&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">myInput&lt;/span>&lt;span class="p">);&lt;/span>
&lt;span class="kd">function&lt;/span> &lt;span class="nx">myInput&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">input&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">input&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">keyCode&lt;/span>&lt;span class="p">);&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;a class="link" href="https://developer.mozilla.org/en-US/docs/Web/Events#event_listing" target="_blank" rel="noopener"
>EventList&lt;/a>&lt;/p>
&lt;p>&lt;a class="link" href="https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode#constants_for_keycode_value" target="_blank" rel="noopener"
>KeyCodeRef&lt;/a>&lt;/p>
&lt;p>&lt;a class="link" href="https://stackblitz.com/edit/web-platform-ucnqqm?file=script.js,index.html" target="_blank" rel="noopener"
>SourceCode&lt;/a>&lt;/p>
&lt;h2 id="with-angular">With Angular&lt;/h2>
&lt;p>With Angular framework is more easier to bind event&lt;/p>
&lt;div class="highlight">&lt;pre class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">input&lt;/span> &lt;span class="na">type&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;text&amp;#34;&lt;/span> &lt;span class="err">(&lt;/span>&lt;span class="na">keyup&lt;/span>&lt;span class="err">.&lt;/span>&lt;span class="na">enter&lt;/span>&lt;span class="err">)=&amp;#34;&lt;/span>&lt;span class="na">onEnter&lt;/span>&lt;span class="err">()&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre class="chroma">&lt;code class="language-javascript" data-lang="javascript"> &lt;span class="nx">onEnter&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="k">void&lt;/span> &lt;span class="p">{&lt;/span>
&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;submit&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;a class="link" href="https://stackblitz.com/edit/stackblitz-starters-hkywok?file=src%2Fmain.ts" target="_blank" rel="noopener"
>SourceCode&lt;/a>&lt;/p>
&lt;p>you can change another key like &lt;code>shift&lt;/code> or anthoer you want&lt;/p></description></item><item><title>NUnit Test Project Init</title><link>https://tonny0531.github.io/post/dotnet/test/init/</link><pubDate>Sun, 09 Apr 2023 13:55:48 +0900</pubDate><guid>https://tonny0531.github.io/post/dotnet/test/init/</guid><description>&lt;h1 id="前言">前言&lt;/h1>
&lt;p>前面已經有寫了幾篇關於解耦合，抽象行為等等概念的說明了．&lt;/p>
&lt;p>而建置測試專案雖然已經做了第二次了，但發現兩次我都會忘記怎麼做，因此才特別寫了這篇初始化的文章出來&lt;/p>
&lt;h1 id="碰到問題">碰到問題&lt;/h1>
&lt;p>在.NET Core 開發的過程中，都知道有IServiceProvider和IConfiguration這兩個介面可以取得實例或是Config的配置&lt;/p>
&lt;p>但正常情況下都被框架包好了，根本不用去考慮他怎麼做出來的．&lt;/p>
&lt;p>但在測試的途中，框架不會知道你是否要使用，因此也不會自動幫你建立一個出來，導致我們需要自己來建立．&lt;/p>
&lt;h1 id="建立專案">建立專案&lt;/h1>
&lt;p>由於我現在的開發環境是 Mac OS 因此這裡就只示範 VSCode &amp;amp; .NET CLI&lt;/p>
&lt;div class="highlight">&lt;pre class="chroma">&lt;code class="language-shell" data-lang="shell">dotnet new nunit -n &lt;span class="o">{&lt;/span>ProjectName&lt;span class="o">}&lt;/span>
&lt;/code>&lt;/pre>&lt;/div>&lt;p>如果是6版本以上的話，會自動建立&lt;strong>using.cs&lt;/strong>&lt;/p>
&lt;p>這個是GloableUsing的部分，如果有全域要使用的using的話可以考慮放到這裡&lt;/p>
&lt;h1 id="nunit的介紹">Nunit的介紹&lt;/h1>
&lt;p>正常來講在寫整合測試的時候，我們為了減少變因，因此都會做一次初始化以及完畢的行為&lt;/p>
&lt;p>例如：初始化建立相依的物件(DB,Redis等等相關連線)&lt;/p>
&lt;p>完畢的時候要刪除測試的資料等等行為&lt;/p>
&lt;p>而在NUnit中的屬性則叫做Setup(初始) &amp;amp; TearDown(結束)&lt;/p>
&lt;p>實際上寫出來會長得像這樣&lt;/p>
&lt;div class="highlight">&lt;pre class="chroma">&lt;code class="language-C#" data-lang="C#">&lt;span class="na"> [SetUp]&lt;/span>
&lt;span class="k">public&lt;/span> &lt;span class="k">void&lt;/span> &lt;span class="n">SetUp&lt;/span>&lt;span class="p">()&lt;/span>
&lt;span class="p">{&lt;/span>
&lt;span class="c1">//初始化區塊
&lt;/span>&lt;span class="c1">&lt;/span> &lt;span class="p">}&lt;/span>
&lt;span class="na">
&lt;/span>&lt;span class="na"> [TearDown]&lt;/span>
&lt;span class="k">public&lt;/span> &lt;span class="k">void&lt;/span> &lt;span class="n">TearDown&lt;/span>&lt;span class="p">()&lt;/span>
&lt;span class="p">{&lt;/span>
&lt;span class="c1">//測試完成後執行的區塊
&lt;/span>&lt;span class="c1">&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/code>&lt;/pre>&lt;/div>&lt;h1 id="建立iserviceprovider--iconfiguration">建立IServiceProvider &amp;amp; IConfiguration&lt;/h1>
&lt;p>建立方式其實沒有很困難，實際方式如下&lt;/p>
&lt;div class="highlight">&lt;pre class="chroma">&lt;code class="language-C#" data-lang="C#"> &lt;span class="k">protected&lt;/span> &lt;span class="n">IServiceCollection&lt;/span> &lt;span class="n">_services&lt;/span>&lt;span class="p">;&lt;/span>
&lt;span class="k">protected&lt;/span> &lt;span class="n">IServiceProvider&lt;/span> &lt;span class="n">_sp&lt;/span>&lt;span class="p">;&lt;/span>
&lt;span class="k">protected&lt;/span> &lt;span class="n">IConfiguration&lt;/span> &lt;span class="n">_configuration&lt;/span>&lt;span class="p">;&lt;/span>
&lt;span class="na">
&lt;/span>&lt;span class="na"> [SetUp]&lt;/span>
&lt;span class="k">public&lt;/span> &lt;span class="k">void&lt;/span> &lt;span class="n">SetUp&lt;/span>&lt;span class="p">()&lt;/span>
&lt;span class="p">{&lt;/span>
&lt;span class="c1">// 先建立新的ServiceCollection
&lt;/span>&lt;span class="c1">&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">_services&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="n">ServiceCollection&lt;/span>&lt;span class="p">();&lt;/span>
&lt;span class="c1">// 使用檔案進行Config抓取
&lt;/span>&lt;span class="c1">&lt;/span> &lt;span class="kt">var&lt;/span> &lt;span class="n">configuration&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="n">ConfigurationBuilder&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="n">AddJsonFile&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;appsettings.Test.json&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">_configuration&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">configuration&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Build&lt;/span>&lt;span class="p">();&lt;/span>
&lt;span class="c1">//add MongoDB
&lt;/span>&lt;span class="c1">&lt;/span> &lt;span class="kt">var&lt;/span> &lt;span class="n">appSettingsSection_MongoDB&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">_configuration&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">GetSection&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;MongoDbConfig&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;span class="kt">var&lt;/span> &lt;span class="n">appSettingsMongoDB&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">appSettingsSection_MongoDB&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Get&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="n">DbConfig&lt;/span>&lt;span class="p">&amp;gt;();&lt;/span>
&lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">_services&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">AddScoped&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="n">IMongoStorage&lt;/span>&lt;span class="p">&amp;gt;(&lt;/span>&lt;span class="n">sp&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="n">MongoStorage&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">appSettingsMongoDB&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="p">});&lt;/span>
&lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">_services&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">AddSingleton&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="n">IMongoFileStorage&lt;/span>&lt;span class="p">&amp;gt;(&lt;/span>&lt;span class="n">sp&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="n">MongoFileStorage&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">appSettingsMongoDB&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="p">});&lt;/span>
&lt;span class="c1">// 創建出ServiceProvider
&lt;/span>&lt;span class="c1">&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">_sp&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">_services&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">BuildServiceProvider&lt;/span>&lt;span class="p">();&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="na">
&lt;/span>&lt;span class="na"> [TearDown]&lt;/span>
&lt;span class="k">public&lt;/span> &lt;span class="k">void&lt;/span> &lt;span class="n">TearDown&lt;/span>&lt;span class="p">()&lt;/span>
&lt;span class="p">{&lt;/span>
&lt;span class="n">IMongoStorage&lt;/span> &lt;span class="n">db&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">_sp&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">GetRequiredService&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="n">IMongoStorage&lt;/span>&lt;span class="p">&amp;gt;();&lt;/span>
&lt;span class="n">Assert&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">AreEqual&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;TestDB&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="n">db&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">GetDB&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="n">DatabaseNamespace&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">DatabaseName&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;DB Name must be TestDB&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;span class="n">BsonDocument&lt;/span> &lt;span class="n">empty&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="n">BsonDocument&lt;/span>&lt;span class="p">();&lt;/span>
&lt;span class="n">DeleteResult&lt;/span> &lt;span class="n">deleteResult&lt;/span>&lt;span class="p">;&lt;/span>
&lt;span class="n">deleteResult&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">db&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">GetCollection&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="n">UserInfo&lt;/span>&lt;span class="p">&amp;gt;(&lt;/span>&lt;span class="s">&amp;#34;UserInfo&amp;#34;&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="n">DeleteMany&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">empty&lt;/span>&lt;span class="p">);&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;/code>&lt;/pre>&lt;/div>&lt;p>當中比較需要特別注意的是 &lt;strong>appsettings.Test.json&lt;/strong> 會需要在csproj裡面特別說明要輸出到輸出目錄，否則會抓不到Config導致報錯&lt;/p>
&lt;div class="highlight">&lt;pre class="chroma">&lt;code class="language-xml" data-lang="xml"> &lt;span class="nt">&amp;lt;ItemGroup&amp;gt;&lt;/span>
&lt;span class="nt">&amp;lt;None&lt;/span> &lt;span class="na">Update=&lt;/span>&lt;span class="s">&amp;#34;appsettings.Test.json&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>
&lt;span class="nt">&amp;lt;CopyToOutputDirectory&amp;gt;&lt;/span>Always&lt;span class="nt">&amp;lt;/CopyToOutputDirectory&amp;gt;&lt;/span>
&lt;span class="nt">&amp;lt;/None&amp;gt;&lt;/span>
&lt;span class="nt">&amp;lt;/ItemGroup&amp;gt;&lt;/span>
&lt;/code>&lt;/pre>&lt;/div>&lt;p>而因為這些比較算是每個測試都會使用的Init 所以其實可以抽成一個BasicTestClass 後續有相關測試的Class可以直接繼承使用，就不用每個Class都寫一次了．&lt;/p>
&lt;h1 id="總結">總結&lt;/h1>
&lt;p>測試其實不難，真正難的會是在開發過程中的設計，如何設計商業邏輯以及低耦合才是最困難的點．&lt;/p></description></item><item><title>Dependency Injection 與工廠模式的結合(2)</title><link>https://tonny0531.github.io/post/dotnet/di/factorywork/</link><pubDate>Thu, 06 Apr 2023 12:48:19 +0900</pubDate><guid>https://tonny0531.github.io/post/dotnet/di/factorywork/</guid><description>&lt;h1 id="前言">前言&lt;/h1>
&lt;p>前面一篇提到了 DI 的概念，透過 DI 的方式來實現解耦合，而這篇則是要來講 DI 的一些進階應用了&lt;/p>
&lt;h1 id="工廠模式">工廠模式&lt;/h1>
&lt;p>先說明工廠模式的運作方式，直接看Code比較快&lt;/p>
&lt;p>&lt;a class="link" href="https://en.wikipedia.org/wiki/Factory_method_pattern" target="_blank" rel="noopener"
>資料來源&lt;/a>&lt;/p>
&lt;div class="highlight">&lt;pre class="chroma">&lt;code class="language-C#" data-lang="C#">&lt;span class="c1">// Empty vocabulary of actual object
&lt;/span>&lt;span class="c1">&lt;/span>&lt;span class="k">public&lt;/span> &lt;span class="k">interface&lt;/span> &lt;span class="n">IPerson&lt;/span>
&lt;span class="p">{&lt;/span>
&lt;span class="kt">string&lt;/span> &lt;span class="n">GetName&lt;/span>&lt;span class="p">();&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="k">public&lt;/span> &lt;span class="k">class&lt;/span> &lt;span class="nc">Villager&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">IPerson&lt;/span>
&lt;span class="p">{&lt;/span>
&lt;span class="k">public&lt;/span> &lt;span class="kt">string&lt;/span> &lt;span class="n">GetName&lt;/span>&lt;span class="p">()&lt;/span>
&lt;span class="p">{&lt;/span>
&lt;span class="k">return&lt;/span> &lt;span class="s">&amp;#34;Village Person&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="k">public&lt;/span> &lt;span class="k">class&lt;/span> &lt;span class="nc">CityPerson&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">IPerson&lt;/span>
&lt;span class="p">{&lt;/span>
&lt;span class="k">public&lt;/span> &lt;span class="kt">string&lt;/span> &lt;span class="n">GetName&lt;/span>&lt;span class="p">()&lt;/span>
&lt;span class="p">{&lt;/span>
&lt;span class="k">return&lt;/span> &lt;span class="s">&amp;#34;City Person&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="k">public&lt;/span> &lt;span class="k">enum&lt;/span> &lt;span class="n">PersonType&lt;/span>
&lt;span class="p">{&lt;/span>
&lt;span class="n">Rural&lt;/span>&lt;span class="p">,&lt;/span>
&lt;span class="n">Urban&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="c1">/// &amp;lt;summary&amp;gt;
&lt;/span>&lt;span class="c1">/// Implementation of Factory - Used to create objects.
&lt;/span>&lt;span class="c1">/// &amp;lt;/summary&amp;gt;
&lt;/span>&lt;span class="c1">&lt;/span>&lt;span class="k">public&lt;/span> &lt;span class="k">class&lt;/span> &lt;span class="nc">Factory&lt;/span>
&lt;span class="p">{&lt;/span>
&lt;span class="k">public&lt;/span> &lt;span class="n">IPerson&lt;/span> &lt;span class="n">GetPerson&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">PersonType&lt;/span> &lt;span class="n">type&lt;/span>&lt;span class="p">)&lt;/span>
&lt;span class="p">{&lt;/span>
&lt;span class="k">switch&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">type&lt;/span>&lt;span class="p">)&lt;/span>
&lt;span class="p">{&lt;/span>
&lt;span class="k">case&lt;/span> &lt;span class="n">PersonType&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Rural&lt;/span>&lt;span class="p">:&lt;/span>
&lt;span class="k">return&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="n">Villager&lt;/span>&lt;span class="p">();&lt;/span>
&lt;span class="k">case&lt;/span> &lt;span class="n">PersonType&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Urban&lt;/span>&lt;span class="p">:&lt;/span>
&lt;span class="k">return&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="n">CityPerson&lt;/span>&lt;span class="p">();&lt;/span>
&lt;span class="k">default&lt;/span>&lt;span class="p">:&lt;/span>
&lt;span class="k">throw&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="n">NotSupportedException&lt;/span>&lt;span class="p">();&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;/code>&lt;/pre>&lt;/div>&lt;p>這個案例裡面有兩個角色，分別是&lt;/p>
&lt;ul>
&lt;li>Rural&lt;/li>
&lt;li>Urban&lt;/li>
&lt;/ul>
&lt;p>而我只要告訴工廠，現在是哪種人，工廠就會建立相對應的實例會來給我．&lt;/p>
&lt;p>而我也不用關心他的細節是什麼，只要關心他有提供什麼方法即可(Interface)&lt;/p>
&lt;blockquote>
&lt;p>封裝的概念：隱藏細節，注重提供什麼&lt;/p>
&lt;/blockquote>
&lt;h1 id="di工廠">DI工廠&lt;/h1>
&lt;p>其實就只是將工廠做成Interface 放進 DIPool中&lt;/p>
&lt;h2 id="personmodel">PersonModel&lt;/h2>
&lt;div class="highlight">&lt;pre class="chroma">&lt;code class="language-C#" data-lang="C#">&lt;span class="k">namespace&lt;/span> &lt;span class="nn">DelegateFactory.Model&lt;/span>
&lt;span class="p">{&lt;/span>
&lt;span class="k">public&lt;/span> &lt;span class="k">interface&lt;/span> &lt;span class="n">IPerson&lt;/span>
&lt;span class="p">{&lt;/span>
&lt;span class="kt">string&lt;/span> &lt;span class="n">GetName&lt;/span>&lt;span class="p">();&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="k">public&lt;/span> &lt;span class="k">class&lt;/span> &lt;span class="nc">Villager&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">IPerson&lt;/span>
&lt;span class="p">{&lt;/span>
&lt;span class="k">public&lt;/span> &lt;span class="kt">string&lt;/span> &lt;span class="n">GetName&lt;/span>&lt;span class="p">()&lt;/span>
&lt;span class="p">{&lt;/span>
&lt;span class="k">return&lt;/span> &lt;span class="s">&amp;#34;Village Person&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="k">public&lt;/span> &lt;span class="k">class&lt;/span> &lt;span class="nc">CityPerson&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">IPerson&lt;/span>
&lt;span class="p">{&lt;/span>
&lt;span class="k">public&lt;/span> &lt;span class="kt">string&lt;/span> &lt;span class="n">GetName&lt;/span>&lt;span class="p">()&lt;/span>
&lt;span class="p">{&lt;/span>
&lt;span class="k">return&lt;/span> &lt;span class="s">&amp;#34;City Person&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="k">public&lt;/span> &lt;span class="k">enum&lt;/span> &lt;span class="n">PersonType&lt;/span>
&lt;span class="p">{&lt;/span>
&lt;span class="n">Rural&lt;/span>&lt;span class="p">,&lt;/span>
&lt;span class="n">Urban&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="factoryclass">FactoryClass&lt;/h2>
&lt;div class="highlight">&lt;pre class="chroma">&lt;code class="language-C#" data-lang="C#">&lt;span class="k">namespace&lt;/span> &lt;span class="nn">DelegateFactory.Factory&lt;/span>
&lt;span class="p">{&lt;/span>
&lt;span class="k">public&lt;/span> &lt;span class="k">interface&lt;/span> &lt;span class="n">IFactory&lt;/span>
&lt;span class="p">{&lt;/span>
&lt;span class="n">IPerson&lt;/span> &lt;span class="n">GetPerson&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">PersonType&lt;/span> &lt;span class="n">type&lt;/span>&lt;span class="p">);&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="k">public&lt;/span> &lt;span class="k">class&lt;/span> &lt;span class="nc">PersonFactory&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">IFactory&lt;/span>
&lt;span class="p">{&lt;/span>
&lt;span class="k">public&lt;/span> &lt;span class="n">IPerson&lt;/span> &lt;span class="n">GetPerson&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">PersonType&lt;/span> &lt;span class="n">type&lt;/span>&lt;span class="p">)&lt;/span>
&lt;span class="p">{&lt;/span>
&lt;span class="k">switch&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">type&lt;/span>&lt;span class="p">)&lt;/span>
&lt;span class="p">{&lt;/span>
&lt;span class="k">case&lt;/span> &lt;span class="n">PersonType&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Rural&lt;/span>&lt;span class="p">:&lt;/span>
&lt;span class="k">return&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="n">Villager&lt;/span>&lt;span class="p">();&lt;/span>
&lt;span class="k">case&lt;/span> &lt;span class="n">PersonType&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Urban&lt;/span>&lt;span class="p">:&lt;/span>
&lt;span class="k">return&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="n">CityPerson&lt;/span>&lt;span class="p">();&lt;/span>
&lt;span class="k">default&lt;/span>&lt;span class="p">:&lt;/span>
&lt;span class="k">throw&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="n">NotSupportedException&lt;/span>&lt;span class="p">();&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="controller">Controller&lt;/h2>
&lt;div class="highlight">&lt;pre class="chroma">&lt;code class="language-C#" data-lang="C#">&lt;span class="k">namespace&lt;/span> &lt;span class="nn">DelegateFactory.Controllers&lt;/span>&lt;span class="p">;&lt;/span>
&lt;span class="na">
&lt;/span>&lt;span class="na">[ApiController]&lt;/span>
&lt;span class="na">[Route(&amp;#34;[controller]&lt;/span>&lt;span class="s">&amp;#34;)]
&lt;/span>&lt;span class="s">&lt;/span>&lt;span class="k">public&lt;/span> &lt;span class="k">class&lt;/span> &lt;span class="nc">WeatherForecastController&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">ControllerBase&lt;/span>
&lt;span class="p">{&lt;/span>
&lt;span class="k">private&lt;/span> &lt;span class="k">readonly&lt;/span> &lt;span class="n">ILogger&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="n">WeatherForecastController&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="n">_logger&lt;/span>&lt;span class="p">;&lt;/span>
&lt;span class="k">private&lt;/span> &lt;span class="k">readonly&lt;/span> &lt;span class="n">IFactory&lt;/span> &lt;span class="n">_factory&lt;/span>&lt;span class="p">;&lt;/span>
&lt;span class="k">public&lt;/span> &lt;span class="n">WeatherForecastController&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ILogger&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="n">WeatherForecastController&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="n">logger&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">IFactory&lt;/span> &lt;span class="n">factory&lt;/span>&lt;span class="p">)&lt;/span>
&lt;span class="p">{&lt;/span>
&lt;span class="n">_logger&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">logger&lt;/span>&lt;span class="p">;&lt;/span>
&lt;span class="n">_factory&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">factory&lt;/span>&lt;span class="p">;&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="na">
&lt;/span>&lt;span class="na"> [HttpGet(&amp;#34;name/{role}&amp;#34;)]&lt;/span>
&lt;span class="k">public&lt;/span> &lt;span class="n">ActionResult&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="kt">string&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="n">GetName&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">string&lt;/span> &lt;span class="n">role&lt;/span>&lt;span class="p">)&lt;/span>
&lt;span class="p">{&lt;/span>
&lt;span class="kt">bool&lt;/span> &lt;span class="n">isEnumRole&lt;/span> &lt;span class="p">=&lt;/span>&lt;span class="n">Enum&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">TryParse&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">role&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="k">out&lt;/span> &lt;span class="n">PersonType&lt;/span> &lt;span class="n">personType&lt;/span>&lt;span class="p">);&lt;/span>
&lt;span class="k">if&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">isEnumRole&lt;/span>&lt;span class="p">){&lt;/span>
&lt;span class="kt">string&lt;/span> &lt;span class="n">name&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">_factory&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">GetPerson&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">personType&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="n">GetName&lt;/span>&lt;span class="p">();&lt;/span>
&lt;span class="k">return&lt;/span> &lt;span class="n">Ok&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="p">);&lt;/span>
&lt;span class="p">}&lt;/span>&lt;span class="k">else&lt;/span>&lt;span class="p">{&lt;/span>
&lt;span class="k">return&lt;/span> &lt;span class="n">BadRequest&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;Unknow Role&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;/code>&lt;/pre>&lt;/div>&lt;p>這樣起起來就有一個很基本的工廠模式，透過傳進來的 Role 來決定實際上拿到哪個實例並回傳&lt;/p>
&lt;h1 id="結論">結論&lt;/h1>
&lt;p>透過這樣的方式可以大幅的增加擴充的彈性，但相對的因為抽象，也會導致使用上會變有些不方便，例如:Debug，Code Review 等會變成有些困難&lt;/p>
&lt;p>因此是否要採用這種方式還需要特別根據團隊能力或商務邏輯來決定是否要採用&lt;/p>
&lt;h1 id="sample-code">Sample Code&lt;/h1>
&lt;p>&lt;a class="link" href="Github" >https://github.com/tonny0531/DIFactory&lt;/a>&lt;/p></description></item><item><title>Dependency Injection 與工廠模式的結合(1)</title><link>https://tonny0531.github.io/post/dotnet/di/agenda/</link><pubDate>Wed, 15 Feb 2023 12:35:19 +0800</pubDate><guid>https://tonny0531.github.io/post/dotnet/di/agenda/</guid><description>&lt;h1 id="dependency-injection-簡介">Dependency Injection 簡介&lt;/h1>
&lt;p>Dependency Injection 是一種物件改變耦合的一種設計概念，通常會跟 IOC(Inversion Of Control) 一起談&lt;/p>
&lt;p>以下一個簡單的注入範例&lt;/p>
&lt;h2 id="範例">範例&lt;/h2>
&lt;h3 id="故事">故事&lt;/h3>
&lt;p>父母今天要教育小孩科目，但一對父母並不是只會擁有一個小孩，可能會有多個。因此我們把小孩當作參數傳給教育小孩這個方法，使其變得有彈性。&lt;/p>
&lt;p>方法中注入小孩其實就是 &lt;code>方法注入(Method Injection)&lt;/code>&lt;/p>
&lt;p>創建小孩的過程會注入一個姓名，這種注入方式就是 &lt;code>建構式注入(Constructor Injection)&lt;/code>&lt;/p>
&lt;p>透過物件設定值的方式，就是 &lt;code>屬性注入(Property Injection)&lt;/code>&lt;/p>
&lt;h3 id="struct">Struct&lt;/h3>
&lt;div class="highlight">&lt;pre class="chroma">&lt;code class="language-C#" data-lang="C#">&lt;span class="k">public&lt;/span> &lt;span class="k">interface&lt;/span> &lt;span class="n">IParent&lt;/span> &lt;span class="p">{&lt;/span>
&lt;span class="k">void&lt;/span> &lt;span class="n">TeachChild&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">IClild&lt;/span> &lt;span class="n">child&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Subject&lt;/span> &lt;span class="kt">string&lt;/span>&lt;span class="p">);&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="k">public&lt;/span> &lt;span class="k">class&lt;/span> &lt;span class="nc">Parent&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">IParent&lt;/span> &lt;span class="p">{&lt;/span>
&lt;span class="k">public&lt;/span> &lt;span class="n">Parent&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="p">}&lt;/span>
&lt;span class="k">public&lt;/span> &lt;span class="k">void&lt;/span> &lt;span class="n">TeachChild&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">IClild&lt;/span> &lt;span class="n">child&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">string&lt;/span> &lt;span class="n">subject&lt;/span> &lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;span class="n">Console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">WriteLine&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">$&amp;#34;Teach {child.GetName} about ${subject}&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="k">public&lt;/span> &lt;span class="k">interface&lt;/span> &lt;span class="n">IChild&lt;/span> &lt;span class="p">{&lt;/span>
&lt;span class="kt">string&lt;/span> &lt;span class="n">GetName&lt;/span> &lt;span class="p">();&lt;/span>
&lt;span class="k">void&lt;/span> &lt;span class="n">SetAge&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span> &lt;span class="n">age&lt;/span>&lt;span class="p">);&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="k">public&lt;/span> &lt;span class="k">class&lt;/span> &lt;span class="nc">Child&lt;/span> &lt;span class="p">{&lt;/span>
&lt;span class="k">private&lt;/span> &lt;span class="kt">string&lt;/span> &lt;span class="n">_childName&lt;/span>&lt;span class="p">;&lt;/span>
&lt;span class="k">private&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="n">_age&lt;/span>&lt;span class="p">;&lt;/span>
&lt;span class="k">public&lt;/span> &lt;span class="n">Child&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">string&lt;/span> &lt;span class="n">name&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">_childName&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">name&lt;/span>&lt;span class="p">;&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="k">public&lt;/span> &lt;span class="kt">string&lt;/span> &lt;span class="n">GetName&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;span class="k">return&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">_childName&lt;/span>&lt;span class="p">;&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="k">public&lt;/span> &lt;span class="k">void&lt;/span> &lt;span class="n">SetAge&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span> &lt;span class="n">age&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">_age&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">age&lt;/span>&lt;span class="p">;&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="net-的特別之處">.NET 的特別之處&lt;/h2>
&lt;p>當我們知道 DI 之後，就要來細講 NET 的 DI 概念了&lt;/p>
&lt;p>在 NET 的架構下，會建議改用框架預設的 IServiceCollection(IServiceProvider)&lt;/p>
&lt;p>來取代手動 DI ，而透過 IServiceProvider 取出來的物件&lt;/p>
&lt;p>概念如下圖&lt;/p>
&lt;p>&lt;figure
class="gallery-image"
style="
flex-grow: 119;
flex-basis: 287px"
>
&lt;a href="https://tonny0531.github.io/post/dotnet/di/agenda/NET_DI.png" data-size="328x274">
&lt;img src="https://tonny0531.github.io/post/dotnet/di/agenda/NET_DI.png"
width="328"
height="274"
srcset="https://tonny0531.github.io/post/dotnet/di/agenda/NET_DI_hu704ad0c493591872dee22994b14d263b_9651_480x0_resize_box_2.png 480w, https://tonny0531.github.io/post/dotnet/di/agenda/NET_DI_hu704ad0c493591872dee22994b14d263b_9651_1024x0_resize_box_2.png 1024w"
loading="lazy"
alt="NETDI">
&lt;/a>
&lt;figcaption>NETDI&lt;/figcaption>
&lt;/figure>&lt;/p>
&lt;p>先將所有的 Service 註冊到資料提供中心，如果有其他物件需要就在從資料提供中心中取出來&lt;/p>
&lt;p>這樣的好處是 Service 跟 其他物件的耦合性，會變成是跟資料提供中心耦合（詳細概念可參考前篇的&lt;a class="link" href="https://tonny0531.github.io/post/developer/abstract_interface/" >AbstractClass比較Interface
&lt;/a>)&lt;/p>
&lt;p>而在 Service 註冊的時候，會有三種特性，這三種特性會改變資料提供中心對外的時候，是否要產生新的物件(生命週期）給需求者，分別是&lt;/p>
&lt;ul>
&lt;li>Singleton： 在 APP 的生命週期中，只會存在一個實例，因此每次取回都是相同的記憶體位置&lt;/li>
&lt;li>Scoped： 在一個 Requeset 的過程中僅在第一次建立新的實例，第二次開始會取得與第一次同樣的實例&lt;/li>
&lt;li>Transient：每次要都取回一個新的實例&lt;/li>
&lt;/ul>
&lt;h2 id="結語">結語&lt;/h2>
&lt;p>透過 DI 的方式可以解決物件之間高耦合的問題，以利未來要做調整或抽象會更加簡單，而下一篇再來說明怎麼透過原生的 DI Pull 達成工廠模式&lt;/p>
&lt;h2 id="參考資料">參考資料&lt;/h2>
&lt;p>&lt;a class="link" href="https://learn.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-7.0" target="_blank" rel="noopener"
>NETDI&lt;/a>&lt;/p></description></item><item><title>Docker Image Certificate 信任問題</title><link>https://tonny0531.github.io/post/dotnet/certificate/</link><pubDate>Wed, 08 Feb 2023 11:31:27 +0800</pubDate><guid>https://tonny0531.github.io/post/dotnet/certificate/</guid><description>&lt;h1 id="前言">前言&lt;/h1>
&lt;p>繼上一篇做了 Kestrel Port 的大概說明後，後來又發生了一個狀況&lt;/p>
&lt;p>因那個 App 會呼叫公司內部的 API 而公司內的API採用了 Https 引發了憑證不信任的問題&lt;/p>
&lt;p>以下有兩種避免的作法&lt;/p>
&lt;ol>
&lt;li>HttpClient 注入 HttpClientHandler&lt;/li>
&lt;/ol>
&lt;pre>&lt;code>services.AddHttpClient&amp;lt;JY_COG_FactoryWorkClient&amp;gt;().ConfigurePrimaryHttpMessageHandler(sp =&amp;gt;
{
return sp.GetRequiredService&amp;lt;SSLClientHandler&amp;gt;();
});
public class SSLClientHandler : HttpClientHandler
{
public SSLClientHandler()
{
ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;
}
}
&lt;/code>&lt;/pre>&lt;ol start="2">
&lt;li>在 Docker Image 下新增信任憑證&lt;/li>
&lt;/ol>
&lt;blockquote>
&lt;p>目前看起來 Dotnet Core Image Kernel 是使用 Ubuntu
&lt;a class="link" href="https://ubuntu.com/server/docs/security-trust-store" target="_blank" rel="noopener"
>參考Ubuntu官網&lt;/a>&lt;/p>
&lt;/blockquote>
&lt;pre>&lt;code>$ sudo apt-get install -y ca-certificates #預設已經有了
$ sudocp local-ca.crt /usr/local/share/ca-certificates #複製自己的憑證進去
$ sudo update-ca-certificates # 系統更新憑證
&lt;/code>&lt;/pre>&lt;p>而因為我偷懶不想要每次匯，因此就寫在 DockerFile 中&lt;/p>
&lt;pre>&lt;code>FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base
WORKDIR /app
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
WORKDIR /src
COPY . .
RUN dotnet restore &amp;quot;./{ProjectName}.csproj&amp;quot;
RUN dotnet build &amp;quot;{ProjectName}.csproj&amp;quot; -c Release -o /app/build
FROM build AS publish
RUN dotnet publish &amp;quot;{ProjectName}.csproj&amp;quot; -c Release -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish . #複製編譯完成結果
COPY root04.crt /usr/local/share/ca-certificates #複製憑證
RUN update-ca-certificates #更新憑證
ENTRYPOINT [&amp;quot;dotnet&amp;quot;, &amp;quot;{ProjectName}.dll&amp;quot;]
&lt;/code>&lt;/pre>&lt;p>只是因為剛好這個 Image 算好找憑證匯入方式，若是其他Linux版本更新就要找個版本的更新方式了&lt;/p></description></item><item><title>Kestrel監聽Port的設定方式</title><link>https://tonny0531.github.io/post/dotnet/kestrel_config/</link><pubDate>Wed, 08 Feb 2023 10:02:24 +0800</pubDate><guid>https://tonny0531.github.io/post/dotnet/kestrel_config/</guid><description>&lt;h1 id="前言">前言&lt;/h1>
&lt;p>.NET 會紅的一部份很大原因是因為終於實現了跨平台&lt;/p>
&lt;p>而原先相依於 IIS 的 HttpServer 也因為跨平台而改採用較小的 Kestrel&lt;/p>
&lt;blockquote>
&lt;p>也是可以託管給IIS: &lt;a class="link" href="https://learn.microsoft.com/zh-tw/aspnet/core/host-and-deploy/iis/out-of-process-hosting?view=aspnetcore-7.0" target="_blank" rel="noopener"
>詳細參考&lt;/a>&lt;/p>
&lt;/blockquote>
&lt;p>而會寫這篇最大的原因還是因為，使用了微軟官方的 DockerImage 進行包裝後，發現都還是 Kestrel 都只會吃到 80 Port&lt;/p>
&lt;p>&lt;figure
class="gallery-image"
style="
flex-grow: 492;
flex-basis: 1182px"
>
&lt;a href="https://tonny0531.github.io/post/dotnet/kestrel_config/defaultListen.png" data-size="990x201">
&lt;img src="https://tonny0531.github.io/post/dotnet/kestrel_config/defaultListen.png"
width="990"
height="201"
srcset="https://tonny0531.github.io/post/dotnet/kestrel_config/defaultListen_hu17bedbbe1e2da0a67de4f9c7c0300a30_36876_480x0_resize_box_2.png 480w, https://tonny0531.github.io/post/dotnet/kestrel_config/defaultListen_hu17bedbbe1e2da0a67de4f9c7c0300a30_36876_1024x0_resize_box_2.png 1024w"
loading="lazy"
alt="DefaultListen80">
&lt;/a>
&lt;figcaption>DefaultListen80&lt;/figcaption>
&lt;/figure>&lt;/p>
&lt;p>而網路上爬了一些文，後來找到&lt;a class="link" href="https://github.com/dotnet/AspNetCore.Docs/blob/main/aspnetcore/fundamentals/servers/kestrel.md#configureiconfiguration" target="_blank" rel="noopener"
>官方說明&lt;/a>綁定的以下幾種方式&lt;/p>
&lt;ol>
&lt;li>No configuration&lt;/li>
&lt;/ol>
&lt;ul>
&lt;li>預設為 &lt;code>http:localhost:5000&lt;/code> &amp;amp; &lt;code>https:localhost:5001&lt;/code>&lt;/li>
&lt;/ul>
&lt;ol start="2">
&lt;li>使用系統環境變數&lt;/li>
&lt;/ol>
&lt;ul>
&lt;li>使用&lt;code>ASPNETCORE_URLS&lt;/code> 來決定使用的Port&lt;/li>
&lt;/ul>
&lt;blockquote>
&lt;p>Docker 設定的預設為80，導致此次會發生此問題&lt;/p>
&lt;/blockquote>
&lt;ol start="3">
&lt;li>使用參數來決定&lt;/li>
&lt;/ol>
&lt;ul>
&lt;li>使用 &lt;code>dotnet XXX.dll --urls http://+:1234&lt;/code>&lt;/li>
&lt;/ul>
&lt;ol start="4">
&lt;li>寫死在程式碼中(不推薦)&lt;/li>
&lt;/ol>
&lt;pre>&lt;code>webBuilder.UseKestrel((context, serverOptions) =&amp;gt;
{
serverOptions.Configure(context.Configuration.GetSection(&amp;quot;Kestrel&amp;quot;))
.Endpoint(&amp;quot;HTTPS&amp;quot;, listenOptions =&amp;gt;
{
listenOptions.HttpsOptions.SslProtocols = SslProtocols.Tls12;
});
});
&lt;/code>&lt;/pre>&lt;ol start="5">
&lt;li>透過 Appsetting.json 來設定(推薦)
&lt;figure
class="gallery-image"
style="
flex-grow: 212;
flex-basis: 511px"
>
&lt;a href="https://tonny0531.github.io/post/dotnet/kestrel_config/Appsettings.png" data-size="560x263">
&lt;img src="https://tonny0531.github.io/post/dotnet/kestrel_config/Appsettings.png"
width="560"
height="263"
srcset="https://tonny0531.github.io/post/dotnet/kestrel_config/Appsettings_hu0e30c9dad62195e7a8b92f32bb104e2e_9670_480x0_resize_box_2.png 480w, https://tonny0531.github.io/post/dotnet/kestrel_config/Appsettings_hu0e30c9dad62195e7a8b92f32bb104e2e_9670_1024x0_resize_box_2.png 1024w"
loading="lazy"
alt="Appsettings">
&lt;/a>
&lt;figcaption>Appsettings&lt;/figcaption>
&lt;/figure>&lt;/li>
&lt;/ol>
&lt;p>設定完成後包起來就會發現已經吃到 Config 了
&lt;figure
class="gallery-image"
style="
flex-grow: 686;
flex-basis: 1646px"
>
&lt;a href="https://tonny0531.github.io/post/dotnet/kestrel_config/StartSuccess.png" data-size="789x115">
&lt;img src="https://tonny0531.github.io/post/dotnet/kestrel_config/StartSuccess.png"
width="789"
height="115"
srcset="https://tonny0531.github.io/post/dotnet/kestrel_config/StartSuccess_hu08a8c5880df06e8d9839208ec9f7a933_19043_480x0_resize_box_2.png 480w, https://tonny0531.github.io/post/dotnet/kestrel_config/StartSuccess_hu08a8c5880df06e8d9839208ec9f7a933_19043_1024x0_resize_box_2.png 1024w"
loading="lazy"
alt="Success">
&lt;/a>
&lt;figcaption>Success&lt;/figcaption>
&lt;/figure>&lt;/p></description></item><item><title>KeyValuePipe使用細節</title><link>https://tonny0531.github.io/post/angular/keyvaluepipe/</link><pubDate>Mon, 15 Aug 2022 09:23:48 +0800</pubDate><guid>https://tonny0531.github.io/post/angular/keyvaluepipe/</guid><description>&lt;h1 id="前言">前言&lt;/h1>
&lt;p>Angular 提供了相當於 Object.keys 的 Pipe 給大家使用，但有個細節卻常常被忽略&lt;/p>
&lt;p>就是排序的行為，在官方預設會進行排序&lt;/p>
&lt;h1 id="舉例">舉例&lt;/h1>
&lt;p>預期的資料如下&lt;/p>
&lt;ul>
&lt;li>創建中&lt;/li>
&lt;li>取消或未過審&lt;/li>
&lt;li>募資中&lt;/li>
&lt;li>募資成功&lt;/li>
&lt;li>募資失敗&lt;/li>
&lt;/ul>
&lt;p>在開發的過程中想像應該也會是一樣的顯示才對&lt;/p>
&lt;p>但實質上會變成&lt;/p>
&lt;ul>
&lt;li>創建中&lt;/li>
&lt;li>&lt;code>募資失敗&lt;/code>&lt;/li>
&lt;li>募資中&lt;/li>
&lt;li>募資成功&lt;/li>
&lt;li>&lt;code>取消或未過審&lt;/code>&lt;/li>
&lt;/ul>
&lt;p>可以看到其中兩筆順序已被對調&lt;/p>
&lt;p>回頭去看官方的 Pipe 的用法說明&lt;/p>
&lt;pre>&lt;code>
{{ input_expression | keyvalue [ : compareFn ] }}
&lt;/code>&lt;/pre>&lt;p>後方會有 compareFn 可以注入 而固定回傳 0 就會變成原始資料了&lt;/p>
&lt;p>&lt;a class="link" href="https://stackblitz.com/edit/angular-ivy-jusse6?file=src%2Fapp%2Fapp.component.ts" target="_blank" rel="noopener"
>SampleCode&lt;/a>&lt;/p>
&lt;h1 id="參考資料">參考資料&lt;/h1>
&lt;ul>
&lt;li>&lt;a class="link" href="https://stackoverflow.com/questions/52793944/angular-keyvalue-pipe-sort-properties-iterate-in-order" target="_blank" rel="noopener"
>angular keyvalue pipe sort properties / iterate in order&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://angular.io/api/common/KeyValuePipe" target="_blank" rel="noopener"
>KevValuePipe&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>AbstractClass比較Interface</title><link>https://tonny0531.github.io/post/developer/abstract_interface/</link><pubDate>Fri, 25 Mar 2022 15:59:54 +0800</pubDate><guid>https://tonny0531.github.io/post/developer/abstract_interface/</guid><description>&lt;h1 id="前言">前言&lt;/h1>
&lt;p>最近在看同事們在分享 OOP(Object-oriented programming) 的觀念，在過程中他特別提到了&lt;strong>抽象&lt;/strong>&lt;/p>
&lt;p>也是我今天寫這篇的原因，在過程中我假設了一種情境來討論物件應該怎麼切分比較好&lt;/p>
&lt;p>這裡要說明，這個討論的過程中的程式語言是以 Dotnet 來說明的，因此有些地方並不一定是用在每種語言上&lt;/p>
&lt;h1 id="專有名詞">專有名詞&lt;/h1>
&lt;h2 id="耦合coupling">耦合(Coupling)&lt;/h2>
&lt;p>在我的認知裡面，耦合是指物件跟物件之間的關係，若高耦合的話，很高機會改了其中一個物件&lt;/p>
&lt;p>導致另外一個物件故障&lt;/p>
&lt;blockquote>
&lt;p>也就是業界常說的改 A 壞 B&lt;/p>
&lt;/blockquote>
&lt;h2 id="抽象類別abstract-class">抽象類別(Abstract Class)&lt;/h2>
&lt;h3 id="特性">特性&lt;/h3>
&lt;ul>
&lt;li>不可以直接透過 New 來建立物件&lt;/li>
&lt;li>可以訂定抽象方法，強迫繼承的物件去實驗方法&lt;/li>
&lt;li>可以寫共用方法的邏輯在抽象類別層&lt;/li>
&lt;/ul>
&lt;h2 id="介面interface">介面(Interface)&lt;/h2>
&lt;h3 id="特性-1">特性&lt;/h3>
&lt;ul>
&lt;li>不可以實作，僅僅只是是規範而已&lt;/li>
&lt;li>繼承的Class必須要實現規範才可當作成此介面&lt;/li>
&lt;/ul>
&lt;blockquote>
&lt;p>C#9.0打破了這個特性，但我不太能接受就是了&lt;/p>
&lt;/blockquote>
&lt;h1 id="情境">情境&lt;/h1>
&lt;p>有一個班級，裡面有學生和老師&lt;/p>
&lt;p>我們要定義出班級裡面的學生和老師&lt;/p>
&lt;p>學生和老師都有開始工作(上課)、結束工作(下課)&lt;/p>
&lt;p>並且由班級來決定是否開始工作(上課)&lt;/p>
&lt;h1 id="物件切分">物件切分&lt;/h1>
&lt;p>依據情境來看，我們可以切出有三個主要物件&lt;/p>
&lt;ul>
&lt;li>班級&lt;/li>
&lt;li>學生&lt;/li>
&lt;li>老師&lt;/li>
&lt;/ul>
&lt;p>&lt;figure
>
&lt;a href="http://www.plantuml.com/plantuml/png/SoWkIImgAStDuKhEIImkLd06aLpLNDeGLhnJQdfcdeQcDLp0DIy5nAs5gWykAKr9pIi1wmZFByg6Qu3oqTM7f2OcPwGM4Q6UonciGS56LnTk2q88Rf13QbuAqFa0" >
&lt;img src="http://www.plantuml.com/plantuml/png/SoWkIImgAStDuKhEIImkLd06aLpLNDeGLhnJQdfcdeQcDLp0DIy5nAs5gWykAKr9pIi1wmZFByg6Qu3oqTM7f2OcPwGM4Q6UonciGS56LnTk2q88Rf13QbuAqFa0"
loading="lazy"
alt="物件圖開始">
&lt;/a>
&lt;figcaption>物件圖開始&lt;/figcaption>
&lt;/figure>&lt;/p>
&lt;p>由上方可見班級對老師、學生來說是耦合的，很高機會改動了學生這個物件，會連帶影響到班級&lt;/p>
&lt;p>最後面再來解決物件耦合的問題&lt;/p>
&lt;h1 id="第一次抽象">第一次抽象&lt;/h1>
&lt;p>基於老師與學生都是人的情況下，我們可以抽出一個抽象類別叫做 &lt;strong>People&lt;/strong>&lt;/p>
&lt;p>並在 People 裡面定義開始工作與結束工作的抽象方法，待繼承的人實作&lt;/p>
&lt;p>&lt;figure
>
&lt;a href="http://www.plantuml.com/plantuml/png/SoWkIImgAStDuKhEIImkLd06aLpLNDeGLhnJQdfcdeOcdE-QbmBarN8b9XMN52KcboW4fEONvAI2TS35mlEBic4wqSLX9a3j2oufJKdDAr58hIX9pKifHZ8EpHHicaDI4vCpKej8rSpb3DQjOAEh2zFY0DGF30G0" >
&lt;img src="http://www.plantuml.com/plantuml/png/SoWkIImgAStDuKhEIImkLd06aLpLNDeGLhnJQdfcdeOcdE-QbmBarN8b9XMN52KcboW4fEONvAI2TS35mlEBic4wqSLX9a3j2oufJKdDAr58hIX9pKifHZ8EpHHicaDI4vCpKej8rSpb3DQjOAEh2zFY0DGF30G0"
loading="lazy"
alt="第一次抽象">
&lt;/a>
&lt;figcaption>第一次抽象&lt;/figcaption>
&lt;/figure>&lt;/p>
&lt;h2 id="好處">好處&lt;/h2>
&lt;p>若未來有共用的方法要實作我只要修改 People 即可&lt;/p>
&lt;h2 id="壞處">壞處&lt;/h2>
&lt;p>根據前面的類別圖可以看到 People 和 Class 仍是高耦合的情況&lt;/p>
&lt;blockquote>
&lt;p>People、Student、Teacher 本身就是同類，因此不算是耦合&lt;/p>
&lt;/blockquote>
&lt;h1 id="第二次抽象解耦合">第二次抽象(解耦合)&lt;/h1>
&lt;p>這邊解耦合的方法會採用 Interface 的方式來進行解耦合&lt;/p>
&lt;p>&lt;figure
>
&lt;a href="http://www.plantuml.com/plantuml/png/bP2n2i9038RtUugCIkaRE1IBkw3WAF4QjTXU9Kb14D_TzAed5XUNaF_8zrr88OgiVTiutw08b4DrTvUDtNv9fphCvYclGcNfuLmTbFY8dc3NyONOcA59G0nf6W-YZ5vXJTsr8QXRgoq5bG-UGPhwCatUatHSUADzPHwWcrAevFL1r4qiF-0jeJyJ_mkxCjAmoFFt-eNnTiKd" >
&lt;img src="http://www.plantuml.com/plantuml/png/bP2n2i9038RtUugCIkaRE1IBkw3WAF4QjTXU9Kb14D_TzAed5XUNaF_8zrr88OgiVTiutw08b4DrTvUDtNv9fphCvYclGcNfuLmTbFY8dc3NyONOcA59G0nf6W-YZ5vXJTsr8QXRgoq5bG-UGPhwCatUatHSUADzPHwWcrAevFL1r4qiF-0jeJyJ_mkxCjAmoFFt-eNnTiKd"
loading="lazy"
alt="第二次抽象">
&lt;/a>
&lt;figcaption>第二次抽象&lt;/figcaption>
&lt;/figure>&lt;/p>
&lt;p>Class 與 People 的相依變成了一個抽象的 Interface ，後續不管 People 底下的類別怎麼變更，只要符合 Worker 的規範，就都不會影響到 Class 導致編譯錯誤而綁手綁腳的&lt;/p>
&lt;p>而若要做測試，也可以在 Woker 那層建立一個 FakeWoker 就可以透過注入的方式來實現 Mock、Stub 了&lt;/p>
&lt;h1 id="結語">結語&lt;/h1>
&lt;p>透過上述的抽象，可以很明顯看出 Interface 才是可以真正拿出來解耦合的&lt;/p>
&lt;p>只要下面實作的類別抽換了，仍然可以工作，反而抽象類別是無法進行抽換的。&lt;/p></description></item><item><title>MongoDb Lookup 使用紀錄</title><link>https://tonny0531.github.io/post/mongodb/lookup/</link><pubDate>Fri, 18 Mar 2022 15:54:51 +0800</pubDate><guid>https://tonny0531.github.io/post/mongodb/lookup/</guid><description>&lt;h1 id="主旨">主旨&lt;/h1>
&lt;p>最近幾天工作上都在使用 MongoDB，但常常忘記一些指令的格式&lt;/p>
&lt;p>因此特別紀錄一下，並說明一下怎麼使用&lt;/p>
&lt;h1 id="lookup">Lookup&lt;/h1>
&lt;p>在 MongoDB 初期並沒有所謂的 Join&lt;/p>
&lt;p>只有單純的 Collection Search&lt;/p>
&lt;p>但由於單一文本最大限制 16MB 的存在 And 物件管理上的設計&lt;/p>
&lt;p>而衍伸出了 Lookup 這個 Operator&lt;/p>
&lt;p>&lt;strong>可以理解成他就是 Sql 的 Join&lt;/strong>&lt;/p>
&lt;pre>&lt;code>{
$lookup:
{
from: &amp;lt;foreign collection&amp;gt;,
localField: &amp;lt;field from local collection's documents&amp;gt;,
foreignField: &amp;lt;field from foreign collection's documents&amp;gt;,
let: { &amp;lt;var_1&amp;gt;: &amp;lt;expression&amp;gt;, …, &amp;lt;var_n&amp;gt;: &amp;lt;expression&amp;gt; },
pipeline: [ &amp;lt;pipeline to run&amp;gt; ],
as: &amp;lt;output array field&amp;gt;
}
}
&lt;/code>&lt;/pre>&lt;ul>
&lt;li>from: 關聯到哪個Collection&lt;/li>
&lt;li>localField: 當前Collection的欄位名稱&lt;/li>
&lt;li>foreignField: 關聯到Collection的欄位名稱&lt;/li>
&lt;li>let: 相當於 Sql 的要甚麼欄位並且命名&lt;/li>
&lt;li>pipeline: 可以在關聯到的資料庫多做一次Pipline Ex: $match、$gt(e)&amp;hellip;.&lt;/li>
&lt;li>as: 查詢出來的新欄位名稱&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="k">select&lt;/span> &lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">let&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="k">as&lt;/span> &lt;span class="k">where&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="n">currentCollection&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="n">a&lt;/span> &lt;span class="p">,&lt;/span> &lt;span class="o">&amp;lt;&lt;/span>&lt;span class="k">from&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">b&lt;/span>
&lt;span class="n">a&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">localField&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">foreignField&lt;/span>
&lt;/code>&lt;/pre>&lt;/div>&lt;h1 id="實際例子">實際例子&lt;/h1>
&lt;h2 id="orders">Orders&lt;/h2>
&lt;div class="highlight">&lt;pre class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="nx">db&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">orders&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">insertMany&lt;/span>&lt;span class="p">(&lt;/span> &lt;span class="p">[&lt;/span>
&lt;span class="p">{&lt;/span> &lt;span class="s2">&amp;#34;_id&amp;#34;&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;item&amp;#34;&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;almonds&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;price&amp;#34;&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="mi">12&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;quantity&amp;#34;&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="mi">2&lt;/span> &lt;span class="p">},&lt;/span>
&lt;span class="p">{&lt;/span> &lt;span class="s2">&amp;#34;_id&amp;#34;&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;item&amp;#34;&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;pecans&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;price&amp;#34;&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="mi">20&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;quantity&amp;#34;&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="mi">1&lt;/span> &lt;span class="p">},&lt;/span>
&lt;span class="p">{&lt;/span> &lt;span class="s2">&amp;#34;_id&amp;#34;&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="mi">3&lt;/span> &lt;span class="p">}&lt;/span>
&lt;span class="p">]&lt;/span> &lt;span class="p">)&lt;/span>
&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="inventory">inventory&lt;/h2>
&lt;div class="highlight">&lt;pre class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="nx">db&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">inventory&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">insertMany&lt;/span>&lt;span class="p">(&lt;/span> &lt;span class="p">[&lt;/span>
&lt;span class="p">{&lt;/span> &lt;span class="s2">&amp;#34;_id&amp;#34;&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;sku&amp;#34;&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;almonds&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;description&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;product 1&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;instock&amp;#34;&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="mi">120&lt;/span> &lt;span class="p">},&lt;/span>
&lt;span class="p">{&lt;/span> &lt;span class="s2">&amp;#34;_id&amp;#34;&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;sku&amp;#34;&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;bread&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;description&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;product 2&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;instock&amp;#34;&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="mi">80&lt;/span> &lt;span class="p">},&lt;/span>
&lt;span class="p">{&lt;/span> &lt;span class="s2">&amp;#34;_id&amp;#34;&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="mi">3&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;sku&amp;#34;&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;cashews&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;description&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;product 3&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;instock&amp;#34;&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="mi">60&lt;/span> &lt;span class="p">},&lt;/span>
&lt;span class="p">{&lt;/span> &lt;span class="s2">&amp;#34;_id&amp;#34;&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="mi">4&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;sku&amp;#34;&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;pecans&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;description&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;product 4&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;instock&amp;#34;&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="mi">70&lt;/span> &lt;span class="p">},&lt;/span>
&lt;span class="p">{&lt;/span> &lt;span class="s2">&amp;#34;_id&amp;#34;&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="mi">5&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;sku&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kc">null&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;description&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;Incomplete&amp;#34;&lt;/span> &lt;span class="p">},&lt;/span>
&lt;span class="p">{&lt;/span> &lt;span class="s2">&amp;#34;_id&amp;#34;&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="mi">6&lt;/span> &lt;span class="p">}&lt;/span>
&lt;span class="p">]&lt;/span> &lt;span class="p">)&lt;/span>
&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="aggregate">Aggregate&lt;/h2>
&lt;div class="highlight">&lt;pre class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="nx">db&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">orders&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">aggregate&lt;/span>&lt;span class="p">(&lt;/span> &lt;span class="p">[&lt;/span>
&lt;span class="p">{&lt;/span>
&lt;span class="nx">$lookup&lt;/span>&lt;span class="o">:&lt;/span>
&lt;span class="p">{&lt;/span>
&lt;span class="nx">from&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;inventory&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;span class="nx">localField&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;item&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;span class="nx">foreignField&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;sku&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;span class="nx">as&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;inventory_docs&amp;#34;&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="p">]&lt;/span> &lt;span class="p">)&lt;/span>
&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="結果">結果&lt;/h2>
&lt;div class="highlight">&lt;pre class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="p">{&lt;/span>
&lt;span class="nt">&amp;#34;_id&amp;#34;&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span>
&lt;span class="nt">&amp;#34;item&amp;#34;&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;almonds&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;span class="nt">&amp;#34;price&amp;#34;&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="mi">12&lt;/span>&lt;span class="p">,&lt;/span>
&lt;span class="nt">&amp;#34;quantity&amp;#34;&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span>
&lt;span class="nt">&amp;#34;inventory_docs&amp;#34;&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;span class="p">{&lt;/span> &lt;span class="nt">&amp;#34;_id&amp;#34;&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nt">&amp;#34;sku&amp;#34;&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;almonds&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nt">&amp;#34;description&amp;#34;&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;product 1&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nt">&amp;#34;instock&amp;#34;&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="mi">120&lt;/span> &lt;span class="p">}&lt;/span>
&lt;span class="p">]&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="p">{&lt;/span>
&lt;span class="nt">&amp;#34;_id&amp;#34;&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span>
&lt;span class="nt">&amp;#34;item&amp;#34;&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;pecans&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;span class="nt">&amp;#34;price&amp;#34;&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="mi">20&lt;/span>&lt;span class="p">,&lt;/span>
&lt;span class="nt">&amp;#34;quantity&amp;#34;&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span>
&lt;span class="nt">&amp;#34;inventory_docs&amp;#34;&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;span class="p">{&lt;/span> &lt;span class="nt">&amp;#34;_id&amp;#34;&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="mi">4&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nt">&amp;#34;sku&amp;#34;&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;pecans&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nt">&amp;#34;description&amp;#34;&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;product 4&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nt">&amp;#34;instock&amp;#34;&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="mi">70&lt;/span> &lt;span class="p">}&lt;/span>
&lt;span class="p">]&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;span class="p">{&lt;/span>
&lt;span class="nt">&amp;#34;_id&amp;#34;&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="mi">3&lt;/span>&lt;span class="p">,&lt;/span>
&lt;span class="nt">&amp;#34;inventory_docs&amp;#34;&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;span class="p">{&lt;/span> &lt;span class="nt">&amp;#34;_id&amp;#34;&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="mi">5&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nt">&amp;#34;sku&amp;#34;&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="kc">null&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nt">&amp;#34;description&amp;#34;&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;Incomplete&amp;#34;&lt;/span> &lt;span class="p">},&lt;/span>
&lt;span class="p">{&lt;/span> &lt;span class="nt">&amp;#34;_id&amp;#34;&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="mi">6&lt;/span> &lt;span class="p">}&lt;/span>
&lt;span class="p">]&lt;/span>
&lt;span class="p">}&lt;/span>
&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>如果要將 inventory_docs 轉成陣列單一物件屬性 可以透過 $unwind&lt;/p>
&lt;/blockquote></description></item><item><title>Hugo使用教學(1)</title><link>https://tonny0531.github.io/post/hugo_tutorial1/</link><pubDate>Thu, 28 Oct 2021 14:52:48 +0000</pubDate><guid>https://tonny0531.github.io/post/hugo_tutorial1/</guid><description>&lt;h2 id="原由">原由&lt;/h2>
&lt;p>這幾天研究從 &lt;a class="link" href="https://tonny0531.medium.com/" target="_blank" rel="noopener"
>Medium&lt;/a> 轉成自己架 Blog&lt;/p>
&lt;p>為了避免下一個人架設過程中一頭霧水(&lt;del>實質是為了推別人入坑&lt;/del>)&lt;/p>
&lt;p>故撰寫此筆記&lt;/p>
&lt;h2 id="不繼續使用-medium-的原因">不繼續使用 Medium 的原因&lt;/h2>
&lt;p>大部分工程師都喜歡喜歡用 Markdown 進行文件撰寫(包含我在內)&lt;/p>
&lt;p>但 Medium 是不支援的(有工具可以透過 MarkDown 轉成 Medium 文章)&lt;/p>
&lt;p>因此建議如果是想先養成撰寫 Blog 的習慣的話&lt;/p>
&lt;p>可以從 Medium 下手 但長期喜歡用 Markdown 撰寫的話&lt;/p>
&lt;p>強烈推薦自己架一個 Blog&lt;/p>
&lt;h2 id="安裝hugo">安裝Hugo&lt;/h2>
&lt;h3 id="windows">Windows&lt;/h3>
&lt;p>透過 Chocolatey 安裝&lt;/p>
&lt;div class="highlight">&lt;pre class="chroma">&lt;code class="language-bash" data-lang="bash">choco install hugo-extended -confirm
&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="mac">Mac&lt;/h3>
&lt;div class="highlight">&lt;pre class="chroma">&lt;code class="language-bash" data-lang="bash">brew install hugo
&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="檢查安裝是否完成">檢查安裝是否完成&lt;/h3>
&lt;p>透過查詢 Hugo 版本來進行確認是否安裝完成&lt;/p>
&lt;div class="highlight">&lt;pre class="chroma">&lt;code class="language-bash" data-lang="bash">hugo version
&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;figure
class="gallery-image"
style="
flex-grow: 1012;
flex-basis: 2429px"
>
&lt;a href="https://tonny0531.github.io/post/hugo_tutorial1/hugo_version.png" data-size="668x66">
&lt;img src="https://tonny0531.github.io/post/hugo_tutorial1/hugo_version.png"
width="668"
height="66"
srcset="https://tonny0531.github.io/post/hugo_tutorial1/hugo_version_hu074f6c432dca35bc626e354759f3816b_10558_480x0_resize_box_2.png 480w, https://tonny0531.github.io/post/hugo_tutorial1/hugo_version_hu074f6c432dca35bc626e354759f3816b_10558_1024x0_resize_box_2.png 1024w"
loading="lazy"
alt="Version">
&lt;/a>
&lt;figcaption>Version&lt;/figcaption>
&lt;/figure>&lt;/p>
&lt;h2 id="建立新網頁">建立新網頁&lt;/h2>
&lt;div class="highlight">&lt;pre class="chroma">&lt;code class="language-bash" data-lang="bash">hugo new site &amp;lt;&lt;span class="s1">&amp;#39;SiteName&amp;#39;&lt;/span>&amp;gt;
&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;figure
class="gallery-image"
style="
flex-grow: 210;
flex-basis: 505px"
>
&lt;a href="https://tonny0531.github.io/post/hugo_tutorial1/new_site.png" data-size="644x306">
&lt;img src="https://tonny0531.github.io/post/hugo_tutorial1/new_site.png"
width="644"
height="306"
srcset="https://tonny0531.github.io/post/hugo_tutorial1/new_site_hu5f17bb152a932302bcff2c57d6077f13_30351_480x0_resize_box_2.png 480w, https://tonny0531.github.io/post/hugo_tutorial1/new_site_hu5f17bb152a932302bcff2c57d6077f13_30351_1024x0_resize_box_2.png 1024w"
loading="lazy"
alt="NewSite">
&lt;/a>
&lt;figcaption>NewSite&lt;/figcaption>
&lt;/figure>&lt;/p>
&lt;blockquote>
&lt;p>hugo 建立新網頁真的快&lt;/p>
&lt;/blockquote>
&lt;h2 id="執行-hugo-server">執行 Hugo Server&lt;/h2>
&lt;div class="highlight">&lt;pre class="chroma">&lt;code class="language-bash" data-lang="bash">hugo server
&lt;/code>&lt;/pre>&lt;/div>&lt;p>這時候開起來會是一片空白 這是正常的&lt;/p>
&lt;p>是因為現在實際上甚麼都沒有&lt;/p>
&lt;p>等套用了 Theme 就會正常了&lt;/p>
&lt;h2 id="套用-theme">套用 Theme&lt;/h2>
&lt;p>這裡我採用了 &lt;a class="link" href="https://github.com/CaiJimmy/hugo-theme-stack" target="_blank" rel="noopener"
>Stack&lt;/a> 來進行&lt;/p>
&lt;p>先建立 Git 並初始化&lt;/p>
&lt;div class="highlight">&lt;pre class="chroma">&lt;code class="language-bash" data-lang="bash">git init
git add .
git commit -m &lt;span class="s2">&amp;#34;Init&amp;#34;&lt;/span>
&lt;/code>&lt;/pre>&lt;/div>&lt;p>透過 Submodule 的方式載入&lt;/p>
&lt;div class="highlight">&lt;pre class="chroma">&lt;code class="language-bash" data-lang="bash">git submodule add https://github.com/CaiJimmy/hugo-theme-stack/ themes/hugo-theme-stack
&lt;/code>&lt;/pre>&lt;/div>&lt;p>偷懶一點直接複製 Theme 裡面的 Config 來做使用&lt;/p>
&lt;blockquote>
&lt;p>themes\hugo-theme-stack\exampleSite\config.yaml&lt;/p>
&lt;/blockquote>
&lt;ol>
&lt;li>
&lt;p>複製到我們網站的目錄下&lt;/p>
&lt;/li>
&lt;li>
&lt;p>刪除 &lt;code>config.toml&lt;/code>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>並在檔案開頭加上一些屬性&lt;/p>
&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="nt">baseURL&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;http://example.org/&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="nt">languageCode&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;en-us&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="nt">title&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;Demo&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># 你的網站名稱&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="nt">theme&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;hugo-theme-stack&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="nt">module&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nt">hugoVersion&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nt">extended&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nt">min&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;0.87.0&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="nt">params&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nt">mainSections&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>- &lt;span class="l">post&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nt">featuredImageField&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">image&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nt">rssFullContent&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="w">
&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>這樣就套用上主題了&lt;/p>
&lt;p>&lt;figure
class="gallery-image"
style="
flex-grow: 227;
flex-basis: 544px"
>
&lt;a href="https://tonny0531.github.io/post/hugo_tutorial1/sample_page.png" data-size="999x440">
&lt;img src="https://tonny0531.github.io/post/hugo_tutorial1/sample_page.png"
width="999"
height="440"
srcset="https://tonny0531.github.io/post/hugo_tutorial1/sample_page_hu789a5efe3b5db2228c8544d2bece2c12_17364_480x0_resize_box_2.png 480w, https://tonny0531.github.io/post/hugo_tutorial1/sample_page_hu789a5efe3b5db2228c8544d2bece2c12_17364_1024x0_resize_box_2.png 1024w"
loading="lazy"
alt="Sample">
&lt;/a>
&lt;figcaption>Sample&lt;/figcaption>
&lt;/figure>&lt;/p>
&lt;h2 id="建立新頁面">建立新頁面&lt;/h2>
&lt;p>透過 CLI 來建立一個新的文章&lt;/p>
&lt;div class="highlight">&lt;pre class="chroma">&lt;code class="language-bash" data-lang="bash">hugo new post/&amp;lt;&lt;span class="s1">&amp;#39;newPageName&amp;#39;&lt;/span>&amp;gt;.md
&lt;/code>&lt;/pre>&lt;/div>&lt;p>建立完成後應該還是看不到&lt;/p>
&lt;p>因為建立文章預設會將文章設定成草稿&lt;/p>
&lt;p>有兩種方式可以呈現&lt;/p>
&lt;ol>
&lt;li>在啟動 Hugo Server 的時候追加屬性&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre class="chroma">&lt;code class="language-bash" data-lang="bash">hugo server -D
&lt;/code>&lt;/pre>&lt;/div>&lt;ol start="2">
&lt;li>將文章屬性下的 Draft 改為 false&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="nn">---&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="nt">title&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;Test&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="nt">date&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="ld">2021-10-29T01:11:07&lt;/span>&lt;span class="m">+08&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="m">00&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="nt">draft&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">false&lt;/span>&lt;span class="w">
&lt;/span>&lt;span class="w">&lt;/span>&lt;span class="nn">---&lt;/span>&lt;span class="w">
&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>這樣就是建立一個新文章的流程了&lt;/p>
&lt;p>這裡留個伏筆&lt;/p>
&lt;p>下一篇再來補關於Search、Archives為什麼不見了&lt;/p></description></item></channel></rss>