2020年12月11日 星期五

WebApp如何安全的存放機密資訊

WebApp如何安全的存放機密資訊

每一個重要的應用程式裡面都有機密資訊,從資料庫的連線字串,到後台admin的帳號密碼,這些大大小小的機密資訊,常常被開發人員存放在source code的某處…咦? 等等…

對,你應該有所警覺,這些資訊怎麼能存放在程式碼裡呢? 這顯然不夠安全啊。但…直到上周,我在上課的時候,還有學員依舊把連線字串和帳密寫死在程式碼中😮。

那你說,好吧,不寫在程式碼『裡面』,那我獨立寫在一個檔案裡面好了,在 .net 的世界裡,這就是 web.config或是 appsettings.json,但…這樣就安全了嗎?

只能說,『相對』安全了點,但還是不夠安全。

關鍵在於,誰能(或誰該)經手這些機密資訊? 早期的概念是把開發和維運團隊切開,讓開發人員無法得知最終的帳密,讓維運的人無法得知程式碼的內容,這樣,除非這兩個人同時被綁架(或彼此串通),不然企業的機密資訊還是可以得到保障。有點像是出納和會計不能是同一個人的道理。

這種保護是,不讓『某一個人』知道所有的資訊。只能說相對安全,並非絕對安全,因為經手機密資訊的人還是太多了。安全的定義是,不該知道的人,就不要知道。

因此,更好的方式是,讓機密資訊只有單一一個安全存放點,且由單一的某一個人寫入。並且,在取用的過程中維持『不落地』。所謂的不落地,就是不儲存在任何地方。簡單的說,就是需要使用時(例如建立資料庫連線),從安全的存放點取出(帳密),然後就保存在記憶體中,永遠不寫入任何的檔案、資料庫、硬碟…等儲存體。

真能夠實現這件事情嗎? 可以的。

微軟在Azure雲端上,有一個專門提供存放機密資料的儲存庫,稱為Key Vault(金鑰儲存庫),管理人員可以把機密資訊(例如admin的帳號密碼、資料庫的連線帳密…etc.),存放在這個Key Vault中,然後在WebApp的原始程式碼(檔案)中,就不要再存放這些資訊了。當WebApp需要使用的時候,直接問Key Vault拿就可以了:
enter image description here
這樣還有一個莫大的好處,管理人員可以隨時rotate(滾動轉換)帳密資訊,無須開發或維運人員經手。

由於Key Vault是被重重大鎖給保障的(採用高安全性的加密機制),因此可以說是固若金湯,安全無庸置疑…等…等等,不對啊,那我們的WebApp在存取KeyVault的時候,不是也會有帳密或連線字串嗎? 那這連線字串要放哪? 另一個KeyValut嗎??? 😛

如果存取KeyValue的帳密或連線字串,還是放在web.config或是AppSettings.json裡面,那這一切豈不都是白搭?

萬一 web.config 或 AppSettings.json被竊取,機密資訊不是被駭客整碗捧去? 原本只是損失個db連線字串,現在是損失整個KeyVault,豈不更慘?

所以關鍵來了,如果有一個方法,可以不用帳密就能讓WebApp『安全的』存取KeyValue,那不就解決了所有問題? 能嗎? 可以的。

由於WebApp和KeyValue都是Azure管理的,因此,我們可以設定KeyValue『信任』該WebApp,讓該WebApp存取KeyVault中的特定機密資料。只要設定完成,未來WebApp內的程式碼,跟KeyVaule拿資料的時候,就無需帳密驗證(其實不是沒有驗證,而是由Azure自行做驗證),開發人員不需要把KeyVault的存取連線字串或權限放置在WebApp的appsetting.json或任何地方,如此一來,機密資料的存取流程就安全無懈可擊了。

如何讓WebApp被KeyVault信任?
首先,請在Azure上建立該WebApp的身分識別:
enter image description here
這個動作會讓該WebApp在AAD上被註冊,你會得到一個物件識別碼,請取得該物件識別碼,然後到KeyVault的存取原則設定:enter image description here

請新增存取原則,出現底下畫面後,設定服務主體為剛才紀錄的WebApp 服務主體物件識別碼,並提供它具有存取該 secret (帳密)的權限:
enter image description here

完成後,你會發現神奇的事情發生了,該WebApp存取KeyVault中的該機密資訊時,是不需要連線字串或帳密的(類似底下這樣):
enter image description here

而WebApp取得機密資訊後,只要沒有儲存在任何儲存體上,就沒有外洩的可能(應該說外洩可能極低,除非記憶體被動態竊取)。如此一來,從KeyVault取得的機密資訊的流程不僅安全,也不須將機密存放在KeyVault以外的任何地方,達成機密資訊『不落地』的效果,這才是真正的高安全性應用程式開發。😎

2020年10月28日 星期三

Azure DevOps in Action - 如何避免開源套件的使用風險

enter image description here
近代軟體開發,不管是使用哪一種語言,幾乎都一定會使用到套件(Package),套件的使用都有著非常重大的意義。套件不只是讓開發變的更方便,套件的版本管理,能夠讓專案之間的相依性被有效管控,避免dependency hell的發生。

所以各大開發語言,不管是node.js、python、Java…都有自己的套件庫,微軟的.net當然也是,nuget就是.net開發人員的標準套件庫。如今,使用套件庫上的組件來開發企業內的專案,已經是理所當然的習慣了。

套件庫的使用風險

然而,使用套件並非100%毫無風險,由於開源軟體的觀念盛行,這個時代任何人都可以將自己開發的套件貢獻上nuget讓大家使用,雖然開發社群與nuget站台會針對有潛在或惡意風險的套件提出警訊,但由於這些資訊並非即時提供,且有可能因為開發人員的疏忽而沒有被發現,導致你的專案使用到有品質不佳,或是有安全疑慮的套件。

