2013年7月30日 星期二

Ejs page scope直接使用session物件的方式

在ExpressJS中,使用Ejs view engine時,發現session不能再ejs page中直接取用,所有參數都必須靠render時候傳遞過去...跟asp, jsp等scriptlet language操作上有明顯的不同...@@

為了達到可以在ejs中直接取用,這邊可以使用ejs page的locals變數,locals變數在route設定時候可以使用res.locals來取出,這時候只要將它與req.session做串連(res.locals.session = req.session)即可讓前端的ejs page直接透過<%=locals.session%>或是<%=session%>的方式來操作session...

而如果要在每一個route中設定res.locals.session,這樣也太麻煩... 我們可以透過app.use的方式來設定:

app.use(function(req, res, next){
  res.locals.session = req.session;
  next();
});

這樣所有的route在執行的時候都會先跑過一次這個動作,所以在使用時候可以這樣:

[app.js]
app.get('/', function(req, res) {
  req.session.user = {name: 'simon'}
  ...(skip)
});

[view/index.ejs]
...(skip)
<%=session.user? 'got user: '+session.user.name : 'no user...'%>
...(skip)

這時候從ejs取用session的物件就單純多了 :D

在ExpressJS中進階使用Ejs view engine

在操作ExpressJS的Ejs View Engine時候
發現針對頁面上的操作並不是那麼的順心
舉個例子:
app.js中做一個route希望能夠將值往前端(.ejs)帶,但是前端implement了express-partials的模組,希望把頁面用template的方式組織起來,並且在template page中會用到一部分的參數... 假設有時個route用到這個template,則每個route都必須把參數設定進去,否則後端會接收到"not defined"的錯誤訊息...

上面例子的片段程式碼如下:

[app.js]
app.get('/', function(req, res){
  res.render('index', { title: 'my express page' });
});

[index.ejs]
<% if(user) { %>
<%= user %>
<% } %>

執行時候會有Exception:
...(skip)
user is not defined
...(skip)

這部份的錯誤應該是Ejs在render page時候造成的,它直接throw Exception而會造成page終止render,在遍尋不著比較好的方法時,從某篇文章找到一些蛛絲馬跡...

原來Ejs使用"locals"這個物件來包裝頁面上會用到的所有參數,而經過ejs的scriptlet tag包裝起來的部分,可以直接使用locals裡面的參數,也就是說上面的app.js做page render時候:

res.render('index', { title: 'my express page' })

相當於把title這個參數與ejs頁面的locals變數做整合
亦即ejs頁面操作:

<%=title%>

與

<%=locals.title%>

是相等的,但是直接操作title屬性時候,會被ejs compile成runtime exception,這導致如果在ejs中執行下面判斷會出錯;

