Node.js小示例

1.http get和post

httpserverrequestget.js

复制代码
/*
获取GET请求内容
由于GET请求直接被嵌入在路径中,URL是完整的请求路径,包括了?后面的部分,因此你可以手动解析后面的内容作为GET请求的参数。
node.js中url模块中的parse函数提供了这个功能。
 */
var http = require('http');
var url = require('url');
var util = require('util');

http.createServer(function(req, res){
    res.writeHead(200, {'Content-Type': 'text/plain'});
    res.end(util.inspect(url.parse(req.url, true)));
}).listen(3000);

//在浏览器中访问http://localhost:3000/user?name=joey&email=joey@joey.com 然后查看返回结果
复制代码

 

httpserverrequestpost.js

复制代码
/*
POST请求的内容全部的都在请求体中,http.ServerRequest并没有一个属性内容为请求体,原因是等待请求体传输可能是一件耗时的工作,
比如上传文件,而很多时候我们可能并不需要理会请求体的内容,恶意的POST请求会大大消耗服务器的资源,所有node.js默认是不会解析请求体的,
当你需要的时候,需要手动来做。
 */
var http = require('http');
var querystring = require('querystring');
var util = require('util');

http.createServer(function(req, res){
    var post = '';     //定义了一个post变量,用于暂存请求体的信息

    req.on('data', function(chunk){    //通过req的data事件监听函数,每当接受到请求体的数据,就累加到post变量中
        post += chunk;
    });

    req.on('end', function(){    //在end事件触发后,通过querystring.parse将post解析为真正的POST请求格式,然后向客户端返回。
        post = querystring.parse(post);
        res.end(util.inspect(post));
    });
}).listen(3000);
复制代码

注意:不要在真正的生产应用中使用上面这种简单的方法来获取POST请求,因为它有严重的效率问题和安全问题,这只是一个帮你理解的示例。

 

知识扩展:util.inherits继承

复制代码
/*
 util.inherits
 定义了一个基础对象Base和一个继承自Base的Sub,Base有三个在构造函数内定义的属性和一个原型中定义的函数,通过util.inherits实现继承
 注意,Sub仅仅继承了Base在原型中定义的函数,而构造函数内部创造的base属性和sayHello函数都没有被Sub继承。
 */
var util = require('util');

function Base(){
    this.name = 'base';
    this.base = 1991;

    this.sayHello = function(){
        console.log('Hello ' + this.name);
    };
}

Base.prototype.showName = function(){
    console.log(this.name);
};

function Sub(){
    this.name = 'sub';
}

util.inherits(Sub, Base);

var objBase = new Base();
objBase.showName();
objBase.sayHello();
console.log(objBase);

var objSub = new Sub();
objSub.showName();
//objSub.sayHello();
console.log(objSub);
复制代码

2.文本提交与显式



index.js

复制代码
var server = require("./server");
var router = require("./router");
var requestHandlers = require("./requestHandlers");

var handle = {}
handle["/"] = requestHandlers.start;
handle["/start"] = requestHandlers.start;
handle["/upload"] = requestHandlers.upload;

server.start(router.route, handle);
复制代码

server.js

复制代码
var http = require("http");
var url = require("url");

function start(route, handle) {
  function onRequest(request, response) {
    var postData = "";
    var pathname = url.parse(request.url).pathname;
    console.log("Request for " + pathname + " received.");

    request.setEncoding("utf8");

    request.addListener("data", function(postDataChunk) {
      postData += postDataChunk;
      console.log("Received POST data chunk '"+
      postDataChunk + "'.");
    });

    request.addListener("end", function() {
      console.log("data received ending" + pathname);
      route(handle, pathname, response, postData);
    });
  }

  http.createServer(onRequest).listen(8888);
  console.log("Server has started.");
}

exports.start = start;
复制代码

requestHandlers.js

复制代码
var querystring = require("querystring");

function start(response, postData) {
  console.log("Request handler 'start' was called.");

  var body = '<html>'+
    '<head>'+
    '<meta http-equiv="Content-Type" content="text/html; '+
    'charset=UTF-8" />'+
    '</head>'+
    '<body>'+
    '<form action="/upload" method="post">'+
    '<textarea name="text" rows="20" cols="60"></textarea>'+
    '<input type="submit" value="Submit text" />'+
    '</form>'+
    '</body>'+
    '</html>';

    response.writeHead(200, {"Content-Type": "text/html"});
    response.write(body);
    response.end();
}

function upload(response, postData) {
  console.log("Request handler 'upload' was called.");
  response.writeHead(200, {"Content-Type": "text/plain"});
  response.write("You've sent the text: "+
  querystring.parse(postData).text);
  response.end();
}

exports.start = start;
exports.upload = upload;
复制代码

router.js

复制代码
function route(handle, pathname, response, postData) {
  console.log("About to route a request for " + pathname);
  if (typeof handle[pathname] === 'function') {
    handle[pathname](response, postData);
  } else {
    console.log("No request handler found for " + pathname);
    response.writeHead(404, {"Content-Type": "text/plain"});
    response.write("404 Not found");
    response.end();
  }
}

exports.route = route;
复制代码

 

result:

  

 

 

知识点:

require和exports的用法:

index.js中代码

var Hello = require('.hello');

hello = new Hello();
hello.setName('Joey');
hello.sayHello();

hello.js中代码

复制代码
function Hello(){
    var name;

    this.setName = function(thyName){
        name = thyName;
    }

    this.sayHello = function(){
        console.log('Hello ' + name);
    }
}

//exports.Hello = Hello;  //此时我们在其他文件中需要通过 require('./hello').Hello来获取Hello对象,这种写法有点冗余
module.exports = Hello; //输出的就是Hello对象本身,不是上面的exports,上面的是暴露.Hello,.Hello赋予了Hello对象
复制代码

3.图片上传与显式

index.js