除此之外,套件還有許可授權的問題,並非每一個套件使用上都是毫無代價的,雖然nuget會要求套件開發人員具體標明套件的使用授權許可,但倘若開發人員不察,使用到一些並非可以免費使用的套件,或是使用到了標註為GPL的套件,那你依賴該套件開發的專案,也會被要求開源,這對於公司來說,可能會造成一場災難…

在CI Pipeline中掃描套件

因此,為了避免軟體開發人員一時疏忽,CI Pipeline有必要針對軟體套件的使用作一些掃描和檢查。而WhiteSourceBolt就是這樣的一套免費工具。

你可以在Azure DevOps Pipeline中,加入WhiteSourceBolt這個task,就可以輕易的掃描整個專案中使用的套件:
enter image description here

呈現出的報表如下:
enter image description here

你會發現,報表中清楚的告訴我們,哪些套件是高風險的,並且原因為何(上圖A)。如果你的專案有紅色高風險套件,強烈建議你要立即著手處裡(升級版本或尋找替代套件)。

要在Azure DevOps中啟用WhiteSourceBolt非常簡單,只需要為你的組織安裝Azure DevOps Marketplace中的外掛:
enter image description here

下載位置位於:
https://marketplace.visualstudio.com/items?itemName=whitesource.ws-bolt

安裝好之後,你重新進入Azure DevOps,可以看到在Pipeline選單下,出現了WhiteSourceBolt:
enter image description here

請點選進去,輸入你的聯絡方式註冊後即可使用。

接著,請進入Pipeline,在其中加入WhiteSourceBolt task,並且設定working directory:
enter image description here

完成之後,重新觸發運行pipeline,完成後即可看到報表囉:
enter image description here

幾個小小的動作,讓專案免於套件使用的風險,非常划得來吧。


本文摘錄自『Azure DevOps敏捷開發與專案管理實戰

2020年10月24日 星期六

使用IoC與DI有何意義? (三) 在 Console 程式中使用DI

這一篇的重點在介紹,如何在Console App中使用DI,但我們的重點卻不是 Console,而是 “使用DI”。

看完前面兩篇,你應該要想到一個問題,如果PageModel或MVC的Controller,被呼叫的時候,.net core的DI服務會自動幫我們把Startup.cs中指定好的類別實作,自動注入PageModel(或Controller),那Console中的Main()也會嗎?

好比說,如果我把程式寫成底下這樣:
https://gist.github.com/isdaviddong/5faad4066919b4e5e72ae85621d021c6

我在Main()中能夠讀到 _SalaryFormula 的值嗎? 猜猜看?



答案是===> 不會。

原因很簡單,因為Console App的進入點是靜態的 Main()函式,它根本就是一切的源頭,是應用程式被Launch起來的時候,最最最開始的入口,根本不會有人去幫你 run 上面 5-8行的建構子,所以理所當然的也沒有注入這回事。

那…為何 PageModel可以? 為何MVC的Controller可以? 原因也不難,因為當用戶點選(或連結到)某一個頁面(或某一個Routing)的時候,該頁面(PageModel或Controller)所觸發的那隻程式(Onget() 或 Action),根本就不是整個程式的進入點。它(頁面)是"被"人家執行的。這個執行者不是用戶,是由 asp.net 的框架本身host的。

其實,整個 .net 應用程式的入口一直都是 Program.cs。

請看底下這個MVC Web應用程式的Program.cs:
enter image description here
事實上,Program.cs才是一切程式的進入點,而上面的Host,則是幫我們運行(Launch)起 asp,net web應用程式的背後推手。(未來有空我們再談這個類別)

一直以來,是有一個host幫我們承載每一個頁面的。當asp.net的某個頁面被呼叫,每一個controller被觸發,都不是該class被用戶主動觸發,而是『被』呼叫,被誰呼叫呢? 就是框架本身。正確的流程應該是,某個頁面(或說網址)被呼叫到時,asp.net框架中的底層(middleware, routing…etc.)得知該request,接收該http request的各種參數之後,幫我們new出我們所寫的程式碼(類別),然後把相關參數(像是QueryString, 或是http body…etc.)轉成parameters傳遞給我們的Action或Page。

這也是 asp.net MVC一堆預設的約定(慣例)的原因(的來源)

而我們一直說,DI被作為服務納入 asp.net core框架中成為內建機制的一部分,因此框架在幫我們呼叫(觸發)某一個頁面的時候,就順手(偷偷的)把我們想要注入的執行個體透過建構子的方式傳入,造就了這整個 DI"服務" 的誕生,就如同我們底下這段程式:
enter image description here
上圖中的PageModel,之所以可以實現DI,因為該頁面被(框架)呼叫時,由asp.net core框架幫我們new這個頁面的執行個體(這時當然會運行到該類別的建構子),然後框架順手幫我們把我們在Startup.cs中指定好的(適用於HR.ISalaryFormula)類別執行個體給注入(填入)建構子中作為參數,來實現所謂的DI注入服務,然後你在程式碼中就可以光明正大的使用了。

好了,到這邊你大概了解DI的原理了,那回頭看Console App大概也就明白了,因為Console App的Program.cs根本就是一切的起點,在這個起點中,根本沒有所謂的 asp.net core框架、也沒有host,所以沒人可以幫我們的main()實現注入這回事…

真正實現DI服務的是…?