<% if(user) { .... %>

因為實際上locals.user不存在,且ejs compiler會將不存在的狀況throw exception...

解決方式,直接使用locals來判斷裡面是否有user這個變數...,因此改寫上面的判斷後:

<% if(locales['user']) { .... %>

應該就可以正常運作...

2013年6月22日 星期六

About HTTP delete method error response

記錄一個比較不常用到的操作方式...
以client使用request module為例...
http delete method中,某些client遇到error時,解讀回覆部分不是直接取用body
而是去解析error物件來做回覆
此時如何從server端製作一個錯誤來讓client操作呢...

下面是client端的模擬code,主要client端想要取出callback function中的error物件...

Client side:
var opts = {url:'http://localhost:1337/test/123', method:'DELETE'};
request(
  opts, 
  function(error,res,body) {
    console.log(e);
  }
);

此時server side的實作,以express為例,只需要在res.send()中依序帶入:
  • 第一個欄位設定錯誤代碼
  • 第二個欄位為錯誤物件描述,建議以JSON為格式(預設格式為{code: xxx, message:xxx},若符合此格式,回覆時候匯自動取message內文做為error的message欄位,並會在error物件中多一個code的欄位}
下面模擬del協定,於呼叫時候丟錯:

Server side:
app.del('/test/:id', function(req, res) {
  res.send(400, {code:"test error", message:"the tested error"});
});

此時client的console pring匯出現下面訊息:

Client result: 
{ message: 'the tested error',
  body: { code: 'test error', message: 'the tested error' },
  httpCode: 400,
  statusCode: 400,
  restCode: 'test error',
  code: 'test error' }

如上描述,如果server改寫response回傳物件(不使用預設格式)...

Server side:
app.del('/test/:id', function(req, res) {
  res.send(400, {coder:"test error", msg:"the tested error"});
});

則,error物件會長這樣子:

{ message: '{\n  "coder": "test error",\n  "msg": "the tested error"\n}',
  body: { coder: 'test error', msg: 'the tested error' },
  httpCode: 400,
  statusCode: 400 }

Log4js support print object inline

log4js套件是我常使用的log管理工具
最近看別人的程式才發現,原來它有支援物件的列印
透過 log.info('......%o....', jsonObject) 的方式
可以直接把物件解析成String...
真是方便的功能∼

範例:

執行結果:


更正,不是%o造成的
應該是log4js將最後一個參數使用像console.log(object)的方式
解些json物件成為string...

2013年6月11日 星期二

Access-Control-Allow-Origin範例

在JS的世界裡,Ajax的頭號天敵就是為了避免不安全,Browser所設限的coss site not allow的限制,為了避免cross site javascript的問題,傳統作法是透過script tag來嵌入非同個網站的資源,或是使用jsonp來做site to site的呼叫,但是...

  • script tag內文必須要是script,否則無法順利讀取,甚至會弄壞網頁中的js...
  • jsonp已經被chrome排擠... firefox可以正常使用
有了Node.js的request套件後,我們大可以一行解決... 讓遠方資源變成我發資源:
request.get('http://mysite.com/doodle.png').pipe(resp)
上面可以讓原本需要透過http://mysite.com/doodle.png 來呼叫的資源,透過你制定的鏈結呼叫該資源...
而,根本的解決方式可以使用Header中加入Access-Control-Allow-Origin參數來設定你的js,允許讓其他網站來呼叫...
Server端實作:

See:/examples/basic/http/cross-site-server.js

var http = require('http');
http.createServer(function (req, res) {
  res.writeHead(200,{"Access-Control-Allow-Origin": "http://10-20-0-31.my.micloud.tw"});
  res.end(JSON.stringify({result:'yes'}));
}).listen(8080);
console.log('Server running at port 8080');
上面設定資源可以讓site:http://10-20-0-31.my.micloud.tw 下面的網頁來呼叫,如果想要讓所有網站都可以呼叫,可以使用"*"來讓所有網站可以呼叫...
Client端實作:

See:/examples/basic/http/cross-site.html

<script src="http://code.jquery.com/jquery-1.10.1.min.js"></script>
<script>
$(document).ready(function(){
  $.getJSON('http://211.78.254.38:8080/', function(data){
    $('#r').html(data.result);
  });
});
</script>
<h1>Server response: <span id="r"></span></h1>

2013年5月27日 星期一

在你的主機中使用tty.js

tty.js是Node.js實作的一套將主機的Console直接用Web方式顯示的工具
背景透過SocketIO持續的跟前端主機資料同步
讓使用者感覺真的跟開啟終端機一樣!

使用方法:

    npm install tty.js -g
tty.js提供簡單的指令來開起服務,下面展示如何透過指定port的方式來做登入的動作:
    # tty.js --port 8080
鍵入上面指令之後,就可以使用http://localhost:8080連線
會看到下面的畫面:



如果想要客製一些登入參數與SSL等,可以自己寫幾行程式來做到...
(關於SSL Key部分,可以參考:http://peihsinsu.blogspot.tw/2012/12/smartosself-gen-ssl.html,而下面是採用MiCloud SmartOS,已經內建有一組SSL Key: "/opt/local/etc/openssl/private/selfsigned.pem")
關於更多的參數設定,可以參考:https://github.com/chjj/tty.js.git

    var tty = require('tty.js');    var app = tty.createServer({    shell: 'bash',    users: {      foo: 'bar'    },    "https": {      "key": "/opt/local/etc/openssl/private/selfsigned.pem",      "cert": "/opt/local/etc/openssl/private/selfsigned.pem"    },    port: 10000  });    app.listen();