如何用Node去寫一個Web應用框架
第一步,用node輸出一個hello world
- var http=require('http');
 - http.createServer(function(req,res){
 - var urlPares=url.parse(req.url);
 - var query=querystring.parse(urlPares.query);
 - res.end('hello world');
 - }).listen(80);
 
大部分的node教程在這里會告訴你,我們很容易的建立的一個服務器。但是在實際使我們通常使用的是express.(f**k,難道Node必須要用express嗎?自己實現一個Web應用框架真的很難嗎?)其實并不是。
那么既然打算自己寫我們首先要知道我們要做哪些事情。 1.路由或者智能路由 2.靜態(tài)文件輸出 3.session/cookie 4.模版渲染 5.數據庫處理 6.文件上傳
第二步,路由
路由好高大上的名字,它是干啥的?url對應具體方法就是它該做的事情。 那么我們?yōu)槭裁床蛔寀rl對應xxx文件的xx方法。 例如:/user/login能不能自動對應到user.js的login方法上。實現起來很難么?其實只需要幾句代碼
- var fs = require("fs");
 - module.exports=function(req,res){
 - var query=req.query;
 - var urlPares=req.urlPares;
 - var pathname=urlPares.pathname;
 - var arr=pathname.split("/");
 - req.arr=arr;
 - //start 這段代碼處理默認行為??梢韵群雎?/span>
 - if(arr.length==0||arr.length==1){
 - arr=["","index","index"];
 - }else if(arr.length==2){
 - arr.push("index");
 - }
 - if(arr[1]==""){
 - arr[1]="index";
 - }
 - if(arr[2]==""){
 - arr[2]="index";
 - }
 - //end 這段代碼處理默認行為??梢韵群雎?/span>
 - if (fs.existsSync(APP_PATH+'/controller/'+arr[1]+'.js')){
 - var controller=require('./controller/'+arr[1]);
 - if(controller[arr[2]]){
 - controller[arr[2]](req,res);
 - }else{
 - res.writeHead(404,{'Content-Type': 'text/plain' });
 - res.end("你訪問的控制器不存在指定方法");
 - }
 - }else{
 - res.writeHead(404,{'Content-Type': 'text/plain' });
 - res.end("你訪問的路徑不存在");
 - }
 - }
 
通過fs判斷文件是否存在。然后去require它就行了。APP_PATH是個全局變量表示程序入口的路徑。
第三步,靜態(tài)文件輸出
靜態(tài)文件輸出我們需要一個庫MIME
- var url = require("url");
 - var fs = require("fs");
 - var mime = require('mime');
 - /**
 - * [[檢測是否為靜態(tài)資源]]
 - * @param {Object} req [[Description]]
 - * @param {[[Type]]} res [[Description]]
 - * @returns {bool} [[Description]]
 - */
 - module.exports = function (req, res) {
 - //正則表達式檢測文件后綴
 - var url_resource_reg = /.*\.(html|htm|gif|jpg|jpeg|bmp|webp|htc|swf|png|ico|txt|js|css)/;
 - if (!url_resource_reg.test(req.url)) {
 - return false;
 - }
 - var urlPares = url.parse(req.url);
 - var pathname = urlPares.pathname;
 - var fileUrl = APP_PATH + "/static" + pathname;
 - if (fs.existsSync(fileUrl)) {
 - var contentType = mime.lookup(fileUrl);
 - res.setHeader('Content-Type', contentType || "text/plain");
 - var fileStream = fs.createReadStream(fileUrl);
 - fileStream.pipe(res);
 - fileStream.on('end', function () {
 - res.end();
 - });
 - return true;
 - } else {
 - return false;
 - }
 - }
 
第四步,session/cookie
這里稍微有點。但是代碼量也不多
- var sessions = {};
 - var sessionKey = 'session_key';
 - var EXPIRES = 30 * 60 * 1000;
 - function randString(size) {
 - var result = '';
 - var allChar = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
 - size = size || 1;
 - while (size--) {
 - result += allChar.charAt(rand(0, allChar.length - 1));
 - }
 - return result;
 - }
 - var generate = function () {
 - var session = {};
 - session.id = Date.now() + randString(12);
 - session.cookies = {
 - expire: Date.now() + EXPIRES
 - }
 - sessions[session.id] = session;
 - return session;
 - }
 - var parseCookie= function (cookie) {
 - var cookies = {};
 - if (!cookie) {
 - return cookies;
 - }
 - var list = cookie.split(";");
 - for (var i = 0; i < list.length; i++) {
 - var pair = list[i].split("=");
 - cookies[pair[0].trim()] = pair[1];
 - }
 - return cookies;
 - }
 - var serializeCookies = function (cookies) {
 - var arr = [];
 - for (var key in cookies) {
 - arr.push(serialize(key, cookies[key]));
 - }
 - return arr;
 - }
 - var serialize = function (name, value, option) {
 - var pairs = [name + '=' + encodeURI(value)];
 - //設置cookie默認共用"/"路徑
 - option = option || {
 - path: "/"
 - };
 - if (option.maxAge) pairs.push('Max-Age=' + option.maxAge);
 - if (option.domain) pairs.push('Domain=' + option.domain);
 - if (option.path) pairs.push('Path=' + option.path);
 - if (option.expires) pairs.push('Expires=' + option.expires);
 - if (option.httpOnly) pairs.push('HttpOnly');
 - if (option.secure) pairs.push('Secure');
 - return pairs.join('; ');
 - }
 - module.exports = function (req, res) {
 - req.cookies = parseCookie(req.headers.cookie);
 - var id = req.cookies[sessionKey];
 - if (!id) {
 - req.session = generate();
 - } else {
 - var session = sessions[id];
 - if (session) {
 - if (session.cookies.expire > Date.now()) {
 - session.cookies.expire = Date.now() + EXPIRES;
 - req.session = session;
 - } else {
 - delete sessions[id];
 - req.session = generate();
 - }
 - } else {
 - req.session = generate();
 - }
 - }
 - for (var key in sessions) {
 - if (sessions[key].cookies.expire < Date.now()) {
 - delete sessions[key];
 - }
 - }
 - var writeHead = res.writeHead;
 - res.writeHead = function () {
 - delete req.cookies[ham_sessionKey];
 - var sessionStr = serialize(ham_sessionKey, req.session.id);
 - res.setHeader('Set-Cookie', serializeCookies(req.cookies).concat(sessionStr));
 - return writeHead.apply(res, arguments);
 - }
 - }
 
第五步,模版渲染
這是最簡單的。因為我用https://github.com/aui/artTemplate ,自己用自己喜歡的模塊組件就行了
第六步,數據庫處理
這里可以是用一些ORM框架。例如https://github.com/dresende/node-sql-query
第七步,文件上傳,post
這里只需要一個組件https://github.com/felixge/node-formidable
第八步,就是你把上面的代碼組織起來。
可以參考我的實現 https://coding.net/u/as3long/p/today/git/tree/master/node_modules/ham 代碼比較亂,見諒。















 
 
 









 
 
 
 