2017年2月20日 星期一

the DevOps journey (1) - 敏捷開發不相信時程預測?

這一篇,我們來討論敏捷開發中怎麼看待時程預估,以及迭代開發的意義…

到底專案時程能不能預測?

這是一個常常困擾初踏入敏捷開發領域的新手的問題。

你一定聽到過有人說(甚至,其實上一篇推薦的Martin Fowler的文章中也提到過),敏捷開發不太相信可以進行準確的時程預測,我特別用『預測』這個字,而非預估。中文很有意思,這兩個字很接近,但有那麼一點點的不同。

前面提到過,既然真實世界裡面的需求根本不可能固定,開發人員的素質(每個人每天的產出)也不太可能統一,那我們幾乎已經宣告無法掌握軟體開發專案中,最重要的兩個變因,那這樣怎麼可能精準的預測出開發時程呢?

Martin Fowler在早期的文章中,幾乎是直接告訴你,預測是不可能的。

但這件事情非常弔詭,大概所有開發人員都明白,在需求不確定的狀況下,要精準的預測專案時程、計算出人天數,是幾近不可能的事情。但好笑有趣的地方就在於,幾乎所有的軟體專案的業務或PM,都會要求你在專案起始前,在一切模糊的前提下,估出人天數以便於報價

壓在合約裡面的驗收日期和報價耶!? 難到不需要更精細的計算嗎?

這是繼上一篇提到的軟體開發迷思之後,另一個超級經典、行之有年、卻嚴重違反常識/常理的軟體開發迷思。同樣的問題又來了,難道從來沒有人覺得哪裡怪怪的嗎? 我相信有,但你會碰到各種壓力,告訴你整個業界都是這樣幹的,你區區一個程式設計師想改變這個慣例? 不可能。

因此,開發人員只能習慣性的在時程預估裡面保留一些buffer,業務/PM報價時再加一些Buffer,然後客戶的採購砍一些作為折扣,done,成交。後面就看彼此的運氣了…

不能預估,怎麼辦?

這篇文章一開始的那張圖很貼切,它描繪出了理想狀況與真實世界之間的差異,上一篇說過,兩個軟體專案的需求不可能完全一樣,因為完全一樣你就根本不需要新的專案,買個套裝軟體或直接copy一份即可。因此,我們知道每一個軟體專案都是新的開始,既然是新的開始,沒做過的事情要預估時間,這本來就是一種賭博

這也是軟體開發和工程專案差異很大的地方,傳統的工程專案施工較容易預估人天,因為施工方法固定,需求固定,常常只是在可以控制變因的狀況下,重新做一次而已,但軟體開發不是,每一個專案都是一場新的冒險。

我常開玩笑的跟業務說,你真要我這樣報價,乾脆就隨便猜一個數字,你讓我在模糊的狀況下進行時程和成本的預測,估出來的金額,跟你用猜的其實也差不了多少。需求越模糊,我猜(給出)的範圍就越大(例如,150萬~300萬之間,這是我的預估,你在中間隨便選一個數字報價吧)。另外,越有經驗的開發人員,因為考慮的越多,估出來的時程就可能越保守(這某種程度上解釋了,為何同一個需求,新手估出來的金額往往比較低)。

不過,在台灣軟體專案的報價現在已經變成一種藝術,而非可以討論的科學方法,如果可以,我們當然希望能夠採用time-materials的報價,但往往甲方很難接受,如何解決? 在這邊我們先跳過這個議題,(如果有興趣可以私下問我),我們回過頭來談談,如果開發時程不能預估,那專案管理該怎麼進行? 答案就在迭代…

用迭代開發解決問題

敏捷開發中的迭代行為,簡單的說就是把一整個開發循環,切成一個一個迭代(iteration),如果採用Scrum(迭代開發法的一種),那每一個迭代(Scrum稱為Sprint)都需要產出一個潛在可交付產品增量(Potentially Shippable Product Increment ),這個字很有趣,注意『潛在可交付產品增量』並非半成品,也不一定就是一個大產品的一小部分功能,更不是一個功能的某一部分(例如先做該功能的UI、或是先完成該功能的後端API)。

很多新手分不出什麼是潛在可交付產品增量,常常跟prototype甚至半成品搞混。然而,潛在可交付產品增量根本就是迭代開發的重點,每一個迭代的最主要目標(甚至接近是唯一目的),就是產出正確的潛在可交付產品增量

潛在可交付產品增量不是半成品、不是雛形、不是部分功能,而是一個可提供客戶價值(Value)的最小成果,搭配MVP的概念更容易理解,我們參考底下這張很經典的圖:

常有人誤會這張圖所要表達的意思。以為MVP(Minimum Viable Product)是說,客戶要一台車,你就先給他一個滑板應付一下…不,不是這個意思。

MVP是最小可行性產品,不管是軟體專案或是軟體產品開發,只要面對需求可能常常改變、無法控制,那採用迭代開發與潛在可交付產品增量(或MVP概念),都是一個非常好的技巧。

上圖中MVP的意思是,客戶本質上要的是一個交通/運輸工具(這是Value),但往往腦袋裡直覺就會想到是一台車,卻又說不清楚是什麼車?

『車』這個需求描述很空泛,是雙門? 四門? 多少馬力? 幾輪? 甚麼顏色? 傳統瀑布式專案中的系統分析(SA),往往企圖在一開始就想把所有的細節談定,並且要求客戶,一旦定下來,你就不能改