复制代码
var server = require("./server");
var router = require("./router");
var requestHandlers = require("./requestHandlers");

var handle = {}
handle["/"] = requestHandlers.start;
handle["/start"] = requestHandlers.start;
handle["/upload"] = requestHandlers.upload;

server.start(router.route, handle);
复制代码

server.js

复制代码
var http = require("http");
var url = require("url");

function start(route, handle) {
  function onRequest(request, response) {
    var postData = "";
    var pathname = url.parse(request.url).pathname;
    console.log("Request for " + pathname + " received.");

    request.setEncoding("utf8");

    request.addListener("data", function(postDataChunk) {
      postData += postDataChunk;
      console.log("Received POST data chunk '"+
      postDataChunk + "'.");
    });

    request.addListener("end", function() {
      console.log("data received ending" + pathname);
      route(handle, pathname, response, postData);
    });
  }

  http.createServer(onRequest).listen(8888);
  console.log("Server has started.");
}

exports.start = start;
复制代码

requestHandlers.js

复制代码
var querystring = require("querystring");

function start(response, postData) {
  console.log("Request handler 'start' was called.");

  var body = '<html>'+
    '<head>'+
    '<meta http-equiv="Content-Type" content="text/html; '+
    'charset=UTF-8" />'+
    '</head>'+
    '<body>'+
    '<form action="/upload" method="post">'+
    '<textarea name="text" rows="20" cols="60"></textarea>'+
    '<input type="submit" value="Submit text" />'+
    '</form>'+
    '</body>'+
    '</html>';

    response.writeHead(200, {"Content-Type": "text/html"});
    response.write(body);
    response.end();
}

function upload(response, postData) {
  console.log("Request handler 'upload' was called.");
  response.writeHead(200, {"Content-Type": "text/plain"});
  response.write("You've sent the text: "+
  querystring.parse(postData).text);
  response.end();
}

exports.start = start;
exports.upload = upload;
复制代码

router.js

复制代码
function route(handle, pathname, response, postData) {
  console.log("About to route a request for " + pathname);
  if (typeof handle[pathname] === 'function') {
    handle[pathname](response, postData);
  } else {
    console.log("No request handler found for " + pathname);
    response.writeHead(404, {"Content-Type": "text/plain"});
    response.write("404 Not found");
    response.end();
  }
}

exports.route = route;
复制代码

 

result:

  

 

 

知识点:

require和exports的用法:

index.js中代码

var Hello = require('.hello');

hello = new Hello();
hello.setName('Joey');
hello.sayHello();

hello.js中代码

复制代码
function Hello(){
    var name;

    this.setName = function(thyName){
        name = thyName;
    }

    this.sayHello = function(){
        console.log('Hello ' + name);
    }
}

//exports.Hello = Hello;  //此时我们在其他文件中需要通过 require('./hello').Hello来获取Hello对象,这种写法有点冗余
module.exports = Hello; //输出的就是Hello对象本身,不是上面的exports,上面的是暴露.Hello,.Hello赋予了Hello对象
复制代码

index.js

复制代码
var server = require("./server");
var router = require("./router");
var requestHandlers = require("./requestHandlers");

var handle = {}
handle["/"] = requestHandlers.start;
handle["/start"] = requestHandlers.start;
handle["/upload"] = requestHandlers.upload;
handle["/show"] = requestHandlers.show;

server.start(router.route, handle);
复制代码

server.js

复制代码
var http = require("http");
var url = require("url");

function start(route, handle) {
  function onRequest(request, response) {
    var pathname = url.parse(request.url).pathname;
    console.log("Request for " + pathname + " received.");
    route(handle, pathname, response, request);
  }

  http.createServer(onRequest).listen(3000);
  console.log("Server has started.");
}

exports.start = start;
复制代码

requestHandlers.js

复制代码
var querystring = require("querystring"),
    fs = require("fs"),
    formidable = require("formidable");

function start(response) {
    console.log("Request handler 'start' was called.");

    var body = '<html>'+
        '<head>'+
        '<meta http-equiv="Content-Type" content="text/html; '+
        'charset=UTF-8" />'+
        '</head>'+
        '<body>'+
        '<form action="/upload" enctype="multipart/form-data" '+
        'method="post">'+
        '<input type="file" name="upload" multiple="multiple">'+
        '<input type="submit" value="Upload file" />'+
        '</form>'+
        '</body>'+
        '</html>';

    response.writeHead(200, {"Content-Type": "text/html"});
    response.write(body);
    response.end();
}

function upload(response, request) {
    console.log("Request handler 'upload' was called.");

    var form = new formidable.IncomingForm();
    form.uploadDir = "D:\\min\\nodejsExample2\\tmp";
    console.log("about to parse1");
    form.parse(request, function(error, fields, files) {
        console.log("parsing done");
        console.log(files.upload.path);
        fs.renameSync(files.upload.path, "D:\\min\\nodejsExample2\\tmp\\test.png");
        response.writeHead(200, {"Content-Type": "text/html"});
        response.write("received image:<br/>");
        response.write("<img src='/show' />");
        response.end();
  });
}

function show(response) {
    console.log("Request handler 'show' was called.");
    fs.readFile("D:\\min\\nodejsExample2\\tmp\\test.png", "binary", function(error, file) {
        if(error) {
            response.writeHead(500, {"Content-Type": "text/plain"});
            response.write(error + "\n");
            response.end();
        } else {
            response.writeHead(200, {"Content-Type": "image/png"});
            response.write(file, "binary");
            response.end();
        }
    });
}

exports.start = start;
exports.upload = upload;
exports.show = show;
复制代码

router.js

复制代码
function route(handle, pathname, response, request) {
    console.log("About to route a request for " + pathname);
    if (typeof handle[pathname] === 'function') {
        handle[pathname](response, request);
    } else {
        console.log("No request handler found for " + pathname);
        response.writeHead(404, {"Content-Type": "text/html"});
        response.write("404 Not found");
        response.end();
    }
}