所以要實現注入在Console App中的話,我們必須自己建立這個Host。這時,有一個現成的機制很好用,就是 ServiceCollection,請看底下這段code:
enter image description here
這是一段console App,不過請不要誤會了,它長的像是Main()但不是Main(),你可以說它是仿的main(),因為它的類別名稱是 MainApp(而非Program),且其中的Method Main()並非靜態。

上面這一段寫起來就很像是前面我們寫在 MVC Controller或是PageModel裡面的建構子,對吧。

並且,它能夠執行出我們想要的結果:
enter image description here

但請注意了,它並不是主程式(應該說它不是程式碼的進入點),我們的主程式其實依舊位於Program.cs中的這個main()靜態方法:
enter image description here

真正去 new出 MainApp這個類別的執行個體,並且調用MainApp中的Main()方法的是上圖中的第43行。簡單的說就是,這一行程式碼"幫你"建立(new)並執行了MainApp.Main()這個方法。

而33,35分別註冊了依賴注入所需要的型別參考。也因為是43行"幫你"執行的,所以它在幫你執行的時候,可以順手把你需要(定義過的)的依賴類別給注入(填入)MainApp的建構子當中,以至於所謂的DI服務可以實現。將來要換其他的公式時候,只須把35行改為37行這樣的寫法即可抽換。

再次提醒,其實是可以透過設定來抽換,不一定必須寫成程式碼。

這才是整個DI服務具體的實現方式。

結論

有沒有發現,DI之所以成為服務,是因為有人幫你做了許多事情。框架幫你做了,ServiceCollection幫你做了,所以我們才說,它(DI)在.net core中已成為了一種"服務(功能)",被納入到框架當中。那…我們可以自己做嗎?當然也行,具體的實現方式其實不難,如果只是要一個最簡單的實踐,其實只需要透過reflection機制就可以了。

好,這一段先到這邊,我們後面要準備來介紹更重要的內容…
source code :
https://github.com/isdaviddong/DotNetCoreConsoleDIServicesExample


本文內容來自於『團隊開發與架構設計實務』課程,我們最近要開課囉,依照過去經驗這門課幾乎都是秒殺,如果你需要預先保留席次,請點選這裡登記唷。

若您要詢問問題,或是需要即時取得更多相關訊息,可按這裡加入FB專頁。
若這篇文章對您有所幫助,請幫我們分享出去,謝謝您的支持。

2020年10月23日 星期五

使用IoC與DI有何意義? (二) asp.net core中的DI服務

看過了前面的介紹,在瞭解了DI的背景需求與前提,以及採用介面來降低對特定物件實作的相依性之後,我們接著來看,怎麼使用asp.net core中的DI服務進行功能抽換。

事實上,早在約莫十年前,坊間就有很多DI框架或套件,例如我之前上課介紹過的Unity Application Block,他是一組DI Container(容器)套件,可以幫助我們更輕易地實現動態注入。而asp.net core則是把DI設計為服務,讓我們在程式碼當中,也可以輕易享有相關的功能。

在軟體開發的領域當中,你慢慢會發現到,真正困擾你的不是寫(新)程式,而是改(舊)程式。在我們開發系統的過程當中,我們會希望盡可能維持原始程式碼的不改變,就能夠加入新功能。原因很簡單,拿先前提過的例子,你只是想要修改薪資計算公式,而不是換掉整套HR系統,然後你剛才接手這個系統不久,這時候你不會想看完這套HR系統的整套原始程式碼,才能增加一個小小的薪資計算公式。

另外,你很有可能碰到運氣更差的狀況,就是你手邊根本沒有原始程式碼。

你不過就只是想改一下薪資計算的公式啊?(因為政府剛調整了最低薪資…)

記得Martin Fowler在重構一書中說過這麼一段話:

你回頭想,果真是如此,如果程式碼寫得夠好,你根本不需要看完所有程式碼,甚至也無須擁有整套程式碼,就能夠依照需求在系統中添加新功能。

所幸,前面我們已經為動態注入奠定好了基礎,請看底下這個Razor Page頁面: (我們之所以從前面的Console App改用Razor Page,是為了要展示asp.net core中的DI之故,請務必看過前面這篇才能理解底下情境)

上面這個頁面是 .net core 的 Razor Page Model,如果你不熟這個架構,只會MVC,那你就暫時把它當作某個Controller來看好了,後面我們會介紹Console和MVC模式下的DI。

它的執行結果如下:

頁面被載入的時候,會運行到OnGet()方法,其中的程式碼計算出的薪資是 28800,問題來了,OnGet(…)程式碼當中沒有看到誰去new了那個計算薪資的_SalaryFormula類別啊? 那這個實行個體是哪裡來的? 莫非是從建構子(上面程式碼第4行)傳入的? 是的,你猜對了。

整個asp.net core框架都支援套DI服務,不管是WebAPI、一般的Controller、或是Razor Page的PageModel,都可以在建構子當中加上特定介面作為參數(例如上面第4行),這時候,asp.net core的DI服務框架,會自動幫你在該頁面被執行的時候,注入(傳入)你事先指定好的類別的執行個體。

這就是asp.net core提供的DI『服務』。

如此一來,我們透過asp.net core在開發整套系統時,程式碼就可以砍斷對特定類別實作的依賴,實現動態抽換(注入)的效果了。

那...到底是在哪邊指定具體是要注入哪一個類別的實作(執行個體)呢? 就是那鼎鼎大名的Startup.cs:


你會發現,就是在Startup.cs當中,我們指定了HR.ISalaryFormula介面未來的實作採用的是HR.SalaryFormula這個類別(第7行),這使得 PageModel 的建構子被asp.net core系統給new起來運行的時候,會自動帶入(注入)HR.SalaryFormula物件的實體(.net core自動幫我們new的)作為參數,然後就實現了後續薪資計算的功能: (12-14行)