然而,現在這種『一旦定下來,你就不能改』的要求,客戶越來越不能接受,因為市場瞬息萬變,不是客戶想變,是客戶的客戶(或市場)逼著客戶變。

因此,敏捷開發和MVP鼓勵你不去先談『車』的細節(features),而是先專注在你的目的,我們知道客戶要的是一個交通/運輸工具,OK,我們就在每一個迭代(也許2周、也許4周)中,為客戶逐漸打造出符合他心裡期待的『交通/運輸工具』。

因此,第一個版本是滑板,它不很完美,但能帶著客戶從A點到B點,是一個堪用的交通/運輸工具,它有Value(注意,這很重要),雖然確實還有很大的改善空間。不過,由於客戶看到了實體,客戶的腦袋開始更清楚的知道自己要的是甚麼,以及(特別是)不要的是甚麼。我們的開發團隊每一週持續跟客戶溝通,持續調整對需求的認知、持續蒐集需求、持續開發、持續改善,每一段時間都產出一個更貼近客戶要求的成果(但別忘記,每一次產出的成果,都要能夠運輸,都要有Value),直到達成客戶心中100%的預期 -- 這才是迭代開發,這才是潛在可交付產品增量。

這才是迭代開發、潛在可交付產品增量,與MVP(最小可行性產品)的意義。

好,你回頭對照上圖MVP的上半段,傳統的瀑布式開發會蒐集完詳細且完整的需求,然後先產生出輪子(不能運輸、沒有Value)、接著產生出底盤(不能運輸、沒有Value),過了很久很久以後(也許半年也許一年),才產生出(工程師心裡)一台完美的車子。然而,這時拿給客戶(客戶第一次看到),有很大的機會,客戶會跟你說:『他不能用』,因為他心裡想的車子和你做的不盡然相同,更有可能,他的想法改變了,或是他的客戶的想法改變了…你沒有機會持續修正,沒有機會持續調整,因為你的開發團隊只看規格書,被要求要stick on the plan。

經過幾年的敏捷開發專案,你會發現,需求是不可能在一開始就釐清的,即便一開始就真的讓你釐清(代價很高,多半要花費很多人力),隨著專案的進行,客戶端人員的異動、市場的改變,需求也會一直在變,往往半年後,一開始談的需求現在已經不同了(前面花時間釐清的需求,撰寫的規格書,全浪費了)。現在你知道,強調sitck on the plan的傳統計畫導向式開發,不具備這時代需要的隨時改變、隨時調整的特性。

從這邊,你會看到迭代開發,與傳統計畫導向開發,兩者在本質上有著很大很大的差別。

這樣的開發方法怎麼解決時程問題?

好,我們終究要回頭面對預估的問題,前面提到過,預測精確的時程幾乎不可能,因為大多數的軟體開發根本就是一場冒險,由於所有的事情都是新的,以前沒做過,就算預估也是猜測。加上,客戶的需求無法固定,專案中可能會遭遇哪些問題你也無法預測,所以,精確的預測哪一天會完成根本不可能。

但這不代表我們無法『預估』。

當你用上面提到的這樣的『MVP』、『潛在可交付產品增量』、以及『迭代』的概念進行軟體開發,你就會發現,雖然無法預測哪一天完成,但每一次交付產出的時候(也許每隔兩周、也許每隔一個月),你和客戶雙方都會更清楚的知道,距離雙方的理想目標還有多遠,雙方認知的差距還有多大,每一個迭代都會靠近最終成果更近一點,都會縮小彼此的認知落差,每一個迭代的預估也都會越來越貼近真正的結案時間。

也就是說,我們不能在專案尚未開始進行前,就預測出專案需要的精準人力或完成時間,但我們可以隨著專案的進行,依照前面的經驗和花費過的時間(P),來推估後面還需要花多少時間(E)…

上面這張圖很貼切,我們在專案開始點(B),因為還沒開始做,想要去預估專案完成所需要花費的人力和時程是非常非常困難的,估不准是理所當然。但,我們透過迭代開發、陸續蒐集需求、持續與客戶確認、持續產出潛在可交付產品增量、持續調整更正需求…經過了一段時間,到了今天(T),這時,過去花費的時間(P)就成為一個依據,透過這個依據,我們可以更合理的推估出,還需要多少時間(E)。

因此,我們得到一個結論:

專案開始前幾乎無法精準預估時程,但隨著專案的進行,我們得到的資訊(需求、已花費時間P)越來越多,我們預估的(E)就會越來越準確,只要我們持續修正這個預估

理解嗎? 我舉一個例子你應該會更容易掌握。

你一定用過GPS?。

以前的導航系統很笨,你給他你要去的地方(F),還有開始位置(B),他幫你計算出最佳路徑,然後就叫你依照這個最佳路徑走,不管這個路徑中的路況是否有問題,可能出了車禍導致交通堵塞,也可能前面正在施工無法通行….但,以前的導航只會叫你照著這個路徑走(stick on the plan),因為它認為這是計算過後的最佳路徑。

但,現在的導航都很聰明,還可以連上網,它在幫你計算出最佳路徑之後,還會隨時觀察交通狀況(例如Google導航會有即時路況),當它發現前面路段有塞車,可以立即重新計算出新的路徑,讓你用更短的時間抵達,這就是自適應性,也就是應變能力。而傳統的導航則是計畫驅動式導航,如同過去的瀑布式開發,一旦SA團隊把規格書開好,就拍拍屁股閃人進入下一個專案了,至於開發過程中發生什麼問題?很抱歉,不關我的事(保重,我在另一個案子了)。