exports.route = route;
复制代码

 

result:

 

 

知识点:

其中用到了fs模块的readFile读取文件,它有同步和异步两个版本。node.js中,并不是所有的API都提供了异步和同步版本,node.js不鼓励使用同步I/O。

复制代码
//this is async 异步
/*
 fs.readFile调用时所做的工作只是将异步式I/O请求发送给了操作系统,然后立即返回并执行后面的语句,执行完以后进入事件循环监听事件。
 当fs接收到I/O请求完成的事件时,事件循环会主动调用回调函数以完成后续工作。
 */
var fs = require('fs');
fs.readFile('file.txt', 'utf-8', function(err, data){
    if (err){
        console.error(err);
    } else {
        console.log(data);
    }
});
复制代码

 

//this is sync 同步
var fs = require('fs');
var data = fs.readFileSync('file.txt', 'utf-8');
console.log(data);
console.log('end.');

 

4.服务器监听

index.js

复制代码
var server = require("./server");
var router = require("./router");
var requestHandlers = require("./requestHandlers");

var handle = {}
handle["/"] = requestHandlers.start;
handle["/start"] = requestHandlers.start;
handle["/upload"] = requestHandlers.upload;

server.start(router.route, handle);
复制代码

server.js

复制代码
var http = require("http");
var url = require("url");

function start(route, handle) {
  function onRequest(request, response) {
    var postData = "";
    var pathname = url.parse(request.url).pathname;
    console.log("Request for " + pathname + " received.");

    request.setEncoding("utf8");

    request.addListener("data", function(postDataChunk) {
      postData += postDataChunk;
      console.log("Received POST data chunk '"+
      postDataChunk + "'.");
    });

    request.addListener("end", function() {
      console.log("data received ending" + pathname);
      route(handle, pathname, response, postData);
    });
  }

  http.createServer(onRequest).listen(8888);
  console.log("Server has started.");
}

exports.start = start;
复制代码

requestHandlers.js

复制代码
var querystring = require("querystring");

function start(response, postData) {
  console.log("Request handler 'start' was called.");

  var body = '<html>'+
    '<head>'+
    '<meta http-equiv="Content-Type" content="text/html; '+
    'charset=UTF-8" />'+
    '</head>'+
    '<body>'+
    '<form action="/upload" method="post">'+
    '<textarea name="text" rows="20" cols="60"></textarea>'+
    '<input type="submit" value="Submit text" />'+
    '</form>'+
    '</body>'+
    '</html>';

    response.writeHead(200, {"Content-Type": "text/html"});
    response.write(body);
    response.end();
}

function upload(response, postData) {
  console.log("Request handler 'upload' was called.");
  response.writeHead(200, {"Content-Type": "text/plain"});
  response.write("You've sent the text: "+
  querystring.parse(postData).text);
  response.end();
}

exports.start = start;
exports.upload = upload;
复制代码

router.js

复制代码
function route(handle, pathname, response, postData) {
  console.log("About to route a request for " + pathname);
  if (typeof handle[pathname] === 'function') {
    handle[pathname](response, postData);
  } else {
    console.log("No request handler found for " + pathname);
    response.writeHead(404, {"Content-Type": "text/plain"});
    response.write("404 Not found");
    response.end();
  }
}

exports.route = route;
复制代码

 

result:

  

 

 

知识点:

require和exports的用法:

index.js中代码

var Hello = require('.hello');

hello = new Hello();
hello.setName('Joey');
hello.sayHello();

hello.js中代码

复制代码
function Hello(){
    var name;

    this.setName = function(thyName){
        name = thyName;
    }

    this.sayHello = function(){
        console.log('Hello ' + name);
    }
}

//exports.Hello = Hello;  //此时我们在其他文件中需要通过 require('./hello').Hello来获取Hello对象,这种写法有点冗余
module.exports = Hello; //输出的就是Hello对象本身,不是上面的exports,上面的是暴露.Hello,.Hello赋予了Hello对象
复制代码

httpsnifferInvoke.js

复制代码
var http = require('http');
var sniffer = require('./httpsniffer');

var server = http.createServer(function(req, res){
    res.writeHead(200, {'Content-Type': 'text/plain'});
    res.end('Hello, World!\n');
});

sniffer.sniffOn(server);
server.listen(3000);
复制代码

httpsniffer.js

复制代码
//http sniffer 监听每个服务器事件,然后输出每个事件的相关信息
var url = require('url');
var util = require('util');

exports.sniffOn = function(server){
    server.on('request', function(req, res){
        util.log('e_request');
        util.log(reqToString(req));
    });

    server.on('close', function(error){
        util.log('e_close error=' + error);
    });

    server.on('checkContinue', function(req, res){
        util.log('e_checkContinue');
        util.log(reqToString(req));
        res.writeContinue();
    });

    server.on('upgrade', function(req, socket, head){
        util.log('e_upgrade');
        util.log(reqToString(req));
    });

    server.on('clientError', function(){
        util.log('e_clientError');
    });
}

var reqToString = function(req){
    var ret = 'request' + req.method + ' ' + req.httpVersion + ' ' + req.url + '\n';
    ret += JSON.stringify(url.parse(req.url, true)) + '\n';
    var keys = Object.keys(req.headers);
    for (var i = 0; i < keys.length; i++){
        var key = keys[i];
        ret += i + ' ' + key + ': ' + req.headers[key] + '\n';
    }
    if (req.trailers) ret += req.trailers + '\n';
    return ret;
}

exports.reqToString = reqToString;
复制代码

 


5.EventEmitter发送和接收事件

pulser.js

