2013年1月20日 星期日

ExpressJS三兩事

ExpressJS應該是學Node.js的開發者必碰的框架,一些基本的說明收錄如下:

專案之初

專案採用ExpressJS之框架建置,建置過程減列如下:

    # mkdir ~/project  # express ~/project/ProjectName  # cd ~/project/ProjectName  # npm install  

目錄結構

  • app.js : 程式進入點
  • views/ : 放置*.ejs檔案,為view module的程式碼位置,亦即與java中的jsp相同地位的程式
  • routes/ : 放置route相關處理的程式碼,預設ExpressJS將app.js中的routing處理都拉到routes/下面
  • public/ : 放置靜態資源的位置
  • node_modules : 透過npm install後,npm會將定義域package.json中的所需函式庫都下載安裝至此

Configure

Igongsha將view engine由預設jade修改為ejs,設定部分主要如下:

    var express = require('express')    , partials = require('express-partials')    ....;    var app = express();    // Initial the layout system in expressjs 3.x  app.use(partials());    app.configure(function(){    //Set listen port     app.set('port', process.env.PORT || 7800);    //Set views' folder    app.set('views', __dirname + '/views');    //Set view engine, we choide ejs here    app.set('view engine', 'ejs');    ...    //Set public folder    app.use(express.static(path.join(__dirname, 'public')));  });    //Set running mode  app.configure('development', function(){    app.use(express.errorHandler());  });    //Set routing (this use the routes middleware, that default use the index.js under $project/routes)  app.get('/', routes.index);  

Routing

Routing簡單的說就是URL存取位置,你可以直接在app.js中寫處理,也可以拉出到middleware中

Under app.js:

    //That can access using http://host:port/test  app.get('/test', function(req, res){    res.end('...');  })  

Or move to middleware:

    //app.js  app.get('/test', routes.test);    //routes/index.js  exports.test = function(req, res){    res.end('...');  }  

Routing Types

ExpressJS依據HTTP method wrapper routing成get, post, del, put, all,說明如下:

  • app.get: HTTP GET傳遞方式,此方式傳遞時,無法使用req.body取值,通常可以作為讀取資料之用
  • app.post: HTTP POST傳遞方式,可以使用req.body取出Form中參數的傳遞,通常可以作為新增資料之用
  • app.del: HTTP DELETE傳遞方式,通常可以作為刪除資料之用
  • app.put: HTTP PUT傳遞方式,通常可以作為更新資料之域
  • app.all: 接受所有HTTP協定,內部可用req.method判斷傳入的Method為何

Middleware