以前的軟體開發大多是是計劃導向,我們要求大家遵循計劃,制定計劃的人並不在乎你知不知道目標,也不在乎你要不要隨時依照路況去應變。

之所以過去行之多年,是因為早年路況良好、變數不大、不會常常塞車。但,現在時代不同了,現在路況變幻莫測、瞬息萬變,不能夠再依靠以前的計畫導向,而必須強調目標導向,強調自適應性(應變能力)。

這是敏捷開發和計畫驅動開發最大最大的差異,至於時程預估,你會發現,現在Google導航的預計到達時間幾乎都非常準,且你越靠近目的地,算出來的結果就越準。是了,它會參考過去的資訊與路況持續修正,和過去舊版的導航只依照公里數和平均速度來推估有著很大的不同,沒錯,它是敏捷導航是也。

------
這一篇我們釐清了概念,後面我們會陸續介紹該如何做? 用甚麼工具做?

2017年2月19日 星期日

the DevOps journey (0) - 敏捷開發真和每個開發人員有關?

#不想看廢話,直接按這裡跳到重點

這年頭大多數人已經沒有耐心,已經很難看完一整篇長篇大論,因為這樣,這幾年我們寫blog的時候,幾乎都零零碎碎,這使得許多學員面對知識的掌握,也變得片片斷斷。

如果可以,我希望能夠把我們這幾年面對軟體開發方法,以及ALM/DevOps的一些經驗,有條理一點的整理出來,如果你有興趣,不妨嘗試慢慢的看看,如果實在已經不習慣看長篇文章,也可以挑有興趣的重點來讀。

開始一段新的旅程

這一系列的標題中有個字眼『journey』,第一次看到這個字…是在一場研討會,我忘了是TechEd還是Build,印象中研討會的主題是Metro Style App(現在應該不多人還記得這個名字了吧?),主講人用journey這個字眼來描述學習一種技術的過程,我覺得真TxD的太貼切了…

不知道你有沒有發現,最近五年,軟體開發的潮流根本就是一場journey,而且非常像是我最愛的影集Star Trek Voyager中的旅程。你該怎麼形容這段旅程呢? 圖乎其來? 充滿意外? 不僅如此,大夥摸著石頭過河…似乎根本沒人能告訴你往哪個方向才是對的,你也不知道怎麼走才會到終點…這差不多就是這部影集當中的旅程。

若用來描述最近五年軟體開發技術的發展,根本是整個貼切到不行,而且從Web、前端、行動裝置、開發方法、框架…幾乎都逃不過這場journey魔咒,所有的技術都是在途(on the road),一點都沒有發展成熟的跡象…

當然,如果旅程總是只為了看到終點倒也世俗了些,旅程的過程中還是有很多意外的小驚喜,以及足以令人回味的點滴,不管是你早已遺忘的Metro Style Application,或是充斥各種消耗型框架的所謂前端開發技術,又或者是我們要討論的這個主題 – ALM/DevOps。

關於開發方法的重要性

身為一個以寫程式起家的開發人員,就一個軟體開發人員的生命週期來看,我算是到了很老的時候,才願意承認軟體專案/產品的開發過程中,最重要的可能不是開發技術,而是開發方法。

技術能力的強度,是進入軟體(專案/產品)開發領域的入場券,它就是一塊敲門磚,技術能力的強度,決定了你敲門的力道。然而一但當門打開,你開始了一個專案,進入了一個團隊,參與了一個產品的研發,技術能力對成果的影響力,可能就開始慢慢式微了。請注意,個人技術能力可能會影響開發團隊產出的品質和數量,然而開發團隊產出的品質和數量卻很可能跟軟體專案的『成果』沒有絕對的正相關。

什麼意思? 我們看過太多技術能力很強的個人或團隊,但經手的專案或產品開發依舊是失敗的很徹底的。例子很多,不好一個一個舉。你可以盡其所能地回憶你自己參與過的專案,你應該會發現,團隊的技術能力決定了你能不能做某個案子(或產品),但開發方法和團隊的素養,則決定了案子做得好不好,能不能順利結案。

由於軟體開發與一般工程營造迥異的特性,使得軟體開發這件事情,並不單單的工程行為,也是創作行為…這導致,追求技術的極致,其實對軟體專案沒有絕對的幫助,技術能力一旦到了某一個點之後,對於整體專案的幫助就迅速下滑…

像是上面這張圖,技術能力到一個臨界點之後,再怎麼提升,對於軟體專案的價值與效益其實影響開始遞減。

怎麼觀察? 如果你身為PM或Team Leader,當你開始慢慢發現,手上專案所發生的問題/瓶頸,大多跟技術無關,而是跟時程、溝通、預估…等非技術因素有關的時候,就表示,你的團隊可以在技術能力提升上稍稍休息,而該開始補強一下開發方法和專案管理能力。

總的來說,我接觸到的年輕團隊,大多都是技術能力遠勝於專案管理能力,至於開發方法,則往往都還有很多的成長的空間。

正在糾結?到底要不要踏入敏捷開發?

如果要談論開發方法,這幾年你不可能避開『敏捷開發(Agile Development)』,相較於傳統的瀑布式開發,敏捷開發在意的是自適應性(Self-Adaptive這個字硬翻很怪,我覺得你可以把這個字想成『應變能力』),而傳統的瀑布式開發,在意的是『計畫導向』,簡單一句話,就是要你『stick on the plan』。