复制代码
/*
 EventEmitter发送和接收事件
 HTTPServer和HTTPClient类,它们都继承自EventEmitter

 EventEmitter被定义在Node的事件(events)模块中,直接使用EventEmitter类需要先声明require('events'),
 否则不必显式声明require('events'),因为Node中很多对象都无需你调用require('events')就会使用EventEmitter
 */
var events = require('events');
var util = require('util');

function Pulser(){
    events.EventEmitter.call(this);
}
util.inherits(Pulser, events.EventEmitter);

Pulser.prototype.start = function(){
    var self = this;
    this.id = setInterval(function(){
        util.log('>>>>pulse');
        self.emit('pulse');
        util.log('<<<<pulse');
    }, 1000);
}
//定义了一个类Pulser,该类(通过util.inherits)继承自EventEmitter,它的作用是每隔一秒钟向所有监听器发送一个定时事件。
//start方法使用了setInterval这个函数来定期重复执行回调函数,并调用emit方法将pulse事件发送给每一个监听器


//使用Pulser对象
/*
创建了一个Pulser对象并处理其pulse事件,执行pulser.on('pulse'..)为pulse事件和回调函数建立联系
 */
var pulser = new Pulser();
pulser.on('pulse', function(){
    util.log('pulse received');
});
pulser.start();


//对象使用emit函数发送事件,所有注册到对应事件的监听器都可以收到事件;
//通过调用.on方法注册监听器,参数是事件名,并用一个回调函数接收事件
//通常来说,有一些数据需要伴随着事件同时发送  self.emit('eventName', data1, data2, ..);
//emitter.on('eventName', function(data1, data2,..){
       //接收到事件后的操作
// });
复制代码

  每秒输出一次



6.发送HTTP客户端请求并显示响应结果

wget.js发送HTTP客户端请求并显示响应的各种结果

options对象描述了将要发出的请求。
data事件在数据到达时被触发,error事件在发生错误时被触发。
HTTP请求中的数据格式通过MIME协议来声明,例如,提交HTML表单时它的Content-Type会被设置成multipart/form-data。
要在HTTP客户端请求中发送数据,只需调用.write方法并写入符合规范的数据(见第二个例子)。

复制代码
var http = require('http');
var url = require('url');
var util = require('util');

var argUrl = process.argv[2];
var parsedUrl = url.parse(argUrl, true);

var options = {host: null, port: -1, path: null, method: 'GET'};
options.host = parsedUrl.hostname;
options.port = parsedUrl.port;
options.path = parsedUrl.pathname;

if (parsedUrl.search) options.path += "?" + parsedUrl.search;

var req = http.request(options, function(res){
    util.log('STATUS: ' + res.statusCode);
    util.log('HEADERS: ' + util.inspect(res.headers));
    res.setEncoding('utf8');
    res.on('data', function(chunk){
        util.log('BODY: ' + chunk);
    });
    res.on('error', function(err){
        util.log('RESPONSE ERROR: ' + err);
    });
});

req.on('error', function(err){
    util.log('REQUEST ERROR: ' + err);
});
req.end();
复制代码

node wget.js http://example.com

 

又一个发送客户端httprequest例子:使用req.write发送数据

复制代码
var http = require('http');
var querystring = require('querystring');

var contents = querystring.stringify({
    name: 'joey',
    email: 'joey@joey.com',
    address: 'joey university'
});

var options = {
    host: 'www.joey.com',
    path: '/application/node/post.php',
    method: 'POST',
    headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
        'Content-Length': contents.length
    }
};

var req = http.request(options, function(res){
    res.setEncoding('uft8');
    res.on('data', function(data){
        console.log(data);
    });
});

req.write(contents);
req.end();  //不能漏掉,结束请求,否则服务器将不会收到信息。
复制代码

7.连接SQLite3和mongodb

setup.js初始化数据库

复制代码
var util = require('util');
var async = require('async');   //npm install async
var notesdb = require('./nodesdb-sqlite3');
// var notesdb = require('./notesdb-mongoose');

notesdb.connect(function(error){
    if (error) throw error;
});
notesdb.setup(function(error){
    if (error){
        util.log('ERROR ' + error);
        throw error;
    }
    async.series([   //async.series函数可以控制函数按顺序执行,从而保证最后的函数在所有其他函数完成之后执行
        function(cb){
            notesdb.add("test", "testtest",
            function(error){
                if (error) util.log('ERROR ' + error);
                cb(error);
            });
        }
    ],
        function(error, results){
            if (error) util.log('ERROR ' + error);
            notesdb.disconnect(function(err){});
        }
    );
});
复制代码

nodesdb-sqlite3.js

SQLite3 是一个轻量级的进程内SQL引擎
它是一个无服务器且无需配置的SQL数据库引擎,仅仅是作为一个独立的库被链接到应用程序上

npm install sqlite3 安装此模块之前先在系统上安装sqlite3库 http://www.sqlite.org/download.html 下载

复制代码
//数据库接口库
var util = require('util');
var sqlite3 = require('sqlite3');

sqlite3.verbose();
var db = undefined;

/*
 数据库名是直接硬编码的,所以当调用connect和setup函数时,当前目录中就会生成chap06.sqlite3文件
 */

exports.connect = function(callback){
    db = new sqlite3.Database("chap06.sqlite3", sqlite3.OPEN_READWRITE | sqlite3.OPEN_CREATE,
        function(err){
            if (err){
                util.log('FAIL on creating database ' + err);
                callback(err);
            } else {
                callback(null);
            }
        });
}

//此处的disconnect函数是空的
exports.disconnect = function(callback){
    callback(null);
}

exports.setup = function(callback){
    db.run("CREATE TABLE IF NOT EXISTS notes " +
        "(ts DATETIME, author VARCHAR(255), note TEXT)",
        function(err){
            if (err){
                util.log('FAIL on creating table ' + err);
                callback(err);
            } else {
                callback(null);
            }
        });
}