而有朝一日,我們想換掉這個公式的時候怎麼做呢? 容易,只需要在Startup.cs 當中換成另一個同樣是繼承自ISalaryFormula所實作出的類別即可:

嘩啦,如此一來,整套系統所有使用到薪資計算的地方都不需要做任何修改,單單只需要在Startup.cs 當中設定(註冊),就可以決定系統中每一個用到的薪資計算的地方採用的是哪一個計算公式類別。

這就是注入的好處,也是DI所帶來的可抽換性。

剛才你看到的是薪資計算公式,但更多時候,我們常用的情境其實是處理資料庫的 Data Context 物件、處理日誌紀錄的 Logging 物件、處理通知的 Notification 物件…凡此種種,透過DI的設計就可以輕易的替換了,甚至 asp.net core 已經內建了不少實作。

未來你要把原先發 mail 的 Notification 改成發LINE訊息嗎? 你想把原先寫到檔案系統的Log改成送到雲端嗎? 都沒問題,只需要實作出符合相對應介面的類別,然後在Startup.cs 中以DI服務進行註冊即可,系統中其他的程式碼會自動被注入(.net core採用建構子注入),替換成你註冊的類別實作。

這一整個功能的實現,都源自於一開始我們談到的IoC,砍斷針對類別實作的相依,換成以介面設計的方式來實踐。

到這邊你應該理解了為何前面要先實現IoC,因為IoC是我們後面能夠DI的基礎。前面說過,這邊再次提醒,DI除了能夠幫我們容易抽換(例如上面這個例子是抽換薪資計算的公式),但除此之外,同時可以實現提高可測試性與程式碼高可維護性的效果(一時想不通為什麼嗎? 我們後面會再慢慢介紹)。很有趣是吧,先消化一下。未來我們會接續著來談談,為什麼DI能夠有助於提高可測試性,以及如何連程式碼都不要改,只需要透過『設定』,就可以為系統注入不同的類別實作…

有個問題...

在結束這一篇前先問你一個問題,為何 asp.net 的頁面被載入的時候,建構子會被注入我們所需要的物件執行個體? 我知道,我知道,是因為我們在 Startup.cs 中這麼指定的。但...你有沒有想過,為何我們這麼指定,系統就會這麼運行? 具體誰做的呢? 就好比上例中的 PageModel,一定得有人把這個類別給 new 起來,然後呼叫它的建構子,並且幫我們把new好的_SalaryFormula物件實體傳入,否則程式碼無法運作啊,做這件事情的人到底是誰呢? 這個問題隱含著另一個問題...如果我們用的不是 asp.net ,是Console App,那也可以這樣注入嗎?

如果想不出答案,請接著往下看下一篇,我們要回到console中繼續探索 .net core中的DI...

-----------------------------

本文內容來自於『團隊開發與架構設計實務』課程,我們最近要開課囉,依照過去經驗這門課幾乎都是秒殺,如果你需要預先保留席次,請點選這裡登記唷。

若您要詢問問題,或是需要即時取得更多相關訊息,可按這裡加入FB專頁。
若這篇文章對您有所幫助,請幫我們分享出去,謝謝您的支持。

2020年10月20日 星期二

雲端好物 - 用 Azure Files 建立隨處可存取的 D 槽...

雲端好物 - 用 Azure Files 建立隨處可存取的 D 槽...

enter image description here
USB隨身碟不夠大? 擔心檔案遺失? 想要有便宜好用的儲存空間? 不如把檔案放上雲端吧。想把檔案放到雲端最簡單的方法,大概莫過於 Azure Files了。

當你設定好 Azure Files之後,等同於有一個雲端的隨身碟,不管在任何地方,只要你想要存取檔案,都可以透過安全的連線方式,取得位於雲端大儲存空間的檔案。

設定的方式很簡單,只要你建立好一個 Azure Storage Account,然後找到 檔案共用:
enter image description here

接著,依照下圖的方式,建立一個檔案共用:
enter image description here

建立完成後,選擇連接:
enter image description here

接著會出現底下畫面:
enter image description here

你會看到各種平台連結此雲端硬碟的方式,我們可以選擇某一個磁碟機號,例如上圖選擇W,選擇好後,複製上面這串script,到Windows的PowerShell環境執行:
enter image description here

最後會出現底下畫面:
enter image description here

當啷,一個W槽就出現了。

你可以使用這個 W 槽如同一般的資料夾一樣,所有的檔案也都能在雲端看到:
enter image description here

所有放上去的資料,由於Azure Storage本身就有備份與備援機制,檔案遺失的可能性幾乎等於0,你還可以隨時建立快照集(上圖A),輕而易舉的就讓檔案存取既安全又方便,從此之後檔案隨手可得。

備註:當然,這一切…網路速度要夠快才可以。

2020年10月19日 星期一

使用IoC與DI(Dependency Injection)有何意義? (一) 它到底是甚麼?

其實我們在好幾年前,就已經談過DI(Dependency Injection)這個主題。當時這類議題被視為進階的開發概念,但如果你最近開始使用 .net core,大概已經發現DI如今已變成.net core中的基本要求。

事實上,從事教育訓練這麼多年的觀察下來,不難發現其實還是有相當多的開發人員不真的很明白,到底DI對於軟體開發有何意義? 它能帶來什麼價值?

這一篇希望能夠用一個較為具體的實例,對初學者解釋到底什麼是DI,以及它能帶來的效益。

結論

先說結論,對開發人員來說,使用DI能夠帶來 提高可測試性(testability) 以及 提高可擴充性的價值,同時降低相依性,讓程式碼便於維護。