舉凡非view的,通常nodejs稱作middleware,而我習慣說是lib...,一個存放商業邏輯的地方...

  • lib/*.js : 存放moddleware或函式庫或工具的地方
  • routes/*.js : 存放app.js中抽出的routing處理的地方,也可以說是route的middleware
  • node_modules/* : 定義於package.json中的dependency,在npm install後都會被安裝於此

View

頁面程式的產生工廠,類似java中的jsp,可以透過scriptlet的語法'<%- ... %>', '<%= ... %>', '<% ... %>'來嵌入後台程式碼,其中差別說明如下: '<%- ... %>', 用來嵌入不被跳脫(tag的<或>會被轉譯)的程式碼 '<%= ... %>', 用來嵌入需要被跳脫的程式碼 * '<% ... %>', 用來放置運算 views/layout.ejs

    <h1>This is from view/layout.ejs</h1>    <%-body%>    <footer>    <p align="center" style="width:100%">&copy; MiCloud 2013</p>  </footer>  

sample.ejs

    <title><%=title%></title>  Hello <%=user%>  <ul>  <%  for(var i=0; i<10; i++) {  %>  <li>This is ... <%=i%></li>  <%  }  %>  </ul>  

URL Access

已上面app.get('/test', routes.test)為例,伺服器啟動後,就可以使用http://server_ip:port/test來存取routes.test裡面的運算,而參數的存取,可以透過下面幾種方式:

  • req.params.xxx或req.params[xxx]: 如果routing的定義中有使用到:xxx的路徑,例如app.get('/test/:xxx', routes.test)的話,那就可以透過req.params來取xxx的值
  • req.body.xxx: 如果有使用到form的post時候,可以透過req.body.xxx來存取form中id/name=xxx的欄位傳送過來的值
  • query parameters: 如果參數的傳遞是使用url query string來傳遞時候,則可以使用req.url取出url,並透過url.parse()解析url之後,再取出該參數,範例如下:
    var url_parts = url.parse(req.url, true);  var query = url_parts.query;  console.log(query.xx);  

Static Pages

Express中的靜態資源位置,是在一開始的app.configure()中設定的,通常是寫這樣:

      app.use(express.static(path.join(__dirname, 'public')));  

因此,專案Code Gen後的預設目錄一般為public目錄,這些目錄下的檔案會忠實的呈現在routing上,例如$project/public/test.html檔案,會呈現在url:http://server_ip:port/test.html上,而其他靜態資源(image, video, music...)均可以透過這樣的規則放置與存取。

2013年1月19日 星期六

String as a Function

寫程式的時候,常會有需要從原始碼直接執行的需要
例如,需要把程式碼片段藏在DB中,依照不同的需求取出不同的程式碼來執行
這時候就非常需要能夠從文字重組回物件的方式
在Java裡面,是透過Class.forName(className)的方式,從原始碼中創建一個類別來執行
在Node.js中,有著JavaScript的許多特性
其中一個好用的funciton:eval(),可幫助執行一個純文字輸入的javascript片段
範例:

/tmp/fn.txt
{
  "name":"test",
  "fn":"fn = function(v){ console.log('Hello...' + v); }"
}


/tmp/test.js
var fs = require('fs')
fs.readFile('/tmp/fn.txt', 'UTF-8', function(e, d){ 
  var obj = JSON.parse(d);
  var fnc = eval(obj.fn)
  fn("test");
});

# node /tmp/test.js
Hello...test

上面範例,其中fn.txt中的fn=,測試下是必須要給定
似乎是因為eval會實際執行輸入字串中的語句
而function的語法中,如果是匿名function的話
則必須給定給一個變數
這樣也方便讓eval後的程式片段可以用一個名稱直接呼叫
如此,不論是來自file或是db的程式片段,都可以用類似方式動態載入來使用喔∼


2013年1月2日 星期三

Json to HTML Table

Json to HTML Table是擷取自afshinm的專案,目的是讓所有的Json物件可以輕易的在javascript中轉換成為Html Table的字串
當中也支援http(s)://, ftp://, file://, javascript:()等輸出的轉換,可以方便顯示json物件中的不同屬性文字...
原先開發nodeutil的目的在蒐集各種常用的套件,當中引用該模組作為Node.js中處理Json物件轉換成Html Table的功能
在撰寫Mail或是Render Web時候,可以派上用場...

用法:
var j2t = require('nodeutil').json2table;
app.get('/test', function(req, res){
  var json = [
    {Desc:'Sample of using javascript', Code:'javascript:(alert(\'TEST\'));'},
    {Desc:'Sample of using http', Code:'http://www.google.tw'},
    {Desc:'Sample of using https', Code:'https://www.google.tw'},
    {Desc:'Sample of using ftp', Code:'ftp://www.ntu.edu.tw'}
  ];
  res.end(
    j2t.ConvertJsonToTable(
      json, 
      'jsonTable', 
      'tbclass', 
      '<img src="http://cdn1.iconfinder.com/data/icons/Map-Markers-Icons-Demo-PNG/48/Map-Marker-Push-Pin--Right-Azure.png"/>'
    )
  );
}); 

其中json物件的key部分會變成table title,並且以第一列的為主要顯示,其下有未填寫的部份會補上{0}
value的部份如有http(s)://, ftp://, file://, javascript:()等開頭的文字,將會自動轉變成link輸出
而主要轉換的function是ConvertJsonToTable((objectArray, tableId, tableClass, linkReplace)),帶入四個參數:
  • objectArray: json array物件,主要輸出內容部分
  • tableId: 將帶入到輸出的table id屬性
  • tableClass: 將帶入到輸出的table class屬性
  • linkReplace: 將帶入到所有被轉換成link的顯示文字(或物件)

輸出畫面: 



輸出的HTML原始碼:
<table border="1" cellpadding="1" cellspacing="1" id="jsonTable" class="tbclass"><thead><tr><th>Desc</th><th>Code</th></tr></thead><tbody><tr><td>Sample of using javascript</td><td><a href="javascript:(alert('TEST'));"><img src="http://cdn1.iconfinder.com/data/icons/Map-Markers-Icons-Demo-PNG/48/Map-Marker-Push-Pin--Right-Azure.png"/></a></td></tr><tr><td>Sample of using http</td><td><a href="http://www.google.tw"><img src="http://cdn1.iconfinder.com/data/icons/Map-Markers-Icons-Demo-PNG/48/Map-Marker-Push-Pin--Right-Azure.png"/></a></td></tr><tr><td>Sample of using https</td><td><a href="https://www.google.tw"><img src="http://cdn1.iconfinder.com/data/icons/Map-Markers-Icons-Demo-PNG/48/Map-Marker-Push-Pin--Right-Azure.png"/></a></td></tr><tr><td>Sample of using ftp</td><td><a href="ftp://www.ntu.edu.tw"><img src="http://cdn1.iconfinder.com/data/icons/Map-Markers-Icons-Demo-PNG/48/Map-Marker-Push-Pin--Right-Azure.png"/></a></td></tr></tbody></table>