標籤:
Web API即使通過網路進行調用的API介面,與具體的程式設計語言無關。現在常見的是通過標準的HTTP GET/POST請求,從伺服器擷取響應的資源或服務,伺服器返回調用的結果內容,一般為xml格式或者json格式的資料(現在使用json的更多)。
在開發App的時候,一般原型設計好(如使用just in mind之類的工具)之後,我們會設計出與伺服器互動的介面文檔。一般情況下,App的開發進度(尤其原型)要快於伺服器的開發進度。在App靜態原型開發完到伺服器實現所有的互動介面這段期間內,我們當然不能閑著。這時,我們可以本地類比一個HTTP伺服器,從而可以繼續App的”動態化”開發。
由於對javascript比較熟悉,簡單看了一下node js之後,就使用它來開發本地的HTTP伺服器並提供各種互動的介面。這裡記錄一下是怎麼一步一步實現的。
一步一步實現HTTP伺服器
樣本起見,杜撰了3個介面(無論多少個,原理都一樣),如下:
焦點圖/sample_app/focus_pic文章列表/sample_app/article_list文章詳情/sample_app/article_detail
我們可以將伺服器劃分為以下幾個模組:
- 入口 - app.js,總體管理伺服器。一般是啟動伺服器
- server模組 - server.js,負責伺服器的配置與請求的轉寄。如伺服器監聽的連接埠,請求日誌的記錄,請求轉寄至具體的處理函數等
- router模組 - router.js,顧名思義,負責請求的路由功能。例如,我們在這裡可以將不同的請求地址轉寄至不同的函數處理。
- request handlers模組 - request_handler.js,針對每個請求進行處理的函數都定義在這個模組裡邊。
- response template模組,由於我們只是快速類比提供Web API服務的HTTP伺服器,所以真正返回的內容寫在模板裡邊即可。
其實3個介面的路由(將不同的請求轉到對應的處理函數)按照道理應該在router模組中進行處理,但是因為針對每個介面的處理邏輯都是相同的,只是返回的內容不同,我就把路由邏輯轉到request handlers模組中去了。具體如何寫,可以根據實際的情況進行調整,這裡邊只是提供一個思路而已。
單看文字還是比較晦澀的,我們來看一下具體的代碼:
server.js
/** * Created by FIMH on 2016/05/05. */var http = require(‘http‘);var url = require(‘url‘);function start(route, handle) { function onRequest(request, response) { // 擷取請求路徑 var parsedUrl = url.parse(request.url); var pathname = parsedUrl.pathname; console.log(‘Request for ‘ + pathname + ‘ received.‘); route(handle, parsedUrl, request, response); } http.createServer(onRequest).listen(9999); console.log(‘Server has started.‘);}exports.start = start;
可以看到,主要配置了HTTP伺服器監聽的連接埠 - 9999,以及列印了一條log資訊,然後將請求轉至router模組進行處理
router.js
/** * Created by FIMH on 2016/05/05. */// 針對不同的請求,做出不同的相應function route(handler, parsedUrl, request, response) { var pathname = parsedUrl.pathname; console.log(‘About to route a request for ‘ + pathname); // 禁止訪問favicon.ico if (!pathname.indexOf(‘/favicon.ico‘)) { return; } // 這裡不用檢查請求路徑是否正確,將路由放到handle對應的函數中去 handler(parsedUrl, request, response);}exports.route = route;
這裡我們主要攔截了對favicon.ico檔案的訪問,關於這個檔案是什麼,大家可以自行搜尋。
前面也提到了,由於這個sample裡邊,每個介面的處理邏輯都是相同的,只是返回的內容不同,我就把路由邏輯轉到request handlers模組中去了。
真正的處理邏輯都在下面這個模組中
requests_handlers.js
/** * 請求處理入口。 */function handleRequests(parsedUrl, request, response) { // 解碼並解析querystring //var queryStringUtil = require(‘querystring‘); //var queryString = parsedUrl.query; //var queryStringResultObject = queryStringUtil.parse(queryString); var pathname = parsedUrl.pathname; // 在這裡進行處理}exports.handleRequests = handleRequests;
這裡,我只貼了一個請求處理的入口函數。
在這個地方我重構了一次,最初的處理邏輯大致如下:
var templateName; var innerHtml; if (pathname == ‘/sample_app/focus_pic‘) { templateName = ‘focus_pic‘; } else if (pathname == ‘/sample_app/article_list‘) { templateName = ‘article_list‘; } else if (pathname == ‘/sample_app/article_detail‘) { templateName = ‘article_detail‘; innerHtml = ‘article‘; } if (templateName) { handleValidRequest(request, response, templateName, innerHtml); } else { handleErrorOutput(request, response, 400, ‘Invalid request url!‘); }
因為這裡只是3個請求,看著還不明顯,如果比較多了,那麼if…else寫起來就太煩了,這時候我想起來好多js語言的項目(如cocos 2d-js,egret)都會使用json檔案作為項目的設定檔,依次來簡化代碼並提高靈活性。
這時,我們可以定義一個項目的設定檔,我這裡取名叫appProperties.json
,內容如下
{ "route": { "/sample_app/focus_pic": { "template": "focus_pic" }, "/sample_app/article_list": { "template": "article_list" }, "/sample_app/article_detail": { "template": "article_detail", "inner_html": "article" } }}
然後我們修改前面提到的模組 - requests_handlers.js
先定義一個全域變數var routeObj;
然後在函數handleRequests
裡這樣處理:
// 解析route配置資訊 if (!routeObj) { var fs = require(‘fs‘); var propertiesPath = ‘./appProperties.json‘; var propertiesData = fs.readFileSync(propertiesPath, ‘utf-8‘); routeObj = JSON.parse(propertiesData); } var templateObj = routeObj[‘route‘][pathname]; if (templateObj) { handleValidRequest(request, response, templateObj[‘template‘], templateObj[‘inner_html‘]); } else { handleErrorOutput(request, response, 400, ‘Invalid request url!‘); }
重構之後,無論介面有多少個,處理的代碼依然是這幾行;而重構前的方法,每添加一個介面,都需要增加一個 else if
。
拿演算法複雜度的概念來比對,重構前就是O(n),而重構後則為O(1)。
關於response template模組的處理,這裡就不貼代碼了。主要是使用node js同步或者非同步讀模數板檔案,還有對json對象的序列化,編輯與還原序列化。大家有興趣的話可以看整個sample的原始碼,見文章底部。
總結與原始碼
如果使用傳統的方式,你需要安裝一個http伺服器 - 如apache,還有一個語言處理模組 - 如php。
而使用了node js之後,你只需要安裝一個node運行時,剩下的http伺服器,請求解析,處理,返回等全部使用js來進行編寫即可,而且書寫的代碼量也很小。
整個項目的代碼我放到github上了,詳見nodejs_sample_app
利用node js快速類比Web API