exports.emptyNote = {"ts": "", author: "", note: ""};
exports.add = function(author, note, callback){
    db.run("INSERT INTO notes (ts, author, note) " +
        "VALUES (?, ?, ?);",
        [new Date(), author, note],
        function(error){
            if (error){
                util.log('FAIL on add ' + error);
                callback(error);
            } else {
                callback(null);
            }
        });
}
/*
run函数接受一个字符串参数,其中?表示占位符,占位符的值必须通过一个数组传递进来
调用者提供了一个回调函数,然后通过这个回调函数来声明错误
 */

exports.delete = function(ts, callback){
    db.run("DELETE FROM notes WHERE ts = ?;",
        [ts],
        function(err){
            if (err){
                util.log('FAIL to delete ' + err);
                callback(err);
            } else {
                callback(null);
            }
        });
}

exports.edit = function(ts, author, note, callback){
    db.run("UPDATE notes " +
        "SET ts = ?, author = ?, note = ? " +
        "WHERE ts = ?",
        [ts, author, note, ts],
        function(err){
            if (err){
                util.log('FAIL on updating table ' + err);
                callback(err);
            } else {
                callback(null);
            }
        });
}

exports.allNotes = function(callback){
    util.log(' in allnote');
    db.all("SELECT * FROM notes", callback);
}
exports.forAll = function(doEach, done){
    db.each("SELECT * FROM notes", function(err, row){
        if (err){
            util.log('FAIL to retrieve row ' + err);
            done(err, null);
        } else {
            doEach(null, row);
        }
    }, done);
}
/*
allNotes和forAll函数是操作所有数据的两种方法,allNotes把数据库中所有的数据行收集到一个数组里,
而forAll方法可以接受两个回调函数,每当从数据集中拿一行数据,回调函数doEach都会执行一遍,当读完所有数据时,回调函数done就会执行
 */

exports.findNoteById = function(ts, callback){
    var didOne = false;
    db.each("SELECT * FROM notes WHERE ts = ?",
        [ts],
        function(err, row){
            if (err){
                util.log('FAIL to retrieve row ' + err);
                callback(err, null);
            } else {
                if (!didOne){
                    callback(null, row);
                    didOne = true;   //保证回调函数只被执行一次
                }
            }
        });
}
复制代码

notesdb-mongoose.js

MongoDB是nosql数据库的领头羊之一,"可扩展、高性能、开源、面向文档的数据库",它使用JSON风格的文档。
Mongoose是用于访问MongoDB的模块之一,它是一个对象建模工具,意味着你的程序负责定义模式对象来描述数据,
而Mongoose负责数据到MongoDB的存储。
Mongoose对于Node和MongoDB而言是一个非常强大的对象建模工具,使用嵌入式文档,是一个类型灵活的系统,
适用于字段输入、字段验证、虚拟字段等。

MongoDB Windows 下安装部署 http://www.cnblogs.com/EricaMIN1987_IT/p/3571773.html
安装Mongoose模块 npm install mongoose

Mongoose不是唯一一个在node中使用MongoDB的工具。

复制代码
var util = require('util');
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var dburl = 'mongodb://localhost/chap06';  //dburl用于连接已运行的MongoDB

exports.connect = function(callback){
    mongoose.connect(dburl);
}
exports.disconnect = function(callback){
    mongoose.disconnect(callback);
}

exports.setup = function(callback){callback(null);}

//定义模式
var NoteSchema = new Schema({
    ts: {type: Date, default: Date.now},  //默认值
    author: String,
    note: String
});
//将NoteSchema作为Mongoose的模型注册进去
mongoose.model('Note', NoteSchema);
var Note = mongoose.model('Note');

exports.emptyNote = {"_id": "", author: "", note: ""};

exports.add = function(author, note, callback){
    var newNote = new Note();
    newNote.author = author;
    newNote.note = note;
    newNote.save(function(err){
        if (err){
            util.log('FATAL ' + err);
            callback(err);
        } else {
            callback(null);
        }
    });
}

exports.delete = function(id, callback){
    exports.findNoteById(id, function(err, doc){
        if (err){
            callback(err);
        } else {
            util.log(util.inspect(doc));
            doc.remove();
            callback(null);
        }
    });
}

exports.edit = function(id, author, note, callback){
    exports.findNoteById(id, function(err, doc){
        if (err){
            callback(err);
        } else {
            doc.ts = new Date();
            doc.author = author;
            doc.note = note;
            doc.save(function(err){
                if (err){
                    util.log('FATAL ' + err);
                    callback(err);
                } else {
                    callback(null);
                }
            });
        }
    });
}

exports.allNotes = function(callback){
    Note.find({}, callback);
}

exports.forAll = function(doEach, done){
    Note.find({}, function(err, docs){
        if (err){
            util.log('FATAL ' + err);
            done(err, null);
        }
        docs.forEach(function(doc){
            doEach(null, doc);
        });
        done(null);
    });
}

/*
_id字段是MongoDB提供的全局唯一的ID,用于标识存储的文档
 */
var findNoteById = exports.findNoteById = function(id, callback){
    Note.findOne({_id: id}, function(err, doc){
        if (err){
            util.log('FATAL ' + err);
            callback(err, null);
        }
        callback(null, doc);
    });
}
复制代码

 

app.js

复制代码
//在数据库需要放置在一台计算机上时,应该考虑使用SQLite3
//控制器,在nodesdb-sqlite3.js和notesdb-mongoose.js模块之间切换
var util = require('util');
var url = require('url');
var express = require('express');
var nmDbEngine = 'sqlite3';  //用于命名数据库引擎、选择合适的notesdb实现和选择合适的views目录
//var nmDbEngine = 'mongoose';
var notesdb = require('./nodesdb-' + nmDbEngine);
var app = express();
app.use(express.logger());
app.use(express.cookieParser()); //添加cookieParser中间件
app.use(express.bodyParser());
app.engine('.html', require('ejs').__express);   //3.X
//app.register('.html', require('ejs'));    //2.X
app.set('views', __dirname + '/views-' + nmDbEngine);
app.set('view engine', 'ejs');