上課時,我會推薦學員一定要看Martin Fowler的這篇文章: http://martinfowler.com/articles/newMethodology.html

如果你需要中文翻譯,他位於:
http://www.jianshu.com/p/e042ed1d79b0

我假設你已經讀完了,我要說,這篇文章非常經典,字字珠璣,他解釋了為何這個時代的軟體專案,應該採用敏捷開發。

我覺得只需要扼要說明,請一邊參考底下這張圖。

最早期(1960年代),軟體開發是毫無章法,雜亂無章的情況,迫使技術團隊覺得應該要有一些制度,因此借鏡了傳統的工程營造之類的專案管理概念,一段時間的演進之後,建構了像是CMMI之類的軟體專案管理方式,但後來(最近10年)許多人慢慢發現,似乎並不可行…

不可行的原因出自於幾點:

  1. 傳統的專案管理和工程方法試圖把人當作可替換的資源,但軟體開發並非全然是施工(Implementation),還有創作的成分(而且非常高),每一個程式設計師的產能可能天差地遠完全不一樣,把開發人員視為可替換的資源根本是個錯誤的假設。
  2. 傳統計畫導向的開發方法相信,設計(Design)和實施(Implement)可以分離,甚至,設計需要比較高階的人力,而實施只要muscle就好。但敏捷開發認為,設計和實施無法分離,每一個開發動作都同時包含了設計和實施的成分在。
  3. 我們幾乎永遠不可能有確定的需求。因為完全一樣的程式碼(需求)你根本不用重寫,只需要copy一分(或買套裝軟體)就好,所以,每一個軟體專案幾乎肯定都是獨特的。再加上現今市場變化快速,需求的固定幾乎已經不可能。
  4. 從不確定的需求,衍生出,準確的預估幾乎是不可能實現的。因此,軟體開發沒有stick on the plan這回事。(那要怎麼辦呢? 後面再說)

因為上面這幾個與傳統工程之間的核心差異,導致軟體開發用工程方法、SOP、甘特圖來管理、期待他跟其他的工程施工一樣順利,似乎非常的不切實際。

除非你的需求永不變動、除非你每一個開發人員的產能完全相同,除非你有無窮盡的開發人員可以替換,否則,傳統的工程管理方法根本無法進行軟體開發的專案管理。

不知道你是否曾經好奇過,其實我們早已用工程方法(甘特圖、要徑法、CMMI、SOP…等)來管理軟體開發很多很多年,如果,這個方法是錯的,難道從來沒有人懷疑過? 難道從來沒人覺得工程方法怪怪的?

有,但很抱歉,過去,設計出這些管理方法的人,很可能自己根本都不寫程式。就像設計員工管理制度的HR/Manager,本身可能根本不是一般員工,這些制度的受益者/設計者,從來都不是制度的使用者…也因此,身為程式設計師的你,如果夠敏感,很可能早就覺得哪裡怪怪的,但,你無法改變,因為制度的設計者不是你。

一個新方法

然而,敏捷開發法的出現,和過去工程方法誕生的方式完全不同,敏捷開發的概念是由有實務開發經驗的開發人員所設計,這跟過去建構工程方法的管理階層,自己從來沒下來搬過磚塊的狀況有著很大的差別。

敏捷開發法的設計者認為,你不能把程式設計師看做資源,軟體開發的團隊,也根本不是傳統的施工團隊。如果真要比較,軟體開發團隊比較像是一組球隊(team),團隊中的成員每一個都有自己獨特的角色和任務,人人不可或缺,無法輕易替代。每一個球員的產值也大不相同,高低之間可能相差數十倍,無法相提並論或替換。

這是一個認知上的巨大差異,一旦當你把軟體開發團隊,從傳統工程(例如營造)的施工團隊,轉變成球場上的團隊,你會發現你的思維將會瞬間轉換。你將不再相信傳統的專案管理方法,你帶領團隊的方式也會大不相同。

DevOps/ALM/Agile與軟體專案管理

一旦你用一個全新的眼光看待軟體開發,你對於軟體開發的方式,採用的專案管理工具、做法,都將會跟以前有所不同。

也因此,我們這篇先談敏捷開發,然後接著,我們要來看,在這個思維底下,如何管理軟體專案,如何管理軟體生命週期,如何透過工具來實現DevOps…

如果你要惡補你的敏捷開發概念,請參考底下這本書:
http://www.books.com.tw/products/0010691328

下一篇,我們談時程預估與迭代的概念。

2017年2月2日 星期四

asp.net Web開發框架 (8) - 使用krajee進行非同步檔案上傳

這一篇介紹Bootstrap File Input這個套件,之所以會被歸類到asp.net Web開發框架系列,是因為整個套件和我們採用asp.net走SPA架構(不管是用WebForms或WebAPI)都非常的速配。

檔案上傳範例

首先我們有一個範例位於Github,如果你想要測試,下載這個範例後,基本上已經擁有所有你需要的檔案。請使用Visual Studio運行index.html這個檔案(對,沒錯,是pure html5),執行起來的畫面像是底下這樣:

這個套件精彩的地方是上傳預覽畫面,你會看到上圖中,它除了支援中文、多檔上傳、非同步上傳,還支援圖片預覽、Mp3檔案甚至可以撥放,會不會太誇張了點? 是的,人家就是支援…更不用說UI的部分很有誠意的原生支援正體中文(包含zh-TW的多國語言js)這也是我們使用此套件的原因之一。