在 asp.net core當中DI如今已是基本方案,asp.net core 是以服務(DI Services)的形式把DI這個機制實現在框架當中。讓開發人員不管是用 razor page, MVC, 或是其他開發方式,都可以(幾乎是必須)採用DI服務。

我們很久以前就說過,『框架』本身其實就是一種限制、一種引導,誘導開發人員往某一種方向去開發程式。而如今 asp.net core 把DI納作框架的一部分,就是在引導開發人員在開發時走向某一個路線。

什麼路線呢? 就是高可測試性、低耦合以及高可擴充性,如果兩相比較,我覺得asp.net core中的DI可以為開發人員實現高可測試性這個目的的可能性大概會更高一些,估計是因為最近幾年,unit test已經成為開發領域的某種潮流。

什麼是物件相依性

但不管如何,.net core已經把DI視為基礎,而你要理解這一切,得先搞清楚什麼是物件的相依性。

我常在上架構設計課程時說,職責分離這個設計概念很容易理解,誰都知道不同職責的模塊或物件就該分離,問題是怎麼決定誰的職責是甚麼? 到底誰又該跟誰分離? 等到有一天你真的開始設計,就會發現理解是一回事,真的明白到能夠動手設計又是另一回事…

看個例子,我們先來了解一下背景需求。

假設,我們打算為企業建構一個薪資計算的功能,已知薪資計算會依據三個參數,分別是本月上班時數(WorkHours),時薪(HourlyWage),以及請假時數(PrivateDayOffHours)。

也就是說,倘若Eric本月上班 19天,每天8小時,時薪200,本月請假1天,每請假一天倒扣200元,則Eric的薪資為:

Eric薪資= (19x8) x 200 – (1 x 8) x 200 = 30400 – 1600 = $28800

為了實現這個功能,我們的設計了底下這樣的console程式,它包含一個主程式和兩個類別,我們先看類別的部分:

我們來談談上面這兩個類別的職責,為什麼計算薪資該分成兩個類別? 為何不寫成一個? 如果把上面SalaryFormula中計算薪資的Execute Method直接融入SalaryCalculator裡面作為內建的程式碼(Calculate的一部分),不是讓程式碼簡單的多了嗎? (其實,一般初學者肯定會這麼寫)

的確,但關鍵就在『需求』

由於考量到這是一個套裝軟體,將來會賣到不同的企業,每一個企業計算薪資的具體公式可能不同,我們希望未來計算薪資的公式可以隨時動態『抽換』,最理想的狀況是,賣給不同的企業,可以抽換成不同的模組。

請記得,最終職責的決定其實往往取決於『需求』。

因此,之所以把計算薪資設計成兩個類別,就是考量到上面這樣的背景需求。我們區分成兩個類別,一個是計算薪資的SalaryCalculator類別,另一個則是薪資計算公式SalaryFormula類別。

SalaryFormula是薪資計算的公式,它具體決定了薪資怎麼計算。而SalaryCalculator這個類別則是負責『計算』這個動作,但不管具體算法為何。

這兩個類別設計好了之後,我們再來看主程式如下:

你會發現主程式裡面new了一個SalaryCalculator類別來計算薪資,並且傳入SalaryFormula()物件實體作為參數。好了,現在問題來了。這整段程式碼有什麼缺點?

沒錯,從原始需求的角度來看,薪資計算的公式若想要可以動態調整,依照先前SalaryCalculator.cs的程式碼,由於類別SalaryCalculator會倚賴SalaryFormula類別的實作(SalaryCalculator.cs 16行相依於36行的SalaryFormula類別的具體實作,因為傳入的參數型別寫死了是SalaryFormula類別),未來這套軟體賣給了另一家公司,倘若想要改變薪資計算的公式,勢必得翻出原始程式碼(48行)來改寫SalaryFormula的實作才行。

砍斷針對具體實作的相依

好,知道問題在哪之後,我們來看怎麼調整? 其實很簡單,請回想起你熟悉的物件導向程式設計中的介面(interface)。請注意最大的改變在11,16,36,55行:

上面這段是修改後的類別,你會發現最大的差異在於55-59行增加了一個介面ISalaryFormula,該介面是類別SalaryFormula的抽象定義,而類別SalaryFormula(36-50)行則是ISalaryFormula的實作。

另外一個重要的修改是16行的SalaryCalculator(…)方法,傳入的物件的型別從SalaryFormula改為ISalaryFormula,如此一來,未來只要是符合ISalaryFormula介面所實作的類別,都可以當作參數傳入。

這一個修改動作,就在改變SalaryCalculator類別對於SalaryFormula類別的相依性,從原先針對類別實作的相依,改為對介面(抽象)相依。這樣做有什麼好處? 砍斷對具體實作的相依性,才能夠為將來系統能夠動態抽換計算公式而鋪路…我們接著往下看。

做了這樣的修改之後,未來倘若我們希望改變薪資的具體計算方式,我們無須修改整套程式碼,只需要再建構一個新的薪資計算類別,然後修改主程式即可(你如果一直往後看,未來只需要透過config檔案『設定』,連主程式都無須修改):

注意上面的程式碼當中,主程式有兩段6-9行與12-16行,本質上這兩段code幾乎完全一樣,唯一的差別是new SalaryCalculator(…)類別時傳入建構子的參數有所不同,一個是計算員工的公式類別實作,一個是計算老闆的公式類別實作。

你會發現由於老闆請假不扣薪(26行),因此即便參數完全一樣,但計算出的結果有所不同:

到這邊,我們已經為了動態抽換鋪好了基礎(對,這部分只是基礎,稱為IoC),接著我們來看,如何在ASP.NET Core中透過DI Services實現動態抽換。