//是一个路由中间件函数,用于在一些路由器函数中解析URL查询参数
var parseUrlParams = function(req, res, next){
    req.urlP = url.parse(req.url, true);
    next();
}

//检查用户是否被允许访问,这里只检查cookie是否等于AOK,这个单词通常意味着一切都没问题
/*
 很多应用都需要用户登录,然后用户才能进行一些特权操作。由于HTTP是一个无状态的协议,
 验证用户的唯一方式就是发送一个cookie到浏览器上,然后验证标识符。cookie包含了应用中用于验证用户的数据。

 cookieParser中间件在这里做了很多工作,查找cookie,解析cookie,然后将解析出来的值让到req对象中。
 当存在cookie时,它的值会被放入req.cookies中。
 */
var checkAccess = function(req, res, next){
    if (!req.cookies || !req.cookies.notesaccess || req.cookies.notesaccess !== "AOK"){
        res.redirect('/login');
    } else {
        next();
    }
}

notesdb.connect(function(error){
    if (error) throw error;
})

app.on('close', function(error){
    notesdb.disconnect(function(err){});
});

app.get('/', function(req, res) {res.redirect('/view');});
app.get('/view', checkAccess, function(req, res){  //可以在每个路由上加checkAccess检查
    notesdb.allNotes(function(err, notes){
        if (err){
            util.log('ERROR ' + err);
            throw err;
        } else {
            res.render('viewnotes.html', {title: "Notes ("+ nmDbEngine +")", notes: notes});
        }
    });
});

/*
当用户点击ADD按钮时app.get('/add', ...)内的函数就会被调用,浏览器会发出一个发往/add的HTTP GET请求。
这个函数使用addedit.html模板来创建一个表单,让用于通过这个表单输入标签,然后通过单击SUBMIT按钮提交,
当用户提交表单,浏览器就会发出一个HTTP POST请求,app.post('/add', ...)内的函数就会被调用,
用户输入的数据会被存放在请求主体中,而请求主体会被bodyParser(app.use(express.bodyParser()))中间件处理并存放在req.body中
 */
app.get('/add', function(req, res){
    res.render('addedit.html', {title: "Notes ("+ nmDbEngine +")", postpath: '/add', note: notesdb.emptyNote});
});
app.post('/add', function(req, res){
    notesdb.add(req.body.author, req.body.note,
        function(error){
            if (error) throw error;
            res.redirect('/view');
        });
});

app.get('/del', parseUrlParams, function(req, res){
    notesdb.delete(req.urlP.query.id,
        function(error){
            if (error) throw error;
            res.redirect('/view');
        });
});

app.get('/edit', parseUrlParams, function(req, res){
    notesdb.findNoteById(req.urlP.query.id,
        function(error, note){
            if (error) throw error;
            res.render('addedit.html',
                {title: "Notes ("+ nmDbEngine +")", postpath: '/edit', note: note});
        });
});
app.post('/edit', function(req, res){
    notesdb.edit(req.body.id, req.body.author, req.body.note,
        function(error){
            if (error) throw error;
            res.redirect('/view');
        });
});

app.get('/login', function(req, res){
    res.render('login.html', {title: "Notes LOGIN ("+ nmDbEngine +")"});
});
app.post('/login', function(req, res){
    //此处可以添加检查用户信息的逻辑
    //...
    res.cookie('notesaccess', 'AOK');
    res.redirect('/view');
});

app.listen(3000);
复制代码

show.js

复制代码
//控制台显示
var util = require('util');
var notesdb = require('./notesdb-sqlite3');
// var notesdb = require('./notesdb-mongoose');

notesdb.connect(function(error){
    if (error) throw error;
});
notesdb.forAll(function(error, row){
    util.log('ROW: ' + util.inspect(row));
}, function(error){
    if (error) throw error;
    util.log('ALL DONE');
    notesdb.disconnect(function(err){});
});
复制代码

前台页面在views-sqlite3目录下

layout.html

复制代码
<!DOCTYPE html>
<html>
<head>
    <title><%= title%></title>
</head>
<body>
    <h1><%= title%></h1>
    <p><a href='/view'>View</a> | <a href='/add'>Add</a></p>
</body>
</html>
复制代码

viewnotes.html

复制代码
<% include layout.html %>
<table><% notes.forEach(function(note){ %>
    <tr>
        <td>
            <p><%=new Date(note.ts).toString()%>: by <b><%=note.author%></b></p>
            <p><%=note.note%></p>
        </td>
        <td>
            <form method="get" action="/del">
                <input type="submit" value="Delete" />
                <input type="hidden" name="id" value="<%=note.ts%>" />
            </form>
            <br/>
            <form method="get" action="/edit">
                <input type="submit" value="Edit" />
                <input type="hidden" name="id" value="<%=note.ts%>" />
            </form>
        </td>
    </tr>
    <% }); %>
</table>
复制代码

addedit.html

复制代码
<% include layout.html %>
<form method="post" action="<%=postpath%>">
    <% if (note){ %>
    <input type="hidden" name="id" value="<%=note.ts%>" />
    <% } %>
    <input type="text" name="author" value="<%=note.author%>" />
    <br/>
    <textarea rows="5" cols="40" name="note">
        <%=note.note%>
    </textarea>
    <br/>
    <input type="submit" value="Submit" />
</form>
复制代码

login.html

<% include layout.html %>
<form method="POST" action="/login">
    <p>Click the <i>Login</i> to log in.</p>
    <input type="submit" value="Login" />
</form>

node setup.js

node app.js



8.设置HTTP头


server.js

