2013年3月28日 星期四

15 Lines NodeJS File Server...

Node.js來初始化HTTP協定是相當簡單的事情
下面展示一個簡單的http server範例
可以把執行路徑下的任何檔案當做http資源來瀏覽

    
var fs = require('fs')
  , http = require('http');
http.createServer(function (req, res) {   var path = req.url;
  try{
    var f = fs.readFileSync('.' + path);
    console.log(f);
    console.log(f);
    res.end(f.toString());   } catch(e) {
    console.log(e);
    res.end('');
  }
  }
}).listen(1337); console.log('Server running at http://127.0.0.1:1337/');
目前作者已經包裝成NPM套件
可以透過下面方式安裝與使用(以下擷取自:https://github.com/peihsinsu/noder)

Noder - Simple HTTP Server 

Install

Install using npm...
    # npm install noder -g  

Usage

    # noder  Server running at http://127.0.0.1:1337/  

View

Browser View http://127.0.0.1:1337/test.html (Assump that the test.html exist in the current folder)
    <Buffer 23 21 2f 62 69 6e 2f 62 61 73 68 0a 63 64 20 2f 6f 70 74 0a 23 77 67 65 74 20 68 74 74 70 3a 2f 2f 6e 6f 64 65 6a 73 2e 6f 72 67 2f 64 69 73 74 2f 76 30 ...>  { [Error: ENOENT, no such file or directory './favicon.ico']    errno: 34,    code: 'ENOENT',    path: './favicon.ico',    syscall: 'open' }  <Buffer 1f 8b 08 00 ed 18 e5 50 00 03 ec 3d 6b 77 db 36 b2 f9 5a fd 0a 44 67 cf 4a 4a 25 ea 2d 77 ed 3a 8d 9b b8 4d 77 f3 ba b1 fb b8 27 37 c7 a2 48 c8 62 4c 91 ...>

2013年3月21日 星期四

透過node.js取得request上有用的資訊

身在網路的世界打滾∼有些事情不得不知道...
在node.js中,他把request做了某個程度的wrapper
我們可以透過下面的方法取得一些常用的連線參數
  • 取得server side full url:
    req.url
  • 取得request client的IP位置:
    req.connection.remoteAddress
  • 取得referer page(從哪個網址連線進入)
    req.headers['referer']
  • 取得user agent information:
    req.headers['user-agent']
實際上程式碼可以向這樣:


http.createServer(function (req, res) {
   console.log(req.connection);
   var result = 'req.url:' + req.url + '\r\n';
   result += 'req.connection.remoteAddres:' + req.connection.remoteAddress + '\r\n';
   if(req.headers) {
       result += 'req.headers[\'referer\']:'+ req.headers['referer'] + '\r\n';
       result += 'req.headers[\'user-agent\']:' + req.headers['user-agent'] + '\r\n';
   }
   res.end(result);
}).listen(port, server);


大家可以參考看看∼

2013年2月6日 星期三

Call by value and call by reference of Node.js

Node.js中的變數,屬於call by value的範圍(我猜是原生型別的關係)
下面是範例:

var a = 1;
var b = a;
console.log(a + '::' + b);
> 1::1
b = b + 1;
console.log(a + '::' + b);
> 1::2

上面的b值改變時候,不影響a值,因此a與b無reference關係...

而物件的話,看起來屬於call by reference...

var a = {aa: 111};
var b = a;
console.log(a);
console.log(b);
console.log('===========');
b.aa = 222;
console.log(a);
console.log(b);
>{ aa: 111 }
>{ aa: 111 }
>============
>{ aa: 222 }  //這是a
>{ aa: 222 }  //這是b

上面的b物件改變aa的值時候,實際上影響到a物件的aa值,屬於call by reference
因此在操作時候需要特別小心...
一個不小心....!@#!$!@#


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>




2012年12月23日 星期日

解決Node.js非同步問題... node-promise模組的使用


Node.js的優點:Event Driven, Non-Block非常強悍,但是同時也造成許多寫作上的不便...
這邊主要想解決在非同步的模組中,想要循序執行的問題... node-promise是不錯的解決方案

安裝:
npm install node-promise

情境:
希望可以在抓取一個網頁完成之後,再執行某個動作...
語意上可以翻譯做:當抓取網頁Event完成後,才去驅動下一個Event

使用:
var p = new require("node-promise").Promise()
  , request = require('request'); 
function step1(fn){ //包裝request的動作
  request({
    url : "http://www.google.com",
    method : "GET"
  },
  function(e,r,d){
    console.log('>>1');
    p.resolve(d);  // 註冊當動作完成時候的Listener
  });
}
step1();  // 執行step1()
p.then( //當動作完成時候,即會執行then所定義的動作
  function(result){ //定義收取結果後的操作
    console.log('>>2');
    console.log(result.substring(0,50));
  },
  function(err){ //定義收到錯誤時候的操作
    console.log(err);
  }
)

執行結果:
# node test-promise
>>1
>>2  // 2 會在 1 之後執行
<!doctype html><html itemscope="itemscope" itemtyp  // 最後列出前request結果的50個字元

除了promise,該套件另外有提供defered與when的操作,可以讓流程更加豐富...
但是,說實在的...整個操作並沒有簡化到哪邊去... 甚至比較麻煩...
不過,透過promise的定義,對event附加Listener的方式倒是可以解決許多callback操作的問題...
還是個值得多多了解的套件!