如果你需要完整程式碼,請參考我的Github:
https://github.com/isdaviddong/DotNetCoreConsoleExample_IoC

-----------------------------

本文內容來自於『團隊開發與架構設計實務』課程,我們最近要開課囉,依照過去經驗這門課幾乎都是秒殺,如果你需要預先保留席次,請點選這裡登記唷。

若您要詢問問題,或是需要即時取得更多相關訊息,可按這裡加入FB專頁。
若這篇文章對您有所幫助,請幫我們分享出去,謝謝您的支持。

2020年10月12日 星期一

千呼萬喚始出來:動態修改LINE Bot WebHook 的 API

enter image description here
很多人期待這組API很久了,終於,在光輝的十月,LINE通知大家,這組API出現了😍:
https://developers.line.biz/en/news/2020/10/06/messaging-api-update-october-2020/

過去,開發者要動態修改LINE Bot的WebHook是不可能的。😢 唯一的方式是去LINE的Developer後台手動修改。

但這造成了許多LINE Bot開發廠商的維運難度。

想像一下,你需要為近百個客戶同時升級改版,更新WebHook的時候,會是一個多大的工程? 😒

還不僅如此,有許多LINE Bot應用廠商,自己也做了LINE Bot的管理後台,想讓客戶輸入LINE Bot的Channel Access Token,就可以幫客戶的LINE Bot動態賦予不同的行為。但過去,這也無法達成,非得要到客戶的後台去設定不可。(但直接去客戶後台修改? 還是手動? 有點 low 了吧…)

而現在這一組API,則徹底的解決了這個問題。現在,你只要有LINE Bot的Channel Access Token,就可以動態幫LINE Bot隨時設定或調整WebHook URL。

LINE公布了這組API之後,我們的SDK當然立刻更新。
現在,你只需要透過底下這樣的指令,就可以動態設定 LINE Bot的WebHook:

isRock.LineBot.Utility.SetWebhookEndpointURL(ChannelAccessToken, WebHookUrl);

想要抓取當前LINE Bot的WebHook也不是難事:

var ret = 
isRock.LineBot.Utility.GetWebhookEndpointInformation(ChannelAccessToken);

上面取得的ret,會有底下2個屬性:
enter image description here

active屬性可以得知當前是否有使用WebHook,而endpoint當然就是WebHook的url囉。

想要測試 WebHook是否正確也可以用 API呢,例如:

   ret2 = 
   isRock.LineBot.Utility.TestWebhookEndpoint(ChannelAccessToken, WebHookUrl);

回傳的物件可以用來判斷你的WebHook是否正確,就好比在後台按下Verify按鈕一樣:enter image description here

很方便吧!!
有了這組API,對於LINE Bot開發廠商來說,簡直是如虎添翼啊…

(彷彿…聽到卡米狗偷笑的聲音…)

別忘了,請將 LINEBotSDK升級到2.3.28以上的版本唷~ 👍
https://www.nuget.org/packages/LineBotSDK/2.3.28


LineBotSDK:https://www.nuget.org/packages/LineBotSDK
相關課程:http://www.studyhost.tw/NewCourses/LineBot
線上課程:https://www.udemy.com/line-bot/
更多內容,請參考電子書:https://www.pubu.com.tw/ebook/103305
LINE Bot實體書籍:https://www.tenlong.com.tw/products/9789865020354

如果需要即時取得更多相關訊息,可按這裡加入FB專頁。若這篇文章對您有所幫助,請幫我們分享出去,謝謝您的支持。

2020年9月26日 星期六

導入敏捷最難的是什麼?

enter image description here
『導入敏捷最難的是什麼?』上課時同學這麼問。

『這問題,我可是很有經驗呢。』我心裡這麼想著,然後慢慢的回答…

導入敏捷最難的是…『組織總想在其他條件都不改變的狀況下,實現敏捷轉型。』

我常說:『人人擁抱敏捷,但真實狀況是…沒人想要改變。』

你說,不會啊,我們公司常常改變! 老闆總是朝令夕改…搞的大家無所適從。

我笑著說:『是啊,那是他改變你,不是改變他自己。況且,你不是也不喜歡別人常常改你的工作模式…對吧?』

總之組織從上到下,沒人喜歡『被改變』。

所以,導入敏捷當然可以…但,不能碰KPI、不能違反ISO、不能調整考績與獎金制度、不能改變合約簽訂方式、不能變更HR既定規則、不能調整休假與簽核流程、不能違反愚蠢的勞基法規法條、不能取消既有的公司會議(不管多麼無效)、不能改變組織結構、不能拆分部門與團隊…在所有外界環境都不改變的狀況下,要我們協助導入敏捷…幹的好。

這不是『難』,這根本是『為難』。

但我得說,這才是企業轉型的真實狀況。有興趣『談』轉型的老闆很多,有膽量豁出去的可算是鳳毛麟角,除非…

『除非什麼?』學生問。

除非…那公司差不多快掛了,死馬當活馬醫,這時候老闆可能會破釜沉舟,孤注一擲。但也往往能帶來最好的成效。

這也是大家常常看到,新創和小公司比較容易導入敏捷的關係。在大概超過百人左右的組織中,即便想要在一個小部門中導入敏捷,都很容易碰到與企業規範衝突的問題。

要知道,你可是在跟整個組織既有的習慣和文化對著幹,後台不夠硬,身段不夠軟,導入到最後,往往沒能改變組織,先陣亡的會是你自己。

『那怎麼辦?』另一位學員問。