复制代码
//basic server的配置文件
var port = 3000;
var server = require('./basicserver').createServer();
server.useFavIcon("localhost", "./docroot/favicon.png");
server.addContainer(".*", "/l/(.*)$", require('./redirector'), {})
server.docroot("localhost", "/", "./docroot");
//server.useFavIcon("127.0.0.1", "./docroot/favicon.png");
//server.docroot("127.0.0.1", "/", "./docroot");
server.listen(port);
复制代码

basicserver.js

Response Header 服务器发送到客户端
文件扩展名不足以完全恰当的标识文件类型,而且文件扩展名没有标准,于是,人们设计了Content-Type头和整个MIME类型标准来作为数据类型的表示系统。

对于某些应用,特别是一些处理固定数据的小型应用,我们可以精准的知道该使用哪一种Content-Type头,因为应用发送的数据是特定已知的。然而staticHandler能发送任何文件,通常不知道该使用哪种Content-Type。通过匹配文件扩展名列表和Content-Type可以解决这个问题,但是这个方案不完美。最好的实践方案是使用一个外部的配置文件,它通常由操作系统提供。

MIME npm包使用了Apache项目的mime.types文件,该文件包含超过600个Content-Type的有关数据,如果有需要,mime模块也支持添加自定义的MIME类型。

npm install mime

var mime = require('mime');
var mimeType = mime.lookup('image.gif'); //==> image/gif
res.setHeader('Content-Type', mimeType);

一些相关的HTTP头:
Content-Encoding 数据被编码时使用,例如gzip
Content-Language 内容中使用的语言
Content-Length 字节数
Content-Location 能取到数据的一个候补位置
Content-MD5 内容主题的MD5校验和

HTTP协议是无状态的,意味着web服务器不能辨认不同的请求发送端。现在普遍的做法是,服务器发送cookie到客户端浏览器,cookie中定义了登陆用户的身份,对于每一次请求,web浏览器都会发送对应所访问网站的cookie。

发送cookie时,我们应以如下方式为Set-Cookie或Set-Cookie2头设一个值:
res.setHeader('Set-Cookie2', ..cookie value..);

 

 

复制代码
/*
 Basic Server的核心模块会创建一个HTTP服务器对象,附加Basic Server上用于检查请求,然后给予适当响应的功能
 Basic Server可以通过判断Host头部匹配的容器对象响应来自多个域名的请求
 */
var http = require('http');
var url = require('url');

exports.createServer = function(){
    var htserver = http.createServer(function(req, res){
        req.basicServer = {urlparsed: url.parse(req.url, true)};
        processHeaders(req, res);
        dispatchToContainer(htserver, req, res);
    });
    htserver.basicServer = {containers: []};
    htserver.addContainer = function(host, path, module, options){
        if (lookupContainer(htserver, host, path) != undefined){
            throw new Error("Already mapped " + host + "/" + path);
        }
        htserver.basicServer.containers.push({host: host, path: path, module: module, options: options});
        return this;
    }
    htserver.useFavIcon = function(host, path){
        return this.addContainer(host, "/favicon.ico", require('./faviconHandler'), {iconPath: path});
    }
    htserver.docroot = function(host, path, rootPath){
        return this.addContainer(host, path, require('./staticHandler'), {docroot: rootPath});
    }

    return htserver;
}

var lookupContainer = function(htserver, host, path){
    for (var i = 0; i < htserver.basicServer.containers.length; i++){
        var container = htserver.basicServer.containers[i];
        var hostMatches = host.toLowerCase().match(container.host);
        var pathMatches = path.match(container.path);
        if (hostMatches !== null && pathMatches !== null){
            return {container: container, host: hostMatches, path: pathMatches};
        }
    }
    return undefined;
}

//用于搜索req.headers数组以查找cookie和host头部,因为这两个字段对请求的分派都很重要
//这个函数在每一个HTTP请求到达时都会被调用
//还有很多其他的HTTP头部字段(Accept Accept-Encoding Accept-Language User-Agent)
var processHeaders = function(req, res){
    req.basicServer.cookies = [];
    var keys = Object.keys(req.headers);
    for (var i = 0; i < keys.length; i++){
        var hname = keys[i];
        var hval = req.headers[hname];
        if (hname.toLowerCase() === "host"){
            req.basicServer.host = hval;
        }
        //提取浏览器发送的cookie
        if (hname.toLowerCase() === "cookie"){
            req.basicServer.cookies.push(hval);
        }
    }
}

//查找匹配的容器,分派请求到对应的容器中
//这个函数在每一个HTTP请求到达时都会被调用
var dispatchToContainer = function(htserver, req, res){
    var container = lookupContainer(htserver, req.basicServer.host, req.basicServer.urlparsed.pathname);
    if (container !== undefined){
        req.basicServer.hostMatches = container.host;
        req.basicServer.pathMatches = container.path;
        req.basicServer.container = container.container;
        container.container.module.handle(req, res);
    }else {
        res.writeHead(404, {'Content-Type': 'text/plain'});
        res.end("no handler found for " + req.basicServer.host + "/" + req.basicServer.urlparsed);
    }
}
复制代码

staticHandler.js

复制代码
//用于处理文件系统内的文件,docroot选项指被存放文件所在文件夹的路径,读取该目录下的指定文件
var fs = require('fs');
var mime = require('mime');
var sys = require('sys');

exports.handle = function(req, res){
    if (req.method !== "GET"){
        res.writeHead(404, {'Content-Type': 'text/plain'});
        res.end("invalid method " + req.method);
    } else {
        var fname = req.basicServer.container.options.docroot + req.basicServer.urlparsed.pathname;
        if (fname.match(/\/$/)) fname += "index.html";  //如果URL以/结尾
        fs.stat(fname, function(err, stats){
            if (err){
                res.writeHead(500, {'Content-Type': 'text/plain'});
                res.end("file " + fname + " not found " + err);
            } else {
                fs.readFile(fname, function(err, buf){
                    if (err){
                        res.writeHead(500, {'Content-Type': 'text/plain'});
                        res.end("file " + fname + " not readable " + err);
                    } else {
                        res.writeHead(200, {'Content-Type': mime.lookup(fname),
                            'Content-Length': buf.length});
                        res.end(buf);
                    }
                });
            }
        });
    }
}
复制代码