如何使用

整個套件的使用可以完全採用AJAX方式(當然也支援傳統的submit/postback),搭配我們的SPA架構非常之合拍,也因此,你在運行該範例的時候,會發現只需要執行index.html即可(當然伺服器端接收檔案的部分還是需要Server Code,這我們後面介紹ReceieveFile.aspx的時候再說明)。

如果你看index.html,會發現程式碼如下:

我們先看55行的部分,這是定義一個Input標記,其中的multiple意味著支援多檔上傳,data-show-preview="true"則是支援顯示預覽視窗。當然這些設定都可以在後面透過js來更動。

而66行javaScript所呼叫到32行的setupFileUploadBox(),就是進行相關的設定,其中language: 'zh-TW' 指定了多語UI採用中文(請留意這也是我們在19行引用了locales/zh-TW.js的原因),而後面幾個參數應該不需要太多解釋,showUpload是顯示上傳按鈕,uploadAsync說明了採用非同步上傳,maxFileCount指定了檔案上傳上限。

比較重要的是uploadUrl,這個參數指定了非同步檔案上傳的接收位置,由於這個範例採用WebForms,因此我們指定ReceieveFile.aspx,這部分我們待會後面介紹。(我覺得熟悉WebAPI的開發人員可以輕易的把ReceieveFile.aspx這隻改成WebAPI,因此我暫時就不動手改了)

先看上面程式碼43行的on後面定義的幾個事件hook:

fileuploaded事件發生在所有檔案上傳完成之後(嚴格說起來是伺服器端回應之後),而filepreupload事件則發生在所有檔案上傳之前,如果你還需要hook其他事件,可以參考該套件的官網說明(這套件的事件方法支援都算相當完整)

我們在fileuploaded這段程式碼當中,也只是把上傳完成之後,伺服器端傳來的檔案路徑(URL)給顯示出來而已…

伺服器端如何接收檔案,如何把特定訊息往前端送? 這個我們待會下面介紹,先看看該套件需要引用哪些css與js (我都整理在Content/bootstrap-fileinput資料夾下):
上圖是所有你需要引用的檔案,基本只有四個,如果你需要更多的功能(例如檔案排序…),則可能需要引用更多,其中1,3是與CSS相關,2是核心的js,4則是多國語言(中文需要的js)。

撰寫伺服器端接收檔案的C#程式碼

前端的code非常簡單,接著我們就要來看看後端接收檔案的程式碼如何撰寫。這個套件基本上走非常標準的http post把檔案往後端送,如果你採用非同步檔案上傳,則檔案是被一個一個往後端送的,也就是說,如果你上傳五個檔案,後端接收檔案的API會被呼叫五次。

後端接收檔案的API可以用WebAPI或是單純的apsx頁面撰寫,我們先看.aspx的版本:

你會發現這其實是一個很簡單的WebForm aspx,我們可以在PageLoad當中透過Request.Files接收到檔案,沒什麼特別的。這個範例刻意用aspx來撰寫,其實當然也可以用WebAPI,但在行為上其實都一樣。

由於多檔上傳時,前端的套件是把檔案一次一個往後端送,因此如果你同時上傳五個檔案,其實這個 ReceieveFile.aspx.cs 的Page_Load會被呼叫五次,因此,其實理論上 Request.Files.Count 在正常狀況下只會是1。

後面36-41行就是把取得的檔案儲存。然後43-46行回應一個很標準的JSON給前端。

這邊請特別留意,該套件要求,你撰寫的接收檔案的API(嚴格說起來不該稱之為API,應該叫做Backend Service),必須回應JSON,即便你啥也不想傳給前端,都必須回應一個空的{ }。

你會看到我們new了一個13-19行定義的customResponse物件,其實裡面也只有一個URL屬性,你可以自己加上其他的屬性,如果需要的話。不過最少,也應該回應一個檔案上傳到後端之後的id或URL,範例中我們選擇回應URL,因此你會看到我們在44行組出被上傳檔案的URL,然後在46行回傳給前端。

就這樣,其他的code全是例外處理。

整個範例的source code在: https://github.com/isdaviddong/KrajeeFileUploadExample
該套件的官網: http://plugins.krajee.com/file-input

整個套件採BSD 3-Clause License,這年頭好心人真多…
--------------------------------------------
如果需要即時取得更多相關訊息,可按這裡加入FB專頁。若這篇文章對您有所幫助,請幫我們分享出去,謝謝您的支持。

2017年1月25日 星期三

丙申年臘月廿九 - 煮咖啡論技術

先說個幾天前發生的小故事,最底下是整理好的年度文章摘要。

可能很多年輕人已經開始不知道,每年的農曆十二月,被稱作臘月。而每逢臘月,則不免讓我聯想到俠客行裡面的『賞善罰惡臘八粥』(如果對這主題有興趣,為了不破壞你的閱讀樂趣,請自行參閱金庸俠客行),我猜這個世代大多數的人,應該完全沒有喝過什麼臘八粥,估計對李白俠客行,可能也陌生的很。

回首2016丙申年,發生了不少事。
因此,在年底不免想找找久未聯繫的三五好友,大家聚聚。