『看起來很難,但也不是沒辦法。』我說『先準備好自己,別聽到台上講的精彩,一股熱血就勇往直前。也別亂找顧問,敏捷轉型不是小事。要幫企業實踐轉型,得長時間陪著企業一步步解決問題,身為顧問,你要非常清楚每一個工具、每一個activity想達成的目的,然後幫助企業選擇最適合的方式,量身訂做,趨吉避凶。』

好啦,先上課吧。先搞懂Scrum每一個角色和活動的意義,到時…才能真的幫上企業實現轉型。

2020年9月14日 星期一

什麼是Azure Cosmos DB?

Cosmos DB是微軟的高可用性、低延遲資料庫。近代的電商網站或微服務應用程式,都強調所謂的高可用性(就是網站或服務很難死掉)與快速回應,一旦系統上線,大概都不太容許服務的中止或延遲。

要實現這個能力,我們在Web或AP層可以做HA(high availability)架構,一般透過負載平衡(Load Balance)搭配Auto Scale的方式來實現。從Azure的Web App到常見的Container解決方案(像是K8S, AKS)的使用,背後很多的考量都是為了實現這樣的高可用性需求,讓流量增加的時候動態的在Web或AP層自動延展(動態增加伺服器):
enter image description here

但你慢慢會發現,當Web/AP伺服器可以近乎無限的延展之後,瓶頸接著發生在DB身上,因為傳統的關聯式資料庫,要實現像是Web Server或AP Server這樣快速延展分流成本相對而言非常高。且在資料的抄寫和回應的速度上要能夠有所保障,其架構都不若Web/AP Server的Scale那麼單純。

過去,用戶端的數量大多可以預測,但現在人手一台平板手機加NB,網際網路上的服務所面對的用戶端數量較之以往不可同日而語,如果要支撐一個全球服務的網站,傳統DB的Cluster架構常常力有未逮。

