1.Express介绍 #

Express 是一个小巧且灵活的 Node.js Web应用框架,它有一套健壮的特性,可用于开发单页、多页和混合Web应用。

2.express的应用 #

2.1 安装express #

$ npm install express

2.2 使用express #

//引入express
var express = require('express');
//执行express**函数
var app = express();
//监听端口
app.listen(3000);

2.3 express的get方法 #

根据请求路径来处理客户端发出的GET请求

2.3.1 get方法的使用 #

//引入express
var express = require('./express');
//执行express函数
var app = express();
//监听端口
app.get('/hello', function (req,res) {
   res.end('hello');
});
app.get('/world', function (req,res) {
    res.end('world');
});
app.get('*', function (req,res) {
    res.setHeader('content-type','text/plain;charset=utf8');
    res.end('没有找到匹配的路径');
});
app.listen(3000);

2.3.2 get方法实现 #

//声明express函数
function express(){
    return app;
}
app.router = [];
function app(req,res){
    //获取路径名
    var pathname = require('url').parse(req.url,true).pathname;
    //获取方法名
    var method = req.method;
    //是否匹配,匹配后返回当前项
    var cur = app.router.find((item)=>{
        return item.path==pathname||item.path=='*'&&item.method ==method;
    });
    if(cur){
        cur.fn(req,res);
    }
    res.end(`cannot ${pathname} ${method}`);

}
//注册get方法
app.get =  (path,fn) => {
    app.router.push({method:'GET',path:path,fn:fn});
};
//注册listen方法
app.listen = (port) => {
    require('http').createServer(app).listen(port);
};
module.exports = express;

2.4 express的post方法 #

根据请求路径来处理客户端发出的POST请求

2.4.1 post的方法使用 #

//引入express
var express = require('./express');
//执行express函数
var app = express();
//监听端口
app.post('/hello', function (req,res) {
   res.end('hello');
});
app.post('*', function (req,res) {
    res.end('post没找到');
});
app.listen(3000);

通过linux命令发送post请求

$ curl -X POST http://localhost:3000/hello

2.4.2 post的实现 #

+ app.post = (path,fn) => {
+      app.router.push({method:'POST',path:path,fn:fn});
+  };

2.5 express的all方法 #

监听所有的请求方法,可以匹配所有的HTTP动词 根据请求路径来处理客户端发出的所有请求

2.5.1 all的方法使用 #

var express = require('./express');
var app = express();
app.all('/hello', function (req,res) {
   res.end('hello');
});
app.all('*', function (req,res) {
    res.end('没找到');
});
app.listen(3001);

2.5.2 注册所有方法 #

['get','post','put','delete','options','patch'].forEach(method=>{
    app[method] =  (path,fn) => {
        app.router.push({method:method.toUpperCase(),path:path,fn:fn});
    };
});

2.5.3 all方法的实现 #

app.all = function (path,fn) {
    ['get','post','put','delete','options','patch'].forEach(method=>{
            app.router.push({method:method.toUpperCase(),path:path,fn:fn});
    });
};

2.6 中间件 #

中间件就是处理HTTP请求的函数,用来完成各种特定的任务,比如检查用户是否登录、检测用户是否有权限访问等,它的特点是:

2.6.1 中间件的使用方法 #

var express = require('express');
var app = express();
app.use(function (req,res,next) {
    console.log('过滤石头');
    next();
});
app.use('/water', function (req,res,next) {
    console.log('过滤沙子');
    next();
});
app.get('/water', function (req,res) {
    res.end('water');
});
app.listen(3001);

2.6.2 use方法的实现 #

app.use = function (path,fn) {
    if(typeof fn=='undefined'){
        fn = path;
        path='*'
    }
    app.router.push({method:'middle',path:path,fn:fn});
};

app方法中增加Middleware判断

var index = 0;
function next(){
    //如果等于就是中间件
    var item = app.router[index++];
    if(item.method=='middle'){
        if(item.path==pathname||item.path=='*'){
            item.fn(req,res,next);
        }else{
            next();
        }
    }else{
    //如果不等于就是路由
        if(item.path==pathname||item.path=='*'&&item.method ==method){
            item.fn(req,res);
        }else{
            next();
        }
    }
}
next();

2.6.3 错误中间件 #

next中可以传递错误,默认执行错误中间件

var express = require('express');
var app = express();
app.use(function (req,res,next) {
    console.log('过滤石头');
    next('stone is too big');
});
app.use('/water', function (req,res,next) {
    console.log('过滤沙子');
    next();
});
app.get('/water', function (req,res) {
    res.end('water');
});
app.use(function (err,req,res,next) {
    console.log(err);
    res.end(err);
});
app.listen(3000);

2.6.4 错误中间件的实现 #

function next(err){
    //如果等于就是中间件
    if(index>=app.router.length){
        return res.end('no have')
    }
    var item = app.router[index++];
    if(err){
        if(item.method=='middle' && item.fn.length==4){
            item.fn(err,req,res,next);
        }else{
            next(err);
        }
    }else{
        if(item.method=='middle'){
            if(item.path==pathname||item.path=='*'){
                item.fn(req,res,next);
            }else{
                next();
            }
        }else{
            //如果不等于就是路由
            if(item.path==pathname||item.path=='*'&&item.method ==method){
                item.fn(req,res);
            }else{
                next();
            }
        }
    }
}
next();

2.7 获取参数和查询字符串 #

req.hostname 返回请求头里取的主机名 req.path 返回请求的URL的路径名 req.query 查询字符串