由於大夥都是資訊領域的技術同好,彼此也都在業界耕耘多年(都老了?),但聊起過去種種,卻彷彿就像昨天才發生的事情。然而事實上,這幾年業界幾乎所有的技術都換了一輪,火紅的話題也都翻過了好幾遍。相較之下,我們幾個老朋友們聊到的技術也好、故事也罷,即便還稱不上是歷史,肯定也都已經是往事了。

席間,朋友說到:『技術更迭好像已經是這世代資訊人員的宿命,金庸的俠客行,是1965年出版的,至今已經流傳超過50年,而李白的俠客行,則是1200年前的作品,1200年耶! 這是什麼時間概念? 古人說的『立功、立言、立德』,稱為三不朽,這年代哪還有什麼可以不朽,昨天剛炒熱的技術今天就過時了,更別說寫過的書,才沒幾年就得淘汰,而且越來越快…』另一位朋友笑著說:『你們出的書還可以活個幾年,要絕版了圖書館不小心還能找到,好歹一時也能出個名,像我手上開發出的系統,設計過的架構,估計只要我離職,立馬就會被改的面目全非,要不然就是被直接換掉,根本是屍骨無存』。

這讓我想到俠客行中的一段:『縱死俠骨香,不慚世上英。誰能書閣下,白首太玄經。

時代真的很不同了,如今技術變化太快,潮起潮落可能轉眼間。技術人員手上做的這一切,早晚終究都會過去,也不一定會有人紀念…不過,不管如何,明天太陽依舊會升起,過去的又何必留戀? 每天都是新的,能夠專注在『現在』,才是幸福的事。

我跟朋友們說,我最近第二次看Spencer『禮物』一書的心得:『還記得你人生當中最快樂的一段時間嗎? 那段時間,往往不是我們過的最舒適的時候,也並非是擁有最多掌聲、財富...的時候。

大多數人記憶中最快樂的時光,常常是我們專注在某件事物上、專注在當下的那一段時刻。像是...小時候在公園裡和同伴玩耍不肯回家,深夜時獨自一人寫程式始終不想熄燈,站在講台上底下的人狂舉牌子告訴你超時卻不肯結束...』

我說:『在當下的時候,往往才是人們最快樂的時候...

Spencer在『禮物』這本書裡告訴我們:如果,你的『現在』正擔憂著『未來』的生活(工作)、懊悔著『過去』的境遇或失敗,他要你努力強迫自己,先別管過去和未來,請先專注於現在。

即便我們對未來的掌握度越來越低,但也別讓對過去的懊悔以及對未來的憂慮拿走了你對現在的專注。因為,我們唯一真正擁有的,就只有現在。只有在你專注在這一刻,去體驗它,才能夠慢慢想起以前孩童時期簡單的快樂 -- 那種最初,讓我們每個人寫程式到深夜,不是因為結案或進度的壓力,而只是因為興趣的那種簡單的快樂。

大夥閒聊到這兩年技術的改變與發展,從雲端運算、SaaS帶來的改變、大數據、IoT、最近的Chatbot、人工智慧……最後,我們沒人敢說未來會怎麼發展,這個時代的變化速度,已經遠遠超過我們任何人的預期了…

於是乎,一夥人乾了手中的咖啡….

決定從今年開始,努力練習試著強迫自己,除了憂慮未來,更用心專注於現在。
---------------------------------------------------------------------------------------------------------------
後記 : 底下隨手整理了過去一年的幾個比較重要的系列。今天是丙申年臘月最後一天,過了農曆年,再也沒有任何藉口留戀過去,2017年早已經是新的開始了。

2017年1月24日 星期二

關於LineBot(8) - 發送Template Message

LineBot傳送訊息的形式,除了單純的文字、貼圖、圖片之外,還有一個比較特別的形式,稱之為Template Message,這種形式的訊息本質上是一個選單,長得像是底下這樣:

上面這個是三種Template Message中的一種形式,稱為Buttons Template Message,另外兩種形式是Confirm Template Message(下圖左)與Carousel Template Message(下圖右):

Confirm Template Message好理解,基本上它就是一個確認(confirm)對話框,可以讓用戶選擇Yes/No之類的回答。而Carousel Template Message其實就是多個Buttons Template Message,橫著擺可以左右scroll。

我們先從Buttons Template Message開始看起。

關於Buttons Template Message

我們剛才說過,Buttons Template Message這種形式的訊息本質上是一個選單,包含著一張圖片(下圖1)、一個標題(下圖2)、以及一些文字(下圖3),並且可以設置1-4個選項(下圖4-6,範例中我們只設置了3個):

當你的Bot送出這個訊息給用戶,用戶可以點選底下的選項(上圖4-6的文字),這些選項被稱為Action,這Action也有三種不同的類型,分別是:

  1. MessageAction : 幫用戶說一段指定的文字訊息
  2. UriAction : 直接在Line裡面開啟指定的網頁
  3. PostbackAction : 觸發一個可以你的WebHook收到參數的postback

其中UriAction最容易理解,用戶點選該Action,Line就會直接開啟某個網頁。

而MessageAction可以用來幹嘛? 它可以讓你幫用戶說特定的話,實務上應用時,你可以透過這個機制先設計好一些固定的指令,讓WebHook看到這個指令就能進行相對應的工作(其功能等同於用戶在對話框中說了這個指令)。

而postback則是不出現任何訊息,直接把特定參數傳給WebHook,WebHook就可以進行特定工作。

讓LineBot發送Buttons Template Message

我們看底下這段程式碼,它的功能就是讓LineBot發送Button Template Message:

