2016年12月31日 星期六

關於LineBot(7) - 使用Line Login實現oAuth SSO(單一登入)

上一篇寫完之後,我心裡想,我連Line Notify都寫了,那沒理由不寫Line Login吧,雖然,我猜不一定很多人對這個主題有興趣。

而且我覺得,要寫這個主題,我應該要先多寫一篇關於oAuth和SSO(Single Sign On)的文章才行,否則我覺得大半讀者可能如同到了北京一般(伸手不見五指)…不過,oAuth和SSO有空再寫,我先趁記憶猶新,把Line Login整理一下。(補充:後來寫了,請參考這裡)

背景與用途 : 基本上Line Login是讓網站或手機App開發人員做用戶身分識別(Identity)與單一登入(SSO)用的,也就是說,網站或手機App開發人員,不需要自己管理會員帳號密碼,可以透過Line Login這個符合oAuth標準的Identity機制,來進行用戶的身分驗證與個資取得,以達成單一登入。你一定曾經用過某些網站,在登入或註冊帳號的時候,並非要你填入新的帳密,而是連結(整合)到Google帳號或是微軟的Microsoft Account,即可完成身分驗證與登入。

這有什麼好處?

對用戶來說:

  1. 不用記得很多組帳號密碼

對網站或手機App開發人員來說

  1. 不用費心保管個資,由oAuth Provider(例如Google、以及本文要介紹的Line Login)幫你管理。
  2. 不用管個資,就沒有被竊的風險。
  3. 如果你有多個網站或服務,倘若都支援某一種驗證方式(例如Line Logic),則用戶登入了A網站,當連結到B網站的時候,無須再次登入(此即為SSO)。

好,我當作各位都知道了,如果對於oAuth或SSO還有疑問,以後有機會我再說明。我們先來看,要怎麼將你的網站連結到Line Login…

順帶一提,其實一直以來,Line的各種動作都看得出Line的企圖心不小,目前提供oAuth的廠商,幾乎各各是大廠,Google、微軟、Yahoo、沛米(不認識? 這是我投資參與的公司)…Line在這個領域要衝進來當玩家,很是任性…

註冊Line Login

在使用這個服務與你的網站整合前,你必須先註冊一個Line Login的服務,申請位置位於: https://business.line.me/zh-hant/services/login

(他說他有五億用戶耶…)
請給他按下去,一連串的動作之後,會出現底下這份表格:

有幾個填寫需要注意的地方:

  1. 上圖(1)的部分是你這個服務(網站、或app)的圖示,在要求用戶登入的畫面會出現。
  2. 上圖(2)的部分是你這個服務(網站、或app)的名稱,在要求用戶登入的畫面會出現。
  3. 上圖(3)的部分讓你選擇你是要在你自己的網站,或是你開發的App來使用這個服務,由於本文介紹的是一個網站的例子,因此請選擇Web。
  4. 這個是你的Line帳號 ID(或是你的帳號所註冊的公司的名稱),這無法改,它會在要求用戶登入的畫面出現。

填寫完畢之後,請按下確定,接著會出現一個我認為非常多餘的畫面,讓你確認資料是否正確,如果沒問題,請按下『申請』鈕:

按下申請,如果成功後,會出現底下的畫面,請留意只有Channel ID以後會用到,其他看看就好,然後直接按下右下角的綠色按鈕前往Line Developer網站:

當你按下上圖右下角的綠色按鈕前往Line Developer網站,之後,會出現底下畫面:

請注意,還有幾個地方要填寫,留意上圖左邊的選單,我們會用到(1)和(6)也就是選單的Basic Infomation和Technical Configration這兩個部分,Basic Infomation的部分就是上圖的畫面右半邊,請點選(2)的Edit,並且隨意把資料填寫完畢。然後按下上圖(4)的Show,取得一個重要的資訊,也就是Channel Secret。

至此,你會得到使用Line Login的兩個重要參數,一個是Channel ID,另一個是Channel Secret,它們長得像是底下這樣:
Channel ID : 1504723632 (這當然是假的)
Channel Secret : e2b481ca6ebd222aa653150c1c6375571444720232 (這當然也是假的)

接著,請按下上圖中的(6),會出現底下畫面,讓你設定Callback URL:

你可以點選上圖中的Edit,來設定Callback URL,由於我要在local測試,因此我輸入localhost的URL(如上圖)