舉例來說,我們知道在資料處理上,有個CQRS架構(讀者可以上網搜尋相關資訊,例如 https://docs.microsoft.com/zh-tw/azure/architecture/patterns/cqrs ),其基本的概念是,真實世界中,資料查詢(讀取)與寫入的比例其實並非對等的。例如,台灣前陣子的口罩銷售地圖這類的應用,很明顯查詢的需求遠高於更新(寫入),大部分的全球化應用都有類似的情境。而傳統的資料庫程式設計,卻往往將讀取和寫入設計成同樣的工作通道(例如同樣的db access connection)。

倘若我們可以將資料的讀取,分散在全球不同的資料中心,讓讀取的節點可以分散,而工作比例較低的寫入動作,僅在其中一兩個節點進行,再將寫入的資料自動抄寫到不同的讀取節點,將有助於提升應用程式的查詢效能,這對於很多全球化或分散式的應用來說,是一個相對理想的架構。

這時候,NO SQL(Not Only SQL)類型的資料庫,就開始被許多大型網站考慮了,而Cosmos DB就是一個可以滿足這樣需求的資料庫。

透過Cosmos DB,我們可以動態的設定多個讀取區域,讓應用程式的讀取效能大幅提升:
enter image description here

資料會在異動時自動抄寫到不同的資料中心,微軟對資料讀取的效能進行保證,並提供 99.999%高可用性的承諾。開發人員在使用上也很簡單,只需要建立一組CosmosDB Account,就可以建立多個Database以及Container(類似關聯式資料庫的Data Table)。

建立Cosmos DB

要建立Cosmos DB,只需要在Azure Portal新增Azure Cosmos DB即可:
enter image description here

建立完成後,取得endpoint與金鑰,其實就可以使用了:
enter image description here

在取得endpoint與金鑰之後,可以參考筆者的github上的範例:
https://github.com/isdaviddong/dotNetCoreCosmosDB

透過上面這個dotnet core的console app範例,你可以很簡單的把endpoint與金鑰換掉,就可以體驗一下CosmosDB的使用,包含資料庫與Container的建立、資料item的建立與查詢。

CosmosDB的資料item的store是以JSON的形式保存,而查詢也可以透過"類似"SQL方式的語法,在上面這個範例中都有展示。你會看到我們在程式碼中,建立了一個Customer類別,並且動態建立100筆資料存入container:
enter image description here

當你把資料成功的建立好之後,可以回到Azure Portal,你會發現有個資料總管,它是個簡單好用的工具,你可以在資料總管中做簡單的查詢:
enter image description here

當你執行我在Github上的範例,將會看到我們透過C#程式碼新增和查詢資料:
enter image description here

OK,就這樣,高可用性DB唾手可得。

2020年7月26日 星期日

Azure DevOps in Action - 在Build Pipeline當中加入自動化程式碼檢查

enter image description here
在CI Pipeline當中,想要持續提升開發品質,除了單元測試,靜態程式碼檢查也很重要。在Build Pipeline運行過程中,適度的加上程式碼檢查工具,可以幫助我們掃描程式碼的狀況,檢查是否有具有潛在風險的程式碼。

我們常聽到的程式碼壞味道(code bad smells),或是技術債(technical debt),都是靜態程式碼檢查的主要標的。

技術債這個概念是1992年,由Ward Cunningham首次提出。而後常出現於Martin Fowler等大師的文章中。泛指為了縮短開發時程,而在開發過程中做出的妥協(像是安全性、測試、變數的命名…等)雖然可以得到立即的效果,但未來將可能連本帶利付上更大代價。

而SonarCloud,就是這類掃描工具中的翹楚,他是一個獨立的第三方產品,但可以跟Azure DevOps Pipeline做很好的整合。

要在Build Pipeline當中加入SonarCloud進行程式碼檢查非常容易,你只需要到SonarCloud.io申請帳號,並且在Azure DevOps站台上安裝免費套件後,即可進行這樣的掃描。

整個服務完全免費。

安裝SonarCloud

要使用該服務在Build Pipeline上,首先得安裝套件,請至底下網址:

https://marketplace.visualstudio.com/items?itemName=SonarSource.sonarcloud

在出現下列畫面時,選擇 Get it free:
enter image description here

接著在出現的畫面中,選擇你的組織(站台),然後按下Install即可:
enter image description here

安裝完成後,你會發現在建立Pipeline的時候,多了幾個Tempalte:

enter image description here

上面這幾個『…With SonarCloud』的Build Process Tempalte,就是安裝套件後,自動幫您加上的。針對 .Net Core和傳統 .Net Framework環境的開發專案,都有著適合的建置範本可供參考。

申請帳號

在使用之前,我會建議你,先到https://sonarcloud.io/ 這個站台建立帳號,你只需要透過Azure DevOps帳號(也就是MS Account)即可以Single-Sign-On的方式,取得SonarCloud帳號:
enter image description here

進入SonarCloud的Portal後,你可以在畫面右上角看到你的個人帳號,你可以依照需要,新增組織(organization)或專案(project):
enter image description here

你可以選擇上圖1,在既有的組織之下,建立一個新的專案:

enter image description here

首先選擇的是組織名稱(或是建立新組織,上圖2),建立專案比較重要的是設定專案的Key(上圖3),這個Key必須是唯一的,它會跟你的Build Pipeline連結在一起,我會建議你可以把你的Azure DevOps站台名稱,加上專案名稱再加上特定的序號,以此作為Project Key即可,因為該Project Key必須是全球唯一的。這個Key將會作為Build Pipeline將要生成的報表、與SonarCloud專案連結的關鍵。

舉例來說,我在Azure Devops中有一個站台『mytestaz400』,而其中有個專案『testBmiBuild』,該專案的網址就會是:

https://dev.azure.com/mytestaz400/testBmiBuild

那用在這個專案上的project key,我可能就會用底下這樣:

mytestaz400-testBmiBuild-001

其實,你用『mytestaz400-testBmiBuild』當然也行,但有鑑於你的專案中可能有多個Repos,而每一個Repos都可能會有多條Pipeline,所以後面加上『-001』似乎比較理想。

當然,你要在key中再加上Repo Name + Pipeline Bame也可以,看起來更無敵,但這樣就會顯得Project key很長,似乎有些不便。

好,總之填好一個唯一的Project key,並且確認無誤之後,預設狀況下,display name(下圖)會和project key相同,其實無須修改。

有一個地方稍微留意一下,你會發現該專案目前是Public的(下圖A)並且似乎無法修改:
enter image description here

沒錯,因為你採用的是免費方案,預設狀況下無法修改,如果想要改成Private專案,則必須升級(付費)。

那免費專案有什麼缺點呢?

嗯…其實倒也還好,就是你的掃描報告是公開在網際網路上的,任何人只要知道了你的Project key,都有機會可以看到你的掃描結果(其中還包含你該部分的source code)。這一點確實可能造成一些資安顧慮。你可能會擔心如果有人猜到你專案的這個key,豈不是全被看光了嗎? 是啊,所以或許你可以考慮把project key再設定的長一點。(或是乾脆付費成為正式會員吧…) 😊

好,不管如何,最後請按下『Set up』鈕,並牢記你的Project key,我們要接著後續的步驟了…

建立含有SonarCloud掃描的Pipeline

得到了Project Key之後,接著,我們可以回到Pipeline,後面我們以 .net core為例子,底下是填寫Task的參數時幾個主要的關鍵:

enter image description here

首先是上圖A的部分,我們必須建立一個SonarCloud的Service Endpoint,你可以點選『+New』,在跳出的視窗中,輸入Token與連線名稱:

enter image description here

但這個Token該如何取得呢?

你可以在剛才的SonarCloud的Portal,點選My Account --> Security即可產生一個新的Token:

enter image description here

好的,順利取得Token,建立好連線之後,我們重新回到Pipeline:

enter image description here

若連線建立正確,這時候你應該可以很順利地下拉出你的組織(如上圖),選定組織之後,填寫剛才一開始我們取得的Project Key:

enter image description here

完成之後, Pipeline也就設定完成了。

運行程式碼掃描

接著,你可以試著運行這個Pipeline,你會發現,在運行完SonarCloud的『Run Code Analysis』Task之後,報告就出現在底下的網址了

如果你仔細觀察,該網址其實就是剛才你設定的Project Key。

enter image description here

開啟該頁面,你會看到類似底下這樣的畫面:

enter image description here

其中包含了我們最關心的Bugs(上圖B),以及安全性問題(上圖E),技術債(上圖C)以及程式碼壞味道(上圖D)。

我們可以點進去細看問題來源,例如當你點選上圖E中的安全性問題,會發現,它幫我們檢查到我們的 .net core程式碼中有一行潛在的風險:

enter image description here

上面這行code(34行),是讓專案在開發階段碰到執行階段錯誤時,呈現較多錯誤訊息的指令,一般來說我們會建議在正式機上不要這樣寫,以避免可能的資安風險。

你會發現SonarCloud不只幫你掃描,也告訴你為何它認為這是一個問題:
enter image description here

另外,像底下這個建議,則是潛在的技術債建議:
enter image description here

這是因為我們在程式碼中把一個變數設為Public了,其實是沒有必要的。

凡此種種,都是SonarCloud幫我們透過掃描程式碼所發現的可能問題。妥善地逐一檢視這些問題並著手解決,絕對會有效地提升開發品質。

SonarCloud和Azure DevOps算整合得很相當緊密,是一個很值得使用的工具。

熱門文章