你會看到我們先在第2-8行建立三個Action(因為是範例,因此每一種一個)。第一個是MessageAction,當用戶點選『點選這邊等同用戶直接輸入某訊息』,Line就會『幫』用戶說『/例如這樣』:

而『點這邊開啟網頁』這個選項則很單純,當用戶點選上圖中該action,就會自動開啟Google首頁。

而上圖中最後一個action,被點選時,WebHook會收到一個postback event,其JSON內容類似像底下這樣:

{"events":[{"type":"postback","replyToken":"d7aooxxo154c6d94oo01xx28coxoxfd04","source":{"userId":"U8x2788OOXX53422082e301b80xb00030","type":"user"},"timestamp":1485251255286,"postback":{"data":"abc=aaa&def=111"}}]}

留意其中的data,就是我們在上面程式碼第8行中指定的postback data,當你的WebHook收到上面這樣的指令,就可以進行相對應的工作。

OK,瞭解了Actions之後,我們繼續回頭看上面的程式碼,我們在第11行建立一個ButtonsTemplate。留意一下第13行的altText,因為並非每一種device都可以顯示Template Message,因此,你可以設定替代文字,這會被用於無法顯示Template Message的場合(例如PC版的Line、手機板的Line訊息預覽畫面、手機的Notification通知畫面…etc.)。

而14,15兩行是標題,這沒什麼,17行則是提供一張大圖的url,Template Message會將你指定的圖顯示出來。程式碼的最後,我們把前面建立好的三個Actions丟給ButtonTemplate物件,然後透過PushMessage指令送出,done.

此範例原始程式碼位於 : https://github.com/isdaviddong/LineBotSdkExample/blob/master/LineBotSdkExample/WebFormExample.aspx.cs

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

2017年1月15日 星期日

Microsoft Graph API (2) - 使用OAuth進行身分驗證,並取得用戶資訊

我們前面曾經和大家聊過,如果要幾句話說清楚Microsoft Graph API是啥,其實並不很容易,但透過先前的介紹你大概可以知道,Microsoft Graph API身為MS Office 365所有資源存取的API連線入口,負擔著讓開發人員得以輕鬆存取雲端資料(資源)的重要角色,只要你想存取Office 365、OneDrive、OneNote…等各種資源,基本上你就必須與Graph API打交道:

不管用戶採用的是個人帳號(Microsoft Account)或企業帳號(Orgnization Account),MS Graph API讓開發人員可以用單一Endpoint允許各種不同帳號的身分驗證,當然也會依照權限吐出不同的資源。

這一篇我們接著來看,如何透過OAuth連結MS Graph API,並且取得token抓取用戶資料。
(如果讀者對OAuth還不甚了解,請先參考這裡)

建立Microsoft應用程式

首先,若您是開發人員,要使用MS Graph API(或者是要與O365/Onedrive等系統做SSO),你必須先在微軟的站台上建立一個Application,建立的位置在: https://apps.dev.microsoft.com/ ,進入之後會看到底下畫面:

請用您的Microsoft Account登入,您就可以建立一個自己的應用程式:

點選後會出現底下畫面,依照步驟,輸入名稱,按下建立鈕:

建立完應用程式之後,請接著點選『產生新密碼』:

記得保留上面彈出視窗中顯示的密碼,待會我們會使用到(它只會顯示一次,請務必自行保留)。

接著,請在平台這邊選擇新增平台,然後選Web,這是因為我們範例中是透過Web App整合方式來進行Demo,如果你的情境是要在行動裝置上存取Office365(or Onedrive/onenote…etc.)中的資源,當然就選擇行動應用程式:

接著該頁面會要求你輸入重新導向URI,這部分如果不理解其意義以及為何需要,請參考我們以前分享過的OAuth介紹文,底下的URL就是你取得Auth Code的導回網址,由於我們只是要在Local用IIS Express測試,因此我們網址輸入Localhost:

至於port 13555怎麼來的,它是我們在VS2015中建立了一個Web Application Project之後自動產生的,如果你想測試但懶得寫程式,可以拿我在GitHub上面的範例來修改:
https://github.com/isdaviddong/MsGraphApiExample20170115

接著,請至少為您建立的應用程式新增user.read這個權限,如果你想玩其他的權限,可點選下圖的『加入』鈕:

設定完成之後,請按下儲存鈕(請務必保持Live SDK勾選):

撰寫OAuth/SSO登入頁面

完成了上面所有的設定之後,您的應用程式就建立好了,請取得應用程式中的『應用程式識別碼(Client_id)』與『密碼(Client_Secret)』,應該長得像是底下這樣:
Client_id : 98d720db-xxxx-xxxx-xxxx-7430359c1c10  (這是假的)
Client_Secret : teoXXXeJp8bxxxxSO2HjhNG (這當然也是假的)
有了上面的資訊就夠囉。

