|
在上一篇文章中,我們得出了一個(gè)重要結(jié)論,TinyWebDB本質(zhì)上就是一個(gè)典型的Web服務(wù)系統(tǒng)。那么在這篇文章中,我們就來著手實(shí)現(xiàn)這樣一個(gè)系統(tǒng)。 市面上可用于開發(fā)Web服務(wù)系統(tǒng)的語言和工具有很多,比如Java、Python、Node.js等,但大多都面向?qū)I(yè)開發(fā)領(lǐng)域,對(duì)于非專業(yè)開發(fā)人員而言,需要跨越一定的學(xué)習(xí)門檻。為此,我們特意選擇由IBM Emerging Technology團(tuán)隊(duì)開發(fā)的Node-RED,作為我們構(gòu)建TinyWebDB原型的主要工具,實(shí)際這是一個(gè)面向物聯(lián)網(wǎng)應(yīng)用的開源可視化編程和運(yùn)行環(huán)境。 在App Inventor里,這是一段非常典型的事件處理邏輯,其重點(diǎn)是與功能相關(guān)的詳細(xì)指令序列及具體執(zhí)行步驟。其中,(按鈕點(diǎn)擊事件處理)過程是管控指令執(zhí)行的主體,(按鈕、標(biāo)簽和對(duì)話框等)組件對(duì)象雖然是指令的具體執(zhí)行者,但對(duì)其功能和特性的使用都要由過程來統(tǒng)一指揮和調(diào)度。換句話說,組件只是過程用來實(shí)現(xiàn)功能的道具,它的所有行為都受到過程的直接控制。反過來,過程對(duì)組件功能也具有相當(dāng)程度的依賴性和綁定性,個(gè)別組件的微小變化和改動(dòng)都有可能引起整個(gè)過程的調(diào)整。 實(shí)際這就是Node-RED構(gòu)建應(yīng)用和實(shí)現(xiàn)功能的基本方式,但其中重要的區(qū)別在于,節(jié)點(diǎn)之間的協(xié)作并不是指令式的,而是聲明式的。也就是說,各個(gè)節(jié)點(diǎn)并不關(guān)心所連接節(jié)點(diǎn)的具體類型、功能和特性,它的任務(wù)僅僅是接收消息、處理消息和發(fā)送消息,不負(fù)責(zé)對(duì)所發(fā)消息的用法做具體要求和詳細(xì)規(guī)定。因此,Node-RED節(jié)點(diǎn)不必對(duì)外暴露其具體的功能和特性。節(jié)點(diǎn)之間發(fā)送的消息只是純粹的數(shù)據(jù),不必承載明確的指令和意圖。節(jié)點(diǎn)之間只是依從數(shù)據(jù)交換和處理的需要相互連接,不必因某種邏輯關(guān)系而強(qiáng)制綁定。通常我們把這種編程方式叫做基于流程或數(shù)據(jù)流的編程模式。 安裝完成后,只要在命令行窗口中輸入執(zhí)行node-red,就可以啟動(dòng)整個(gè)Node-RED系統(tǒng)。這時(shí),如果將瀏覽器指向運(yùn)行Node-RED的主機(jī)地址和端口(默認(rèn)為1880),即可進(jìn)入其開發(fā)環(huán)境界面。 打開后的開發(fā)環(huán)境一般包括六個(gè)主體功能單元,分別為:1) 節(jié)點(diǎn)面板,用于顯示所有開發(fā)可用的節(jié)點(diǎn)類型;2) 流程面板,用于為開發(fā)者提供連接節(jié)點(diǎn)、創(chuàng)建和編輯流程等功能;3) 信息面板,用于顯示節(jié)點(diǎn)的詳細(xì)規(guī)格信息;4) 調(diào)試面板,用于顯示流程運(yùn)行過程中產(chǎn)生的調(diào)試信息;5) 編輯面板,用于設(shè)置節(jié)點(diǎn)的屬性行為,主要包括消息內(nèi)容的修改、轉(zhuǎn)換和處理等;6) 部署按鈕和系統(tǒng)菜單,用于流程的部署運(yùn)行以及整個(gè)開發(fā)環(huán)境的配置管理。 Node-RED應(yīng)用開發(fā)的一般過程是,首先將所需的節(jié)點(diǎn)從節(jié)點(diǎn)面板中拖入流程面板,然后根據(jù)需要分別雙擊各個(gè)節(jié)點(diǎn),并在彈出的編輯面板中設(shè)置其屬性和行為。設(shè)置完成后,按數(shù)據(jù)流向和業(yè)務(wù)邏輯要求,以拖拽方式將流程面板中的相關(guān)節(jié)點(diǎn)組合連接起來,形成業(yè)務(wù)和數(shù)據(jù)流程。最后點(diǎn)擊部署按鈕,將設(shè)計(jì)好的流程部署到Node-RED服務(wù)器上運(yùn)行。簡(jiǎn)單說就是,添加節(jié)點(diǎn)、設(shè)置節(jié)點(diǎn)、連接節(jié)點(diǎn)和部署運(yùn)行四步(具體順序可根據(jù)情況進(jìn)行適當(dāng)調(diào)整),更詳細(xì)的步驟和說明請(qǐng)大家自行參考Node-RED官網(wǎng)(https:// )或17coding的Node-RED子站(https://nodered. )。 值得注意的是,請(qǐng)求和應(yīng)答之間必須具有一定的綁定性和關(guān)聯(lián)性,即我們常說的所問即所答,如此才能保證通信的成功。因此,我們要先將同組的輸入、輸出節(jié)點(diǎn)用消息線連接起來,這樣就可以確保二者所針對(duì)的通信對(duì)象具有一致性,避免通信過程中出現(xiàn)偏差。 另外,為了在開發(fā)過程中測(cè)試和調(diào)試的方便,我們還要再加入兩個(gè)debug(調(diào)試)節(jié)點(diǎn),分別連接到兩個(gè)功能組的http in節(jié)點(diǎn)上,這樣就能使接收到的外部消息被同時(shí)傳送并顯示到調(diào)試面板上,以驗(yàn)證接收功能的正確性。 在進(jìn)行正式的開發(fā)、設(shè)置之前,有必要先對(duì)流程中各節(jié)點(diǎn)進(jìn)行統(tǒng)一命名,突出其作用和功能,以方便開發(fā)過程中的管理、跟蹤和調(diào)試。 之后,我們才按照TinyWebDB通信協(xié)議的約定,對(duì)http in節(jié)點(diǎn)中與通信相關(guān)的屬性進(jìn)行設(shè)置,主要包括通信方法和偵聽地址這兩項(xiàng)。對(duì)照上一篇文章中的相關(guān)說明,應(yīng)分別進(jìn)行如下設(shè)置。 設(shè)置完成后,點(diǎn)擊右上角的部署按鈕,將設(shè)置好的節(jié)點(diǎn)和流程部署到Node-RED系統(tǒng)中運(yùn)行。這樣,我們所設(shè)計(jì)的流程就初步具備接收前端數(shù)據(jù)的能力了。 為驗(yàn)證設(shè)計(jì)成果,我們還是先利用App Inventor編寫一段簡(jiǎn)單的測(cè)試代碼。其中,出于簡(jiǎn)化功能的需要,我們?cè)跍y(cè)試數(shù)據(jù)部分,特意采用了硬編碼的方式。 執(zhí)行這段代碼后,我們就能在Node-RED調(diào)試面板中看到以下數(shù)據(jù)內(nèi)容。
恰好與前端應(yīng)用所提交的數(shù)據(jù)能夠?qū)?yīng)起來,這說明我們所設(shè)計(jì)的服務(wù)已經(jīng)能成功接收前端所發(fā)送的數(shù)據(jù)了。只不過在數(shù)據(jù)形式上,它使用了JavaScript對(duì)象類型的表示形式,除了所使用的符號(hào)有些差異外,實(shí)際它基本可以等同于App Inventor中的鍵值對(duì)列表。 我們?cè)賮砜匆幌虑岸薃pp Inventor一側(cè)的測(cè)試結(jié)果。
通過對(duì)結(jié)果做簡(jiǎn)單分析,不難看出,TinyWebDB組件對(duì)于保存數(shù)據(jù)功能的返回結(jié)果是不進(jìn)行內(nèi)容驗(yàn)證的,只要有結(jié)果就算調(diào)用成功,所以彈出了正確提示。而查詢數(shù)據(jù)功能則相反,由于我們沒有按約定返回列表字串形式的結(jié)果(即JavaScript形式的[VALUE, tag值, value值]),所以出現(xiàn)了解析錯(cuò)誤。 另外,如果與調(diào)試面板中出現(xiàn)的信息進(jìn)行對(duì)比,還可以發(fā)現(xiàn),輸入、輸出數(shù)據(jù)的有效成分是被封裝在消息對(duì)象的payload(載荷)屬性之中,其作用有點(diǎn)類似于電子郵件中的正文。而像請(qǐng)求對(duì)象、應(yīng)答對(duì)象等與網(wǎng)絡(luò)通信相關(guān)的其他內(nèi)容,則放置在payload之外其他屬性中,就像電子郵件中的發(fā)送者和接收者等。為了證明這一點(diǎn),我們可以將debug節(jié)點(diǎn)的輸出屬性修改為“完整消息對(duì)象”,然后再來看一下調(diào)試面板中出現(xiàn)的結(jié)果。
實(shí)際在完整的Node-RED消息對(duì)象結(jié)構(gòu)中,_msgid和payload是兩個(gè)必備的主要成分,前者像身份證編號(hào)一樣,是消息的唯一性標(biāo)識(shí),而后者則承載了消息的主體內(nèi)容,其他則是在此基礎(chǔ)上根據(jù)功能需要進(jìn)行的擴(kuò)展,比如上圖中的req和res屬性。流程中的各個(gè)節(jié)點(diǎn)之所以能夠相互連接成為一個(gè)整體,其重要的原因在于它們之間所傳送的消息具有一致性,即_msgid必須保持相同,而我們通常所說的消息處理和轉(zhuǎn)換其實(shí)是指對(duì)同一消息payload部分的處理和轉(zhuǎn)換。 如果要解決前端出現(xiàn)的錯(cuò)誤提示問題,我們必須在現(xiàn)有的流程中增加適當(dāng)?shù)奶幚砉?jié)點(diǎn),將發(fā)往http response節(jié)點(diǎn)消息的payload內(nèi)容修改為符合TinyWebDB通信協(xié)議要求的形式。這里,我們選擇節(jié)點(diǎn)面板中的template(模板)節(jié)點(diǎn)來完成這一使命。
各節(jié)點(diǎn)屬性的具體設(shè)置如下圖所示。暫時(shí)我們將返回結(jié)果也設(shè)為硬編碼形式,以方便驗(yàn)證解決問題的效果。
重新測(cè)試后的結(jié)果如下圖所示。
應(yīng)該說,從輸出消息的角度看,基本解決了上面的問題。但是如果看一下調(diào)試面板中的信息,不難發(fā)現(xiàn),由于采用了硬編碼的方式,所以這樣的測(cè)試結(jié)果,實(shí)際和前端的輸入沒有任何關(guān)系,我們需要進(jìn)一步改進(jìn)我們的設(shè)計(jì),增加相應(yīng)的處理節(jié)點(diǎn)。
首先,我們需要將前端發(fā)送的請(qǐng)求數(shù)據(jù)信息以某種方式保存起來,使兩個(gè)功能流程及其中相關(guān)處理節(jié)點(diǎn)之間能夠?qū)崿F(xiàn)數(shù)據(jù)分享。我們知道常規(guī)的數(shù)據(jù)存儲(chǔ)方式包括變量存儲(chǔ)、文件存儲(chǔ)和數(shù)據(jù)庫存儲(chǔ)等,由于我們的設(shè)計(jì)目標(biāo)是最簡(jiǎn)系統(tǒng),所以暫時(shí)選擇最簡(jiǎn)單的變量存儲(chǔ)方式,以后需要時(shí)再根據(jù)情況逐漸擴(kuò)展到其他存儲(chǔ)方式。 在Node-Red中,系統(tǒng)性的環(huán)境變量包括節(jié)點(diǎn)(context)、流程(flow)和全局(global)等三種類型,分別對(duì)應(yīng)于同一節(jié)點(diǎn)內(nèi)、同一流程標(biāo)簽內(nèi)以及整個(gè)開發(fā)環(huán)境等三類不同范圍的作用域。由于我們目前的設(shè)計(jì)對(duì)象是基于同一系統(tǒng)的不同功能,所以采用流程變量會(huì)更適合一些。 系統(tǒng)預(yù)設(shè)的流程變量名為flow,因此,我們可以將所需的存儲(chǔ)對(duì)象設(shè)為flow.tinywebdb,如果有些讀者對(duì)JavaScript對(duì)象類型不太熟悉的話,不妨把它想像成我們?cè)贏pp Inventor中所創(chuàng)建的鍵值對(duì)類型變量。
具體做法是,在節(jié)點(diǎn)面板中選取change(修改)節(jié)點(diǎn),將其加入storeavalue流程,并通過屬性設(shè)置功能,對(duì)該存儲(chǔ)對(duì)象進(jìn)行命名和賦值,也就是將http in節(jié)點(diǎn)接收到的前端數(shù)據(jù)保存到該對(duì)象中。
同時(shí),在getvalue流程中增加switch(切換)節(jié)點(diǎn),將http in節(jié)點(diǎn)接收消息中的payload屬性值與存儲(chǔ)對(duì)象的對(duì)應(yīng)值進(jìn)行比較,根據(jù)比較結(jié)果將流程切換到不同的返回結(jié)果處理分支上。
最后,再統(tǒng)一修改兩個(gè)流程中所有生成返回結(jié)果的template節(jié)點(diǎn),將相關(guān)變量以模板方式嵌入到其輸出消息的payload屬性值中。其中的{{{...}}}符號(hào)表示,要在輸出時(shí)用其中所包圍變量的當(dāng)前值替換這部分占位內(nèi)容,比如如果a的值為1,那么{{{a}}}的輸出結(jié)果就為1。
至此,我們就完成了最簡(jiǎn)TinyWebDB系統(tǒng)的全部設(shè)計(jì)工作,最后形成的完整數(shù)據(jù)和業(yè)務(wù)流程如下圖所示。
通過執(zhí)行測(cè)試代碼,將其保存到TinyWenDB中。
然后,設(shè)置查詢條件。
再次執(zhí)行測(cè)試代碼,向TinyWebDB提交查詢請(qǐng)求,并獲取相關(guān)數(shù)據(jù),以驗(yàn)證保存效果。
接著,我們?cè)俑淖円幌虏樵儣l件。
看看TinyWebDB是否能正常返回空記錄。
經(jīng)簡(jiǎn)單測(cè)試驗(yàn)證,所設(shè)計(jì)的系統(tǒng)基本實(shí)現(xiàn)了預(yù)期的目標(biāo)。盡管從系統(tǒng)功能上看還十分簡(jiǎn)陋,技術(shù)細(xì)節(jié)上考慮得也不是很周全,而且所能保存的數(shù)據(jù)也非常有限,且無法實(shí)現(xiàn)持久化存儲(chǔ),但俗話說的好,萬丈高樓平地起,一切復(fù)雜的應(yīng)用和擴(kuò)展其實(shí)都是在這類看似簡(jiǎn)單的原型基礎(chǔ)之上逐步搭建起來的。我們將在后續(xù)的文章中,以此為基礎(chǔ),陸續(xù)向大家介紹一些與應(yīng)用和功能擴(kuò)展方面有關(guān)的內(nèi)容和實(shí)例。 |
|
|