說真的,如果你看到這邊覺得一頭霧水,我大概也猜的到為什麼。我自己也當過初學者,我一開始看oAuth和SSO的東西時,也不知道這到底是什麼意思。但久了之後,我就知道,為何以前看別人的貼文時,沒有多少人在解釋上面這些東西是什麼。因為,如果你知道oAuth的基本概念,上面這些根本不需要解釋,你甚至還會覺得我前面寫得太囉嗦。但,如果你不熟悉oAuth,上面這些解釋的再詳細也幫助不大…所以,我也就不解釋了,但我會盡快寫一篇oAuth和SSO的文章的,在我沒寫好之前,請先上網找找oAuth相關資料與概念,相信會有幫助( 補充: 請參考http://studyhost.blogspot.com/2017/01/oauthsso.html)

好,設定好了CallbackURL,並且取得Channel ID與Channel Secret之後,我們就已經擁有所有需要與Line Login整合的資訊了。接著就是如何開始實作…

開始將你的網站連結到Line Login

首先,請先建立底下這樣的網頁(有沒有,跟前面介紹Line Notify的時候好像):

請留意,上面程式碼中的11行裡面的redirect_uri必須和你在設定時設定的CallBackUrl相同(因為我在測試,因此是localhost),當你點選上圖的Button後,你會發現,頁面被導入到Line的登入畫面:

這個登入畫面是由Line提供的,因此你不會知道用戶的帳密,當用戶輸入正確的帳密之後,會出現底下的畫面,讓用戶確認,你提供的服務(網站或手機App)可以取得他的個資:

留意上圖中的(1)、(2),就是先前你設定時,輸入的服務名稱…

當用戶按下上圖中的『同意』鈕之後,會被導回你的CallbackUrl指定的頁面,並且在QueryString中傳遞了一個重要的Code:

請取得這個Code,我們要用該Code換取可以代表用戶的Token。後面我用postman示範,當你取得Code之後,你可以透過POST https://api.line.me/v1/oauth/accessToken ,並且傳遞底下的參數,來取得Token:

注意上圖中,我們透過POST呼叫,把Client_Id(1)、Client_Secret(2)以及相關參數傳遞給Line Login,Line Login則回傳(4)的Access_token給我們。

透過這個Token,我們就可以辨識用戶的身分,取得用戶的資料了,例如:

上面這個http get,將Token傳遞給Line,則Line回覆給我們用戶的displayName、mid、大頭照、以及StatusMessage。

透過這樣的方式,我們就實現了以Line Login提供的oAuth機制,來進行身分驗證的工作了,如此一來,我們自行開發的網站、App,就無需自行設計登入與會員管理機制,直接用Line Login提供的服務進行整合即可。

是不是很方便呢?(呵呵呵,我猜的到你的回答…)

官方文件在:
https://developers.line.me/web-api/integrating-web-login
https://devdocs.line.me/en/#how-to-use-the-apis

oAuth與SSO參考說明在 http://studyhost.blogspot.com/2017/01/oauthsso.html

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

2016年12月29日 星期四

關於LineBot(6) - 不用申請Bot也能發訊息的Line Notify

前面我們討論到了很多跟LineBot有關的機制,但有朋友提了一個問題,如果我單單只是要透過程式碼發訊息給用戶,一定要申請並建立一個LineBot嗎? 其實不用。

一直以來,有一個比較不被重視的機制,叫做Line Notify,其實它已經誕生很久,IFTTT的Line通訊整合,就是用Line Notify做的。

Line前幾天的TectPulse中,對Line Notify的定義是…

第三方服務提供商可以利用 LINE Notify 套件開發通知型的應用,讓外部網站的服務和應用能透過 LINE Notify 官方帳號傳送純文字、貼圖或圖片式的服務通知給用戶,例如天氣預報、貨到超商請取貨、匯款成功、交易完成等。LINE Notify 就像一般的聊天機器人一樣可以加入一對一的對話視窗中,也能加入群組中。

他這樣講你聽得懂? 聽得懂就好…ㄟ…不懂也沒關係,我稍微翻譯一下。

簡單的說就是,如果開發人員(或店家、或企業)只是想要發通知給用戶,或和用戶進行基本的互動,那倒也不一定需要自己申請一個LineBot(因為未來用戶可能得要加入很多LineBot,收到很多不同單位傳來的訊息,這樣用戶會很煩,最後把你的Bot封鎖,或是根本忘了它,而且,商家(開發人員)申請LineBot要付$$$$$很貴的$$$$$),但若你用現成的LineNotify,其實也可達成此功能(且Free)。

LineNotify就是底下這個…

其實,它就跟一個Bot一樣,你根本可以把它想成一個共用的Bot。它一樣可以發訊息給你的用戶,跟你的用戶互動,你只要能夠Access這個Line Notify Bot,就可以透過它來發送訊息。

怎麼做呢? 首先,你還是要申請一個Line Notify的使用憑證,基本上就是取得Client_id與client_secret。申請網址在底下:
https://notify-bot.line.me/zh_TW/

Line把這個申請藏得很好,請拉到最下面,找到…『登錄服務』:

你看,這藏得多麼隱密啊。

點選後用你的Line帳號登入,會出現底下的表格:

請妥善填寫,然後按下『同意並往下一步』。填寫的重點只有兩個:

  1. 服務名稱和服務圖片,會出現在LineNotify用oAuth2機制跟你的用戶請求Access Token的時候出現。
  2. Callback URL會用來作為oAuth2授權後取回authorize code的返回網址。

輸入完成之後,會出現像是底下的畫面,你就可以成功取得Client_id與Client_Secrt:

喔喔,等等,剛才上面出現了一個部分開發人員可能不很熟悉的『oAuth2』身分驗證機制。如果你對oAuth不熟,讀到這邊可以就此放棄….好啦,也可以硬著頭皮看下去…

背景說明 : 由於LineNotify需要讓你對特定用戶發訊息,但你沒有該用戶的Line Id,也沒有申請一個LineBot去取得該用戶的Line User Id,那怎麼發訊息呢? 答案是需要一個Token。當你得到這個Token,就可以透過該Token對用戶發訊息。你要跟用戶要Token,Line系統在產生Token時,就需要用戶登入他的帳號密碼,但用戶當然不能給你他的帳號密碼,因此LineNotify採用的就是oAuth2機制。(補充:後來我補寫了oAuth與SSO介紹,請參考這裡)

別怕,背景說完沒聽懂也沒關係,我們實際操作一下。首先,請先建立底下這個網頁:

請特別留意該網頁中第19行的Button,按下去之後是一個URL轉址的動作,整個目的是用來取得一個authorize Code,你要用待會取得的這個Code再去換Token。(參考官方文件 https://notify-bot.line.me/doc/en/)

請特別注意上述程式碼的第9, 10行,其中第9行要換成你剛申請到的Client_id,第10行則是要填寫你寫在CallbackURL裡面相同的URL,因為我們要在用戶端測試,所以我填寫localhost網址,要這樣測試,這邊你必須能夠在用戶端Run一個模擬的Web伺服器(由於我很幸福的使用著Visual Studio 2015,因此這是一塊蛋糕)。

當用戶點選上面這個按鈕,會被導引到類似底下這樣的畫面(在進到底下這個畫面前,用戶會被要求用他的Line帳號密碼登入,當然登入畫面是Line提供的,因此你不可能知道用戶的帳密,這就是oAuth2的用途,登入後就會看到底下畫面):

請留意上面標示的數字:

  1. 就是你剛才申請時,上傳的LineNotify帳號圖式
  2. 就是你剛才申請時,填寫的LineNotify帳號名稱
  3. 你的開發帳號名稱
  4. 用戶帳號頭像

只要用戶選擇『同意並連動』,他(用戶)的Line會接收到LineNotify帳號(bot)傳遞的訊息(像是底下這樣):

同時間你的網頁會被導回Callback URL,你可以從QueryString取得Code,像是底下這樣:

這個Code很重要,我們要用這個Code來換Token。

後面的動作我用Postman來示範,當你取得這個Code之後你可以透過以POST呼叫底下的URL來取得Token:
請注意要傳遞的參數包含固定的grant_type,你剛才取得的Code(上圖1),你的redirect_url,你一開始申請時取得的Client_id(上圖2)與Clind_secret(上圖3),然後就會取得用戶的token(上圖4)。

取得Token之後就好辦了,這時候你可以透過對https://notify-api.line.me/api/notify的POST,來向用戶傳遞訊息,訊息內容放在POST body中:

ㄟ,別忘了,你要在Headers傳遞剛才取得的Token:

OK,用戶收到訊息囉,格式是【LineNotify服務名稱】訊息內容,像是下圖最後一則訊息:

that’s it.

坦白說,用LineNotify可能比LineBot還難一點,但如果你要大量的發送訊息,卻不想要申請一個自己的Bot(不想付貴桑桑的Line Bot費用),LineNotify也許是一個比較經濟的選擇。

demo source code : https://github.com/isdaviddong/Line_Notify_Example 

原始文件: https://notify-bot.line.me/doc/en/ 
相關課程: http://www.studyhost.tw/NewCourses
--------------------------------------------
如果需要即時取得更多相關訊息,可按這裡加入FB專頁。若這篇文章對您有所幫助,請幫我們分享出去,謝謝您的支持。

2016年12月28日 星期三

asp.net Web開發框架 (3) - 如何讓asp.net WebForms也能搭配Vue.js和bootstrap並享有SPA開發架構?

你沒聽錯,事實上asp.net WebForms也能走SPA(Single Page Application)的開發架構,並且跟在前端的Bootstrap與Vue.js框架搭配得很好。

技術的使用存乎一心

記得曾經對學員說過,不管你用哪一種開發方式,只要對開發技術有足夠的熟悉,並且瞭解基本原理與前因後果(再說一次,學習時,為何永遠比如何重要),那實在無需追逐潮流,你依舊可以在技術變遷中掌握到重點。

曾經聽過有些開發人員從WebForms轉換到MVC走了一條坎坷的路,並且轉換的理由很是讓人莞爾,這邊略過不提。但我想說的是,asp.net的MVC和WebForms走的是兩條截然不同的架構路徑,兩者並沒有取代性的問題,千萬別誤以為MVC主要目的就是用來取代WebForms用的

順帶一提,那我認為什麼是真的有取代性的? 舉個例子,物件導向程式設計幾乎取代了結構化程式設計;資料庫存取的ORM技術,幾乎取代了ADO.NET等非ORM的資料存取技術;C#中的Linq取代了傳統的物件查找方式。這些,則是有取代性的…

所以,這一路上曾經聽過有些開發人員從WebForm轉換到MVC卻走了不少冤枉路,然後換到MVC之後卻用和傳統ASP一樣的思維在開發系統,這樣換比不換更慘

如何在asp.net WebForms中實現SPA?

警語 : 接下來的內容很是驚悚,如果你對於asp.net WebForm或是MVC的『標準寫法』有著異於常人的堅持,不容妥協,那請自行斟酌,小心閱讀這篇。

還記得我們先前曾經說過的嗎?SPA對我們團隊來說的價值是『沒有任何從伺服器端Render HTML到前端的行為』,這麼做有哪些好處,在前面我們已經多次的提到過。

但整個WebForms的架構根本就是從伺服器端Render一堆的HTML到前端啊,這架構怎麼會符合SPA呢? 是的,這沒辦法,因為WebForms生來就不是走SPA路線的,在WebForms誕生的那個年代,根本還沒有SPA的影子。整個WebForms的行為都是在模擬傳統的Windows物件導向程式設計,因此任何的WebControls,舉凡Button、InputBox、DropdownList…全都是透過後端的HTML Render Engine動態產生的。

這導致,如果你用了任何WebControls,那根本不可能走SPA。所以,不要懷疑,如果你想得到SPA的種種好據,我們的第一個建議是,在專案中請用HTML Controls(加了runat=server的純HTML Tag)取代WebControls。甚至,你完全用純HTML Tag而不用任何的WebControls或HTMLControls我都覺得很OK。(是的,這代價不小,或失去一些WebForms的特性,但會保留MasterPage和ServerSite Events,請自行斟酌)

那這樣怎麼寫程式呢? 別急,請繼續看下去…

我們用實作來介紹這個特異獨行的WebForms SPA架構。

  1. 請先建立一個基本的Empty Web Application專案,請記得勾選WebForms:
  2. 請安裝Bootstrap、Vue、isRock.Framework.Web(重點當然還是這個)這幾個nuget套件:
  3. 接著,請建立一個default.aspx WebForms頁面,並撰寫底下的前端程式碼,我建議讀者對照前面我們介紹過用WebAPI所撰寫的SPA,該專案的前端是純html頁面,而這個default.aspx除了附檔案是.aspx之外,也幾乎是純html頁面:

    請留意UI的部分是32-42行,這裡除了form tag之外幾乎都是純HTML,而上面的JavaScript和先前我們介紹過的WebAPI SPA也幾乎完全相同,唯一的差異是呼叫Service Layer API的JavaScript Function換成了CallPageMethod(注意19行),這是指要呼叫一個名為BMI的PageMethod(並且把身高體重para傳入當作參數)…
  4. 等等? PageMethod是啥玩意兒? 請開啟default.aspx.cs撰寫底下程式碼(注意9-20行):

    請讀者留意,這是一個WebForms的code behide頁面,我特意把Page_Load方法留了下來(雖然裡面沒有撰寫任何指令)。重點在於9-10行的PageMethod。PageMethod是asp.net中很特別的一個存在,它可以被前端HTML頁面上的JavaScript直接呼叫,並且回傳JSON…這…這行為…不就跟我們要的SPA一模一樣嗎? 是的,正是如此。
    至於這個名為BMI的Method,其中的指令碼應該不用多解釋,基本上就是跟先前介紹WebAPI版本SPA的Business Logic一樣,計算出BMI並且回傳。唯一的差別是回傳型別,這裡使用的是PageMethodDefaultResult<T>。
  5. 執行的結果如下,和WebAPI撰寫的SPA一模一樣,頁面不會submit,而是透過AJAX進行所有與伺服器端的互動:
  6. 等一下等一下,那Services Layer呢? 說好的Business Logic分離呢?
    其實,這個PageMethod本身就是API形式的存在,你完全可以把他當作WebAPI一模一樣的方式來看待(也就是Service Layer),但差別在於PageMethod的Route(嚴格說起來是endpoint)是跟著.aspx頁面的,也就是說,每一個.aspx頁面上的PageMethod,其呼叫路徑跟WebApi的/api/ModuleName/{MethodName}不同,而是/{PageName}.aspx/{MethodName},而框架中寫的的javaScript Function 『CallPageMethod』則是幫開發人員簡化了這個呼叫,因此注意開發人員可以在default.aspx中透過javaScript(default.aspx第19行)直接呼叫CallPageMethod即可以AJAX方式調用這個伺服器端的API。
  7. 那…Business Logic呢? 不是應該抽離Service Layer嗎? 上面範例中的PageMethod(BMI)不是把商業邏輯給寫進去了?
    是的,如果你堅持在PageMethod中,不要撰寫資料庫存取或是商業邏輯,把PageMethod中的程式碼改寫成和WebAPI中一樣(只呼叫另外一個Business Logic Component),我相當贊成。然而,這堅持很難,我們實務上也沒這麼做。這跟WebForms(.aspx)的原始設計行為有關。由於WebForms的每一個.aspx頁面本來就是為了呈現某種功能,因此相關的商業邏輯程式碼如果重用性不高,直接放在PageMethod中我覺得尚且可以接受,這個妥協並不會導致後續的維護和交接變得複雜(但最好放在同一個Region區塊中),這樣做等於某個頁面的所有相關程式碼,都在該頁面中,我覺得管理上甚至更為方便。

但,如果考慮到重用性,你確實應該跟先前WebAPI的範例一樣,額外建立一個BO,成為獨立的Class或Project,然後再在PageMethod中呼叫調用。這樣的架構更為完善,而且日後很容易可以轉變成為用WebAPI的SPA專案。但基於對傳統的.aspx開發人員來說,要養成這個習慣不容易,因此實務專案上這部分我沒有堅持。

OK,看到這邊有沒有發現,如果透過PageMethod,傳統WebForms專案要走SPA其實可能比asp.net MVC還來的方便,也容易理解。基本上就是透過前端頁面上的JavaScript呼叫同一個頁面上,撰寫成PageMethod的C#後端API, 然後忍住不用WebControls,不要Postback(Submit)就可以了。

每一個PageMethod也可以做Unit Test,將來抽出Business Logic Component(或當下就抽出Business Logic Component,在PageMethod中不要撰寫商業邏輯,只調用Business Logic Component)都非常容易。

這一個專案架構有一個好處,可以輕易的讓舊有的WebForms專案與新架構並存混用,不需要一次全部打掉翻掉,只需要讓新的功能透過新開發架構開發,逐漸把舊的WebForms轉換成SPA架構即可。

用WebForms走SPA有何好處?
  1. 首先,你不用拋棄原本的WebForms專案,卻可以用新的前端開發套件或效果(光這點就很吸引人)
    因為太多asp.net舊的專案還活得非常好,為了新的架構需要拋棄舊的專案,這得不償失,透過這樣的方式,新頁面可以跟舊頁面並存混搭,逐漸移轉,同時新頁面也可以享有新的前端套件(像是toastr、sweetAlert、眾多的前端js圖表特效…etc.)
  2. 讓開發團隊成員開始熟悉SPA
    SPA肯定會是未來的Web開發走向(好吧,我說了不賭未來,這個『肯定』請讀者自己判斷),不管以後用MVC、WebForms、或是任何其他開發方式,前後端分離的很徹底的SPA,都是較為漂亮的架構,團隊成員越早熟悉越好
  3. 和設計師(designer)可以密切合作(我們自己很在意這部分)
    如同先前透過WebAPI走SPA的方式相同,你會發現這樣設計出的網頁,設計師和程式開發人員可以持續合作,共同完成一個前端頁面,不會有設計師改過開發人員就跳腳,或開發人員改完設計師就罷工的情形出現…
  4. 所有前端套件都可以盡情使用
    Bootstrap,Vue…任何fancy的前端套件,在以往的WebForms專案中幾乎難以相容,走SPA架構的WebForms,跟任何前端套件都不互相衝突,讓開發人員能夠大展身手…
  5. 兼顧開發速度與後續維護
    WebForms先前最讓人詬病的部分,包含重複的postback影響執行效能(PageMethod的效能不可能比優化過的WebAPI好,但會比傳統的postback好很多)、臃腫的ViewState、WebControls動態生成的HTML難以維護,與前端js框架不相容…etc,在上面這樣的架構中完全不會發生,但開發人員卻又可以同時運用Page_Load, Page Exception等伺服器端事件,也可以輕易地透過MasterPage機制處理重複使用的頁面區塊,搭配上PageMethod可以很輕鬆的撰寫新型態的SPA Web應用程式,當你用熟了這種開發方式,你會發現在測試、維護、交接、執行效能上一點都不輸asp.net MVC,雖然比走WebAPI的SPA來得稍慢,但絕對是優於傳統的WebForms。

這種開發方式是為了與早期WebForms專案(與開發人員)兼容的一種變形,能夠在最少學習成本的狀況下,讓WebForms開發人員直接踏入SPA架構領域,非常有趣。

好玩嗎?

我知道很多開發人員在WebForms和MVC之間很是掙扎,但透過上面的範例,你不難發現,其實駕駛的好,AE86也能跑贏GT-R,不是嗎?

後記 : 還是得再說一次,每一種做法的背後都有著不同的需求與原因,無法放諸四海皆準,上述做法是一種折衷和妥協,也和很多人習慣的WebForms開發方式不見得相同,提供讀者參考,使用的情境必須與你的實際需求和系統環境有所搭配或取捨才行。

範例: https://github.com/isdaviddong/WebFormsSPA

口說無憑,要更多實例? 下一篇,立刻來看看如何配合Vue.js輕易完成Data Binding…
本系列索引http://studyhost.blogspot.tw/2017/01/blog-post.html
--------------------------------------------------------------------------------------------
如果需要即時取得更多相關訊息,可按這裡加入FB專頁。若這篇文章對您有所幫助,請幫我們分享出去,謝謝您的支持。

2016年12月26日 星期一

關於LineBot(5) - 透過程式碼讓Linebot發送圖片、貼圖

這幾天我們更新了Nuget Package LineBotSDK,加上了一組非靜態的bot類別,現在你可以透過底下這段程式碼,直接建立一個bot物件實體,來操作所有有關發送Line訊息、或是圖片、貼圖…等工作…

注意上面這段程式碼,其中的圖片URI必須是https,否則會發生錯誤,圖片支援.png的格式(估計其他也支援,但我沒試)。

透過物件實體(Object instance)來操作,省去了呼叫PushMessage的時候一直要傳遞AccessToken的不方便,同時在記憶體管理上也比較安全。

這組bot類別還提供了傳送貼圖的功能,例如:

上面這段程式碼可以發送出底下這樣的貼圖:

為何傳入packageId與StickerId參數 1 , 2 是出現上面這個貼圖呢? 目前LineBot可以用的貼圖就那幾組,請參考底下連結: https://devdocs.line.me/files/sticker_list.pdf 

上面這個連結顯示出了目前有可以用的PackagId與StickerId。

別忘了,使用上述的功能必須先在你的專案中加入最新版(0.3.9)的LineBotSDK喔:

OK,盡情的傳送貼圖吧,Have fun~

----------------------
相關課程: http://www.studyhost.tw/NewCourses/LineBot

2016年12月23日 星期五

asp.net Web開發框架 (2) - 基本SPA架構


前面
我們談過了非技術的背景原因之後,我們在這篇會正式開始跟各位介紹我這幾年在團隊中最常使用的開發架構。在大部分(超過九成五)的Web開發情境下,最近幾年我們多半採用SPA(Single Page Application)架構。這邊我們先定義一件事情,有些人覺得(認定)SPA就一定只有一個html page,整個應用程式就都在這個page當中運行,所有的功能都在這個頁面上呈現,所以也沒有換頁這件事,但,對我們來說並不是這樣的。

SPA概念在.NET或非.NET陣營上都有著不同的定義,但對於我們團隊而言,其重點在於『沒有從伺服器端動態Render HTML到前端』的這個動作,伺服器端與前端Browser之間的交互,只有幾個行為:

  1. 下載靜態HTML Pages(大多時候都不只一頁)
  2. Client(Browser)與Server(WebAPI)之間的Http Call與JSON傳遞

前端頁面也幾乎沒有Form Submit這個動作,取而代之的是AJAX非同步呼叫。

that’s all.

這樣的規範(你要說是限制也行)讓開發架構變得非常單純,應用程式很自然地分成了三層:

  1. Presentation Layer(view),採用HTML+javaScript , 運行於用戶端Browser
  2. Service Layer,採用asp.net WebAPI或PageMethods,運行於伺服器端IIS
  3. Business Logic Layer,採用C#寫成的class,運行於伺服器端Application Server或IIS(和Service Layer佈署在一起)

為何不用任何『從伺服器端動態Render HTML到前端』的機制?

伺服器端只對前端傳遞JSON與靜態HTML(而非透過C#程式碼動態生成的HTML),這是一種限制,但這個限制利大於弊,所有透過伺服器端往前端Render HTML的技術(ASP、PHP、ASP.NET WebForms、ASP.NET MVC View),一不小心都很容造成相依性、並且導致重用性降低,因為開發人員會忍不住把Business Logic寫在其實是Render View的展示層程式碼中(如果你曾經在MVC的Controller或是WebForm的Page裡面寫存取資料庫或撰寫運算的code,那就是了)。你很難在上述狀況下誘導開發人員把前後端抽離,讓程式碼的職責清晰。這在只有一個人開發時可能不會覺得有什麼問題,但在團隊開發的時候,這導致分工、維護、交接都很困難。且如此一來,要能達成如同SPA架構一樣,讓Business Logic和Service可以被任何種類的前端重用(例如行動裝置、Desktop Application…etc),也近乎不可能。

同時,動態Render到前端的HTML是一個黑箱,我們不知道那些HTML Code怎麼產生的(嚴格說起來不是真的不知道,而是無法由自己掌控),在開發階段也很難看到預覽的效果(你非得有個伺服器跑起來才看的到效果)。另外一個問題是設計師很難與程式開發人員互動,一旦你把設計師設計好的純html+css改成razor view或是asp.net WebForm(或是更傳統的PHP/JSP/ASP),基本上這個檔案別想讓設計師再維護了。綜合了許許多多的原因,我們的Web專案幾乎讓大部分的html page都保持原始(設計師能夠持續維護)的狀態,前端永遠只是加上javascript,便於與WebAPI所撰寫的Service Layer互動,透過javascript把後端傳遞來的JSON資料呈現在頁面上。

因此,要持守這個架構,採用SPA對我們來說是最理想的選擇。此外前面提過另一個優點,當我們透過SPA開發,很自然地變成了View <--> Service <--> Business Logic這樣的結構,在現今動不動客戶就要Mobile解決方案的狀況下,我們可以輕易地把View換成Native App,對於後端核心邏輯來說,完全不需要調整。

如何輕鬆地使用SPA開發Web應用程式

既然是團隊開發,我們當然要讓開發人員可以很容易的follow這個開發架構,因此我們透過Nuget來簡化使用此開發框架的流程,現在,開發人員只要建立一個最基本的asp.net Web Application,並且引入isRock.Framework.Web套件即可(是的,我們將其公開在Nuget上了,因此讀者也可以使用),步驟如下:

  1. 建立最基本(Empty)的Web Application(別忘了至少要勾選WebAPI)
  2. 建立好的專案如下,然後請加入 bootstrap、jQuery、Vue、isRock.Framework.Web套件(重點當然是最後這個,其他幾個是順便。或是你也可以直接引入一個Nuget套件isRock.Framework.Web.AllPackages,它會幫你引入其他所有需要的Nuget套):
  3. 套件都安裝完之後,我們打開自動幫我們產生的ExampleController…

    這是一個已經寫好的樣板WebAPI,它的功能是擔任Service Layer,被前端的JavaScript以AJAX方式呼叫,它吐回前端的Always是一組JSON。

    請留意它的Route,呼叫的方式會是 api/Example/{MethodName} ,這什麼意思呢? 請先看它的Code,在第12-15行的部分,assemblyLauncher.ExecuteCommand是我們的重點,這個類別可以幫我們運行特定的Business Logic Component。程式碼中的TestClassA當然就是我們示範用的Business Logic Component。

    這一段需要解釋一下。為什麼這麼設計呢?

    首先,這個開發架構很清楚地把Business Logic Component與Services Layer分開,因此每一隻WebAPI的APIController,在設計上,我們都希望它代表著一塊模組(或一組功能,或一組API),而WebAPI只是一層Service介面,實際上系統核心功能的實現,是透過後面的Business Logic Component,也就是上面程式碼中的TestClassA。所以,你會看到我們的ExampleController,設計成讓呼叫端(JavaScript)可以直接要求某一個Method,然後該WebAPI就幫我們去執行對應的Business Logic Component(TestClassA)中的該Method,取得回傳值之後,再透過WebAPI以JSON的形式回傳給前端的JavaScript。

    知道這個概念之後,您就不難理解,上面的程式碼主要是透過WebAPI撰寫的Service Layer,可以透過assemblyLauncher.ExecuteCommand運行到我們的Business Logic Component(範例中的TestClassA)。接著,我們來看,Presentation(View)的部分。
  4. 請在專案中加入一個index.html,並且撰寫底下內容(這是一個從身高體重計算BMI的範例):

     
  5. 上面這段html很簡單,就是輸入身高體重的UI,我們讓用戶輸入身高體重,透過後端的Business Logic算出BMI值。當然,後端也應該要有一個計算身高體重的類別,因為我們在專案中建立一個BO資料夾,在其中撰寫一個類別如下(其實,更嚴格的作法是將BO獨立撰寫成一個專案,而非放在同一個專案中,這部分容後再敘)。注意BMI方法傳入身高體重當作參數,計算出BMI值(體重kg/身高m的平方)回傳:
  6. 你可能發現了上面這個類別繼承自BusinessLogicBase,這有很多用途,以後我們再解釋,請留意我們做了一個計算BMI值的Method(名稱為BMI),有一個傳入參數(bodyInfo物件),其回傳型別是ExecuteCommandDefaultResult ,另外請注意我們把核心運算(以後包含資料庫存取)都撰寫在這個類別中,最後真正的計算結果我們透過ExecuteCommandDefaultResult 的Data屬性回傳。
    當然,如此一來,我們在Service Layer(WebAPI)那邊也需要跟著做一些調整(把原本的測試用BO(TestClassA)換成正式的BO:

    注意我們把原本測試的TestClassA換成了真正的Business Logic Component (BO.Health),這樣一來,當我們從用戶端透過javaScript呼叫 /api/Example/BMI 時,就會運行到BO.Health這個類別的BMI方法。
  7. 最後,我們再調整一下前端的Code,加入JavaScript(同樣的,理想狀況應該是獨立的.js檔案,這是範例為求單純因此我們塞在HTML頁面中),讓靜態的HTML可以和WebAPI撰寫的Service Layer互動:
    我們加入了JavaScript來抓取用戶在頁面上輸入的值(14行),並且透過ExecuteAPI這個由isRock.Framework.Web框架提供的JavaScript function來呼叫伺服器端的WebAPI(16行傳入的參數表示要呼叫ExampleController所對應的類別的BMI Method),並且將參數para傳遞到伺服器端。伺服器端的BO(Health.BMI)會自動被執行(前端的Para參數會自動被帶入BMI Method的bodyInfo參數),執行後會把回傳值回傳到前端,透過前端18行的alert顯示出來…
  8. 執行結果如下:

請留意,在這個架構當中,擔任Service Layer的WebAPI,我們要求開發人員,不得在其中存取資料庫,當然也不得在其中撰寫Business Logic,它只有一個任務,就是呼叫Business Logic Component,並且pass相關的參數(未來Service Layer還會擴充成負責安全控管、權限、Logging、例外處理…等與商業邏輯沒有直接關係的infrastucture 機制,但目前我們先不談這部分)。所以你會發現,所有核心的運算與資料庫存取,應該透過Business Logic Components(也就是C#寫成的Class)來開發與運行。

這使得整個架構中核心的部分幾乎都在Business Logic(BO),由於這部分是很單純的Method,因此也可以輕易地進行Unit Test或TDD,而Services Layer的部分,在專案開發過程中,我們只讓Project/Team Leader做必要的調整(一般來說幾乎不改寫,只會依照系統模塊相對應的增加APIController),如此一來,當架構確定之後,前後端開發人員都專注於撰寫各自的程式碼,前端是View(JavaScript/HTML)、後端是Business Logic(C#),責任歸屬非常明確,每一個開發方式和風格也都相同,後續的重用性和維護便利性也大幅提升。

同時,你會發現View的撰寫永遠是HTML/CSS/JavaScript,設計師不管做任何調整,開發人員都可以接手繼續進行開發,反之亦同。而切割開來的Services Layer(WebAPI),可以給任何異質系統呼叫,不管是行動裝置,桌面應用,或是其他平台的API,都可輕易透過標準的HTTP+JSON進行API的調用。而後端的BO(Business Logic Component)則被保護得很好,並維持單純且統一的撰寫方式。在BO的部分我們一般要求要進行Unit Test,確保核心邏輯的正確性。

前面說過,這樣的架構,含括了近九成五以上的Web開發專案,長期下來再也沒有混雜不堪的程式碼,同時交接與維運或程式碼的重用性都大幅提高。

上面只是一個很簡單的範例,後面我們會繼續介紹,如果面對更複雜的需求該如何開發。

後記 : 千萬別以為,我們團隊這樣做,就表示我們覺得只能這樣做,解決方案只有當下的最適解,並沒有永遠的最佳解,時空不同往往答案就不同,團隊或客戶種種因素都可能影響架構師或開發人員的判斷依據。(白話:以上架構僅供參考)

source code :
https://github.com/isdaviddong/isRockWebFxBasicSample

--------------------------------------------
上一篇: asp.net Web開發框架 (1) - 天下武功,唯快不破
下一篇: asp.net Web開發框架 (3) - 如何讓asp.net WebForms也能搭配Vue.js和bootstrap並享有SPA開發架構?

2016年12月14日 星期三

asp.net Web開發框架 (1) - 天下武功,唯快不破


講技術之前,先讓我說說最近發生的幾件事情…

這時代沒有什麼趨勢好說的…
前些日子,我在某個研討會當中,碰到一位學員,他在上課前找到了我,搶先問我先前提過要開的架構設計課程,最近一次何時會開呢? 我很高興他對這個課程有興趣,但是卻只能黯然地跟他說,今年實在不一定有空了。

除了時間的因素之外,其實也因為有另一個顧慮,就是這幾年的資訊科技變化實在不小,在頻繁的變化當中,要建構出相對穩定的架構確實不易,也因為有感於此,在這場研討會中,我很猶豫的要不要提更多我對於這幾年技術變化自己的看法。

別忘了技術的最終目的永遠是應用
前陣子有位大神問了我一個問題,當然,大神的功力都是很強的,問的問題當然也讓人無法小覷,所以沒多久我就投降了,我直接告訴他我不知道。原因很簡單,因為我沒用過,而沒用過最近也不會用到的東西,我大概不太會去碰。因為『吾生也有涯,而知也無涯。以有涯隨無涯,殆已。』數千年前,莊子就知道這件事了,我又何必勉強呢?

得知道,所有的技術都是為了應用,學技術本身當然是愉快的,如果學技術本身就可以賺錢,那何樂而不為?然而非也,技術能用到對的地方,才能賺錢。不是說賺錢很重要,而是玩物喪志恐怕也不是成年人可以一直走的路,也因此,誠實的面對自己的知與不知,選擇在有限的時間內,將有需要的技術盡快地掌握,是我這幾年來學習的原則。

捷徑只有一條
我曾經聽一位老師說過,在專案管理當中,只有一個方法能立即加快速度,那就是減少浪費。加人、進修、提升能力、凝聚向心力…都很可能治標不只本,再說一次,只有一條路可以真正的加速,那就是減少浪費。其實,學習也是。請原諒我這麼說,但這年頭很多的大學,四年的時間基本上是浪費了,你常常看到為何有些年輕人不需要念大學就可以有所成就,因為,雖然學習是重要的,但依照別人排好的時間表以及重點來學習,它不是為你量身訂做的,對學習者來說當然弊多於利。要知道,現行台灣的教育制度也不是培養人才的最佳解決方案。但沒辦法,對於一般人來說,恐怕這是唯一的路,所以莘莘學子跟著大家一起念大學,之後跟著大家一起找不到工作。如果你不想做『一般人』,就得要想辦法減少浪費,從學習到專案,都是如此。

新舊從來都不是問題
我有一個朋友,其實算是前輩了,他公司還是用Delphi開發系統。幾年前第一次聽到的時候我很是訝異,我不是訝異Delphi還活著,它活著我知道,但在這個技術一個月小改三個月大改的時代,怎麼會有勇氣一直用Delphi開發呢? 有好多好多的障礙和疑惑橫在我面前,客戶真的能接受嗎? 開發人員會不會覺得無法發揮或進步? 新的需求(例如行動裝置、雲端)能夠支援? 我有太多太多問題了…但幾次分享之後,我慢慢明白了。特別是當他告訴我,客戶有新的需求時,他們如果真的做不到,他就誠實地跟客戶說他們不會,做不到,客戶也不會為難廠商;但只要能解決客戶問題的,他們的回應速度始終很快,他們真正的在意客戶的需求、以及客戶的反饋,長期下來,客戶也發現資訊系統真正要能解決問題,用新技術或舊技術其實還真沒太大差別,反而不穩定的新技術或新玩具,在還沒帶給客戶實際效益之前,自己往往就先淘汰了。長遠來看,新技術對客戶一點意義都沒有。

這讓我認真地思考,在追逐新技術(玩具)的工程師本性之下,是否需要抓穩一條界線,讓團隊和企業(客戶)真正能夠得到效益,而並非因為好玩有趣,就把所有的技術塞到專案裡面。

故事講完了,所以呢?
所以,這幾年我花比較多的時間在團隊開發,不管是Agile/Scrum、VSTS、或是開發框架設計,背後的原因都是為了建構一個可長可久的團隊開發機制,這機制不僅僅是軟體或框架、還包含文化、習慣、規範…,因為我們很清楚的知道,一個人寫程式,和一堆人寫程式,是有很大很大的不同的。當我們一個人學習、一個人開發、大多狀況下都不需要太多的規範和框架,因為所有細節存乎一心,只要人夠強,那『管理』只是時間浪費,但,這是只有一個人的時候。

如果是一群人,那你的世界將會完全不同。因此,這一系列文章我想做的事情是,把這幾年團隊開發與框架設計的經驗,稍微做一些整理,希望幫助大家減少浪費,少走一些冤枉路。我常常說,上課需要喬出學員和講師彼此的時間,能不能和學員有直接的互動,我們得看緣分,緣分多一點,或許我們能夠面對面談這些問題,如果時間不夠,也只能先把這些經驗稍微做一些整理。當然,既然是經驗,就不一定放諸四海皆準,沒有完美的設計,任何的設計都有前提,都是依據當下的需求來評估的,因此,像是Scrum一樣,請別將其視為policy,他只是一個framework,既然是framework,你就可以調整、修改、擷取其中一部分來應用。從來沒有人規定,敏捷開發或是Scrum一定要如何如何,重點從來都不是如何,而是為何…(例如,當你學習Scrum,你該知道的是,為何要進行demo meeting、而非拘泥於如何進行demo meeting)

知道背景之後,從下一篇開始,我們來談談我自己的團隊中,Web專案的開發方式。

前面說道『天下武功、唯快不破』。但在這個變化莫測的時代,要練到很快,又談何容易? 我們也只能在這條路上少走冤枉的路,盡可能減少浪費,僅此而已。

2016年12月11日 星期日

關於LineBot(4) - 透過asp.net輕鬆建立Line機器人WebHook

如果你申請好了新版的Line Messaging API帳號。(申請位置位於https://business.line.me/zh-hant/services/bot),就可以建立一個Line對談機器人了,你要讓你的Line機器人能夠透過程式來回覆用戶的訊息,那關鍵當然是底下這個WebHook:

這個WebHook的設定位置可以從Line Messanging的管理介面進入:

一但你設定好WebHook,所有從用戶傳送給LineBot的訊息,就會傳送到這個WebHook,而你透過程式碼寫的這個WebHook,在接收到這個訊息之後,就可以依照用戶的訊息內容,來回應(回覆)不同的訊息給用戶。這就是一個Line對談機器人最基本的架構:

簡單的說就是,你只要寫一個WebHook(C#開發人員可以用WebAPI寫),放上網站,他就是一個網址,而你的機器人與用戶對談時,Line會把這個訊息封包傳送到你的WebHook這個網址,你可以在接受到訊息封包後進行剖析,取得用戶說的話(或是傳送來的圖片),透過後端的對談邏輯(可以用以前我們介紹過的LUIS來進行文字的斷字和語意分析)判斷,然後把該回覆的訊息組出來回覆給用戶,大致上是這樣。

所以你不難理解,那個WebHook顯然是我們第一個要實做的東西。其實開發起來很簡單。首先,請先用Visual Studio 2015建立一個Web專案:

在下一個畫面,專案範本請選擇Empty,但記得勾選底下的WebAPI:

完成後的專案框架大概是底下這樣,請在Controllers資料夾底下新增一個Controller:

我們先叫他LineChatController好了:

完成後大概是底下這樣:

接著我們就可以開始寫程式了,但別忘了,在寫程式之前,請先引用LineBotSDK這個Nuget Package:
SNAGHTML334df3fe

完成後,請直接將底下這段程式碼填入你的WebAPI Controller,請留意你要自行替換掉Channel Access Token:

所謂的Channel Access Token基本上就代表著你的Line Messanging,你在程式碼中想要透過你的Bot回覆或發送訊息,都需要這個Channel Access Token,他可以在你的Line Messanging WebHook管理站台上找到(第一次進來是空的,請按下Issue鈕就會出現) :

把程式碼調整好(Channel Access Token換好)之後,請將此程式(其實就是一個網站),建置(Build)後發佈(Publish)到某個站台,由於該網站必須支援https,因此用windows azure web app是一個不錯的選擇。

上傳完成之後,可以將你網站的URL,填入Line管理後台的WebHook URL: (這裡請注意,由於我們剛才的Controller取名為LineChat,因此你輸入的位置必須是 https://你的網站domain/api/LineChat ,例如底下這樣):

輸入完畢儲存後,你就可以測試你的LineBot了,還記得嗎? 剛才我們的程式碼當中,在第11行的地方取得用戶訊息的封包,將其剖析後,取得ReceivedMessage物件(第13行),接著找到用戶說的訊息,組出一段Echo的文字(16行),最後透過18行的程式碼回覆,程式碼就只有這樣。

我們試試看,當您加入這個bot做為好友(QR Code在管理站台上),對他說test,結果如下:

其實非常的簡單,對吧。

當然,上面程式碼還有很多不完善的地方,目前也都偵測不到傳送照片、加入或取消好友…等行為,不過沒關係,要達成其實也不難,我們後面再找時間跟大家介紹。

相關課程: http://www.studyhost.tw/NewCourses
--------------------
btw, 有時候blog的留言我會很晚才看到,如果有問題或需要討論,請用底下網址上FB囉…
https://www.facebook.com/DotNetWalker/posts/1308046072568296

2016年11月8日 星期二

關於 LintBot (3) - 使用LineBotSDK發送Line訊息

新版的Line Messaging API,要主動發送訊息給用戶不是很困難,主要是透過Push API,可以參考底下的官方說明 : https://devdocs.line.me/en/#push-message

另外,如果你想申請一個Line Bot,可參考: 關於LineBot (1) - 用c#建立一個LineBot

前陣子說過,我們為了團隊開發方便,做了一個Nuget Package以便於讓撰寫C#的開發人員可以方便的進行Line Messaging API的操作。

所以,如果你想要透過該SDK來發送訊息給用戶,可以透過底下的指令:

isRock.LineBot.Utility.PushMessage(
              UserId, 要傳送的訊息, AccessToken);

別忘了使用前,要先引用linebotSDK這個套件:

當您在.net 4.x的專案中,安裝上述lintbotSDK,即可透過前面提到的指令發送訊息給用戶,但請注意,該用戶必須加你的bot為好友。

你可能在使用時會碰到一個問題,怎麼知道用戶的id呢?
請注意,PushAPI的Line用戶id並非你的line ID,而是一長串項是底下這樣的id:
U6OX388eOX3634X2OX2e341OX06b8OX4123

這個ID會在你的用戶跟你的bot交朋友,或是對談的時候,由系統傳送給你所設定的WebHook網址(其本質上是一個asp.net WebAPI或類似的API服務)。你會在Line傳遞給該WebHook URL的封包中的JSON形式資料內獲得。

也就是說,當你的用戶傳訊息給你的Bot,或是與你的Bot做朋友(或封鎖),你所在你的LineBot後台,所設定的這個WebHook,都會收到這些相關的訊息。其設定位置如下:

訊息內容是以JSON的格式出現。

但是,如果你還不知道怎麼寫一個WebHook,該怎麼辦呢? 寫個WebHook其實很簡單,以asp.net來說,就是寫一個asp.net WebAPI即可。(有空我再說明,但今天比較沒空)

(2017更新,WebHook開發方式請參考這裡)

如果你沒空寫,可以先用底下這個工具(注意這個工具僅限測試使用,我們不保證其穩定性與可用性、與資料安全性):
http://isbaas.azurewebsites.net/WebForms/LineTokenManage.aspx

上面這個工具可以幫你產生一個臨時的WebHook網址,你只要到上面這個網址,把Line後台取得的Access Token輸入,並且順便輸入一個你想監聽該封包的 email(一般來說就是你自己的email啦),按下產生WebHook URL,系統會幫你產生一組像是底下這樣的URL:
https://isbaas.azurewebsites.net/api/LineWebHook?key=677510db-c204-46a0-9c72-d74ffac7e51c
你只需要把上面這組URL貼到你的Line管理後台WebHook URL的位置:

你就有一個公版的測試用WebHook了,這時你就可以跟你的line bot說話:

這個WebHook會echo用戶輸入的訊息,並且把整包JSON mail到你指定的email位置,同時間,你也可以透過跟bot說 /showmyid 取得你的line id,以便於測試。

取得你的用戶id之後,你就可以透過底下的程式碼來發訊息給特定用戶:
isRock.LineBot.Utility.PushMessage(
              UserId, 要傳送的訊息, ChannelAccessToken);

請注意,這個小工具只是為了我們團隊內部測試方便使用,如果可以,我們還是建議你自行建立WebHook,我們並不負責為您保管Channel Access Token,建議您測試完成後,立刻重新Issue你的bot Access Token,以確保安全。

相關課程: http://www.studyhost.tw/NewCourses