接著請撰寫底下的頁面(如果你想測試但懶得寫程式,可以拿我在GitHub上面的範例來修改:https://github.com/isdaviddong/MsGraphApiExample20170115):

然後在VS2015的IIS Express中運行此頁面,你會發現點選上圖中的Button之後,會被導引到AAD2.0的登入畫面:

您可以用Microsoft Account或Orgnization Account登入都行,因為Graph API都支援。(但用哪一種帳號登入後面的畫面將會有所不同,我們在本文中的展示是以Microsoft Account登入)

用戶登入之後,系統會跟他要求資訊存取權限:

你會發現上圖(1)的地方就是您建立您的應用程式時,所上傳的圖示,而(2)的地方是您的應用程式名稱,(3)是您跟用戶要的權限 (4)是…就只是一個用戶非點不可的按鈕。

當用戶成功登入之後,網頁會被導回你原先的頁面,但這時,請注意網址列QueryString已經出現了Auth Code:

請取得該Code,我們要用此Code換Token。

後面的動作我用postman示範,取得auth code之後,請對https://login.microsoftonline.com/common/oauth2/v2.0/token這個位置做POST呼叫,並傳入下列參數:

上圖中的(1)是你原本在應用程式以及要求Auth Code的登入URL中所設定的redirect_url(必須都相同),(2)與(3)是一開始我們請你保留的client_id和client_secret,而(4)則是剛才從QueryString取得的Auth Code,成功呼叫之後,你會發現上圖下方API回給我們了Access_token,將其保留起來,該Token就是我們依照權限取得用戶資訊的令牌了。

透過token抓取用戶資訊

拿到token之後你就無敵了,基本上你可以用該token來取得所有用戶授權你抓取的資訊(如果用戶授權你更新,你當然也能修改用戶資訊)。

底下我們依舊用postman來示範,例如,倘若你要取得用戶身分,可以對https://graph.microsoft.com/beta/me這個網址做GET,並傳入底下資訊:
上圖中的Authorization參數當然傳遞我們剛才取得的token,而結果就是上圖下方取得的JSON資訊,你會看到其中有用戶的個人身分資訊。

就這樣,如我你還想取得其他資訊,可以透過哪些API呢? 很多,例如底下是常見的:

更多詳細的資訊可參考 http://graph.microsoft.io/en-us/docs

有沒有發現,當你熟悉了OAuth之後,透過API要取得用戶的資訊其實非常容易。
--------------------------------------------
如果需要即時取得更多相關訊息,可按這裡加入FB專頁。若這篇文章對您有所幫助,請幫我們分享出去,謝謝您的支持。

2017年1月13日 星期五

asp.net Web開發框架 (7) - 使用Vue.js進行表單雙向繫結

先前我們分別討論過了使用Vue.js進行表格資料渲染以及渲染時動態產生的功能性按鈕之後,接著,我們來看Vue.js在表單上的資料繫結。

範例依舊是我們先前使用的那個:
source code : https://github.com/isdaviddong/AspNetWithVueBinding
測試網址: http://ex20170110-aspnetwithvuebinding.azurewebsites.net/ 
(如果該網站被我不小心殺掉,請FB訊息提醒我,網站裡面的資料由於是共用的,可能會被玩壞或出現詭異的內容,一概與本人無關XD )

看過了先前的介紹之後,你大概已經知道,動態產生的『編輯』鈕:

該按鈕會在用戶點選時,將畫面導入到AddNewOrUpdate.html這個頁面,進行編輯的動作,而首頁上的新增鈕,其實也是將網頁導入到AddNewOrUpdate.html這個頁面。唯一的差異在,如果是按下編輯鈕,該筆紀錄的guid會被帶入:

這個動作怎麼實現的,答案在我們先前進行動態Button渲染時候的Vue instance中的edit method:

這使得如果用戶按下的是新增鈕,AddNewOrUpdate.html這個頁面將不會收到QueryString,若用戶按下的是編輯鈕,則是用戶欲編輯的record的guid。

因此,我們就不難理解AddNewOrUpdate.html這個頁面一開始進入後的javaScript code,就是判斷是否有Guid:

我們把guid保留在12行的變數中,30行之後的document ready,則是判斷是否有guid,如果有,則在36行呼叫LoadData,把該筆資料載入。而不管有沒有資料,31行的SetBinding都會把UI上的HTML表單與記憶中的vd_StudentRec做繫結:

所以你不難猜到,LoadData這個function,在從遠端載入完資料之後,會放到哪一個地方? (別往下看,先想一下…)

答案當然是:

記憶體中的vd_StudentRec,沒錯。其實,上面這段程式碼是為了說明,否則在實務上你把回傳的資料result.Data,直接放入Vue.js的instance就好(vm_student.rec=result.Data),因為vm_student.rec根本就是vd_StudentRec的代理。

好的,資料塞入vm_student.rec,會觸發Binding,Vue.js的Binding Engine會去找HTML UI上的Form_StudentRec:

然後它發現你寫的v-model指令,這表示該欄位要進行雙向繫結(two-way binding),因此,Vue.js engine就會把你繫結的物件vd_StudentRec中的屬性值帶入,這就是畫面上的表單資料會自動呈現的原因。

同樣的,由於是Vue.js是設定雙向繫結,因此進入AddNewOrUpdate.html這個頁面之後,不管是有沒有透過QueryString的參數載入一筆預設的資料,任何表格上使用者的異動都會被反應到記憶體中的vd_StudentRec物件中,因此,當用戶按下畫面上的『新增/更新』鈕:

頁面上的JavaScript程式碼,做的就是把記憶體中的vd_StudentRec物件中的值,透過呼叫後端的AddNewOrUpdate方法,寫回後端資料庫中,程式碼如下:

後端的AddNewOrUpdate方法也很簡單,就是取得傳入的物件,然後判斷一下是否有guid(1),如果沒有,應該是新增(2),如果有,就從資料庫中找出來更新(3):

就這樣而已,相當單純。

以上提到的完整程式碼都在 https://github.com/isdaviddong/AspNetWithVueBinding 請自行參考。

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