//http://localhost:3000/?a=1
app.get('/',function(req,res){
    res.write(JSON.stringify(req.query))
    res.end(req.hostname+" "+req.path);
});

2.7.1 具体实现 #

+    req.hostname = req.headers['host'].split(':')[0];
+    req.path = pathname;
+    req.query = require('url').parse(req.url,true).query;

2.8 获取params参数 #

req.params 匹配到的所有路径参数组成的对象

app.get('/water/:id/:name/home/:age', function (req,res) {
    console.log(req.params);
    res.end('water');
});

2.8.1 params实现 #

['get','post','put','delete','options','patch'].forEach(method=>{
    app[method] =  (path,fn) => {
        var config = {method:method.toUpperCase(),path:path,fn:fn};
        if(path.includes(':')){
            var params = [];
            path = path.replace(/:(\w+)/g, function (matchers,group) {
                params.push(group);
                return "(\\w+)";
            });
            config.path = '^'+path+'$';
            config.params = params;
        }
        app.router.push(config);
    };
});
 //如果不等于就是路由
if(item.params){
    //带params
    var arr = pathname.match(new RegExp(item.path));
    if(arr){
        var obj = {};
        for(var i = 1; i<=item.params.length;i++){
            obj[item.params[i-1]] = arr[i];
        }
        req.params = obj;
        item.fn(req,res);
    }else{
        next();
    }
}else{
    if(item.path==pathname||item.path=='*'&&item.method ==method){
        item.fn(req,res);
    }else{
        next();
    }
}

2.9 express中的send方法 #

参数为要响应的内容,可以智能处理不同类型的数据,在输出响应时会自动进行一些设置,比如HEAD信息、HTTP缓存支持等等

res.send([body]);

当参数是一个字符串时,这个方法会设置Content-type为text/html

app.get('/', function (req,res) {
    res.send('<p>hello world</p>');
});

当参数是一个Array或者Object,这个方法返回json格式

app.get('/json', function (req,res) {
     res.send({obj:1});
});
app.get('/arr', function (req,res) {
 res.send([1,2,3]);
});

当参数是一个number类型,这个方法返回对应的状态码短语

app.get('/status', function (req,res) {
    res.send(404); //not found
    //res.status(404).send('没有找到');设置短语
});

2.9.1 send方法的实现 #

app.use(function (req,res,next) {
    res.send = function (data) {
        if(typeof data=='string'|| Buffer.isBuffer(data)){
            res.setHeader('content-type','text/html;charset=utf8')
            res.end(data)
        }else if(typeof data=='object'){
            res.setHeader('content-type','application/json;charset=utf8')
            res.end(JSON.stringify(data));
        }else if(typeof data=='number'){
            res.statusCode=data;
            res.end(''+404)
        }
    };
    next();
});

3. 模板的应用 #

3.1 安装ejs #

$ npm install ejs

3.2 设置模板 #

var express = require('express');
var path = require('path');
var app = express();
app.set('view engine','ejs');
app.set('views',path.join(__dirname,'views'));

app.listen(3000);

3.3 渲染html #

app.set('view engine','html')
app.set('views',path.join(__dirname,'views')); 
app.engine('html',require('ejs').__express);

3.4 渲染视图 #

第一个参数 要渲染的模板 第二个参数 渲染所需要的数据

app.get('/', function (req,res) {
    res.render('hello',{title:'hello'});
});

3.5 模板的实现 #

app.use(function (req,res,next) {
    res.render = function (p,d) {
        var p = p.endsWith('.html')?p:p+'.html';
        fs.readFile(path.join(app.get('views'),p),'utf8', function (err,data) {
            data = data.replace(/<%=(\w+)%>/g, function (matcher,group) {
                return d[group];
            });
            res.send(data);
        })
    };
    next();
});

4.静态文件服务器 #

如果要在网页中加载静态文件(css、js、img),就需要另外指定一个存放静态文件的目录,当浏览器发出非HTML文件请求时,服务器端就会到这个目录下去寻找相关文件

var express = require('express');
var app = express();
var path = require('path');
app.use(express.static(path.join(__dirname,'public')));
app.listen(3000);

4.1 静态文件服务器实现 #

app.use(static(path.join(__dirname,'public')));
function static(p){
    return function (req,res,next) {
        var pathname = require('url').parse(req.url,true).pathname;
        var flag =fs.existsSync(path.join(p,pathname));
        if(flag){
            fs.createReadStream(path.join(p,pathname)).pipe(res);
        }else{
            next();
        }
    }
}

5.重定向 #

redirect方法允许网址的重定向,跳转到指定的url并且可以指定status,默认为302方式。 参数1 状态码(可选) 参数2 跳转的路径

res.redirect([status], url);

5.1 redirect使用 #

app.get('/', function (req,res) {
    res.redirect('http://www.baidu.com')
});

5.2 redirect的实现 #

app.use(function (req,res,next) {
    res.redirect = function (url) {
        console.log(url);
        res.statusCode = 302;
        res.setHeader('Location','http://www.baidu.com');
        res.end('');
    };
    next();
});

6. 接收post响应体 #

安装body-parser

$ npm install body-parser

6.1 使用body-parser #

app.get('/login', function (req,res) {
    res.sendFile('./login.html',{root:__dirname})
});
app.post('/user', function (req,res) {
    console.log(req.body);
    res.send(req.body);
});
app.listen(3000);

6.2 req.body的实现 #

app.use(function (req,res,next) {
    var result = '';
    req.on('data', function (data) {
        console.log(data);
        result+=data;
    })
    req.on('end', function () {
        req.body = querystring.parse(result);
        next();
    });
});