faviconHandler.js

复制代码
//这个处理函数处理对favicon.ico的请求
//MIME模块根据给出的图标文件确定正确的MIME类型,网站图标favicon可以是任何类型的图片,但是我们必须要告诉浏览器是哪个类型
//MIME模块,用于生成正确的Content-Type头
var fs = require('fs');
var mime = require('mime');

exports.handle = function(req, res){
    if (req.method !== "GET"){
        res.writeHead(404, {'Content-Type': 'text/plain'});
        res.end("invalid method " + req.method);
    } else if (req.basicServer.container.options.iconPath !== undefined){
        fs.readFile(req.basicServer.container.options.iconPath, function(err, buf){
            if (err){
                res.writeHead(500, {'Content-Type': 'text/plain'});
                res.end(req.basicServer.container.options.iconPath + "not found");
            } else {
                res.writeHead(200, {'Content-Type': mime.lookup(req.basicServer.container.options.iconPath),
                'Content-Length': buf.length});
                res.end(buf);
            }
        });
    } else {
        res.writeHead(404, {'Content-Type': 'text/plain'});
        res.end("no favicon");
    }
}
复制代码

redirector.js

复制代码
/*
 把一个域的请求重定向到另一个上,例如将www.example.com重定向到example.com上,或者使用简短的URL跳转到较长的URL
 实现这两种情况,我们需要在HTTP响应中发送301(永久移除)或者302(临时移除)状态码,并且指定location头信息。有了这个组合
 信号,web浏览器就知道要跳转到另一个web位置了
 */
//地址http://localhost:3000/l/ex1 会跳转到http://example1.com
var util = require('util');
var code2url = {'ex1': 'http://example1.com', 'ex2': "http://example2.com"};
var notFound = function(req, res){
    res.writeHead(404, {'Content-Type': 'text/plain'});
    res.end("no matching redirect code found for " + req.basicServer.host + "/" + req.basicServer.urlparsed.pathname);
}

exports.handle = function(req, res){
    if (req.basicServer.pathMatches[1]){
        var code = req.basicServer.pathMatches[1];
        if (code2url[code]){
            var url = code2url[code];
            res.writeHead(302, {'Location': url});
            res.end();
        } else {
            notFound(req, res);
        }
    } else {
        notFound(req, res);
    }
}
复制代码

docroot目录下:有favicon.png

index.html

复制代码
<html>
<head>
</head>
<body>
    <h1>Index</h1>
    <p>this is a index html.</p>
</body>
</html>
复制代码



index.js

复制代码
var server = require("./server");
var router = require("./router");
var requestHandlers = require("./requestHandlers");

var handle = {}
handle["/"] = requestHandlers.start;
handle["/start"] = requestHandlers.start;
handle["/upload"] = requestHandlers.upload;

server.start(router.route, handle);
复制代码

server.js

复制代码
var http = require("http");
var url = require("url");

function start(route, handle) {
  function onRequest(request, response) {
    var postData = "";
    var pathname = url.parse(request.url).pathname;
    console.log("Request for " + pathname + " received.");

    request.setEncoding("utf8");

    request.addListener("data", function(postDataChunk) {
      postData += postDataChunk;
      console.log("Received POST data chunk '"+
      postDataChunk + "'.");
    });

    request.addListener("end", function() {
      console.log("data received ending" + pathname);
      route(handle, pathname, response, postData);
    });
  }

  http.createServer(onRequest).listen(8888);
  console.log("Server has started.");
}

exports.start = start;
复制代码

requestHandlers.js

复制代码
var querystring = require("querystring");

function start(response, postData) {
  console.log("Request handler 'start' was called.");

  var body = '<html>'+
    '<head>'+
    '<meta http-equiv="Content-Type" content="text/html; '+
    'charset=UTF-8" />'+
    '</head>'+
    '<body>'+
    '<form action="/upload" method="post">'+
    '<textarea name="text" rows="20" cols="60"></textarea>'+
    '<input type="submit" value="Submit text" />'+
    '</form>'+
    '</body>'+
    '</html>';

    response.writeHead(200, {"Content-Type": "text/html"});
    response.write(body);
    response.end();
}

function upload(response, postData) {
  console.log("Request handler 'upload' was called.");
  response.writeHead(200, {"Content-Type": "text/plain"});
  response.write("You've sent the text: "+
  querystring.parse(postData).text);
  response.end();
}

exports.start = start;
exports.upload = upload;
复制代码

router.js

复制代码
function route(handle, pathname, response, postData) {
  console.log("About to route a request for " + pathname);
  if (typeof handle[pathname] === 'function') {
    handle[pathname](response, postData);
  } else {
    console.log("No request handler found for " + pathname);
    response.writeHead(404, {"Content-Type": "text/plain"});
    response.write("404 Not found");
    response.end();
  }
}

exports.route = route;
复制代码

 

result:

  

 

 

知识点:

require和exports的用法:

index.js中代码

var Hello = require('.hello');

hello = new Hello();
hello.setName('Joey');
hello.sayHello();

hello.js中代码

复制代码
function Hello(){
    var name;

    this.setName = function(thyName){
        name = thyName;
    }

    this.sayHello = function(){
        console.log('Hello ' + name);
    }
}

//exports.Hello = Hello;  //此时我们在其他文件中需要通过 require('./hello').Hello来获取Hello对象,这种写法有点冗余
module.exports = Hello; //输出的就是Hello对象本身,不是上面的exports,上面的是暴露.Hello,.Hello赋予了Hello对象
复制代码
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值