手动实现 express

express 中文翻译 “快递”

  • 基于http服务进行了二次封装
  • 在express中扩展了很多 中间件
  • 对路由进行了封装
  • req,res 进行了一些功能的封装

关于express和koa中间件的区别

  • koa 小巧,仅做基础封装
  • express 有很多额外的中间件,路由

简单使用

需要安装npm install express express.js

let express = require('../node_modules/express');
let app = express();
app.get('/',(req,res)=>{
  res.end('ok'+req.money);
});
app.get('/hello', (req, res) => {
  res.end('hello');
});
app.delete('/hello', (req, res) => {
  res.end('delete hello');
});
app.post('/hello',(req,res)=>{
  res.end('post hello');
});
app.all('*',(req,res)=>{
  res.end('end');
});
// 发送静态文件
app.get('/404',function (req,res) {
  res.sendFile('404.html',{root:__dirname});//必须保证绝对路径
  //res.sendFile(require('path').join(__dirname,'404.html'));
})
app.listen(3000);
复制代码

上述我们可以看出:

  • express是一个函数
  • 函数有 delete, post,all等,处理路由
  • express执行后 返回的是一个监听函数
  • 该函数可以起服务
  • 在访问路由之前可以做很多事情 => 中间件

express架子 express/index.js


let http = require('http');
let url = require('url');
function createApplication(){
    //若果请求方法,路径一致,则执行回调
    let app = (req,res) => {
        let reqMethod = req.method.toLowerCase();
        let {pathname} = url.parse(req.url,true);
        for(let i=0;i<app.routes.length;i++){
            let {method,path,handler} = app.routes[i];
            if(method === reqMethod && pathname == path){
                handler(req,res)
            }
        }
        res.statusCode = 404;
        res.end(`Cannot ${reqMethod} ${pathname}`);
    }
    app.routes = [];
    //存储 方法 路径 回调
    app.get = (path,handler) => {
        app.routes.push({
            method:'get',
            path,
            handler
        })
    }
    app.listen = (...args) => {
        let server = http.createServer(app);
        server.listen(...args);
    }
    return  app;
}
module.exports = createApplication
复制代码

post测试:可以下载postman插件下载安装

all

function createApplication(){
    ...
    let app = (req,res) => {
        ...
        for(let i=0;i<app.routes.length;i++){
            let {method,path,handler} = app.routes[i];
            if((method === reqMethod || method === all)&& (pathname == path|| pathname === '*')){
                handler(req,res)
            }
        }
        res.statusCode = 404;
        res.end(`Cannot ${reqMethod} ${pathname}`);
    }
    app.all = function (path, handler) {
        app.routes.push({
          method: 'all',
          path,
          handler
        })
    }
    ...
}
复制代码

洋葱模型

express/test1.js

function app(){

}
app.routes = [];
app.use = function(cb){
    app.routes.push(cb)
}

app.use((next)=> {
    console.log(1);
    next();
    console.log(2);
})
app.use((next)=> {
    console.log(3);
    next();
    console.log(4);
})    
app.use((next)=> {
    console.log(5);
    console.log(6);
})  

let index = 0;
function next(){
    if(index === app.routes.lenth) return;
    app.routes[index++](next)
}
next();
复制代码

输出135642,这就是我们说的洋葱模型

中间件

  • 中间件作用 当我们真正访问的路由之前可以干很多事情
  • 可以决定是否向下执行
  • 可以做一些权限判断,可以写多个中间件,和路由是放在同一个队列中的
  • req是同一个
  • 类似于洋葱模型

express.js

app.use('/', (req,res,next)=> {
  req.money = 10000;
  next();// 继续的意思,不调用next表示不继续了
});
app.use((req, res, next) =>{
  req.money -= 2000;
  next();
});
app.get('/',(req,res)=>{
  res.end('ok'+req.money);
});
复制代码

最后 localhost:3000 输出ok 3000

中间件实现 express/index.js

...
function createApplication(){
    let app = (req,res) => {
        let reqMethod = req.method.toLowerCase();
        let {pathname} = url.parse(req.url,true);
        let index = 0;
        function next(){//将回调转换为next形式
            if(index === app.routes.length){
                res.statusCode = 404;
                res.end(`Cannot ${reqMethod} ${pathname}`);
                return;
            } 
            let {method,path,handler} = app.routes[index++];
            if(method === 'middleware'){
                //中间件
                if(pathname == path || path === '/' || pathname.startsWith(path + '/')){
                    handler(req,res,next)
                }else{
                    next();//  没有迭代到 就执行下一个中间件
                }
            }else{
                //路由
                if((method === reqMethod || method === all)&& (pathname == path|| pathname === '*')){
                    handler(req,res);
                }else{
                    next();
                }
            }
        }
        next();
    }
    app.routes = [];
    //存储 方法 路径 回调
    let methods = ['get', 'post', 'put', 'delete', 'options'];
    app.use = function(path,handler){
        if(typeof handler != 'function'){
            handler = path;
            path = '/'
        }
        app.routes.push({
            method: 'middleware',
            path,
            handler
        })
    }
    ...
    return  app;
}
module.exports = createApplication
复制代码

有个特殊的地方,比如我们的路径是article/9/zdl,我们这样写article/:age/:name => {age:9,name:zdl}

如何解析这种路径?在express里可以通过req.params获取到

express.js

let express = require('../express');
let app = express();
app.get('/article/:id/:name', (req,res,next)=> {
    console.log(req.params);
    res.end(JSON.stringify(req.params));
});
app.listen(3000);
复制代码

test: localhost:3000/:1/zdl1

express/index.js

...
function createApplication(){
    let app = (req,res) => {
        ...
        function next(){
            ...
            if(method === 'middleware'){
                ...
            }else{
                //路由
                if(path.params){
                    // 到路径参数的路由
                    if(path.test(pathname)){
                        let params = {}
                        let values = pathname.match(path).slice(1);
                        values.forEach((value,index)=>{
                            params[path.params[index]] = value
                        });
                        req.params = params; // 把参数挂载到req上
                        handler(req,res);
                    }else{ 
                        next();
                    }
                }else{
                    ...
                }
            }
        }
        next();
    }
    ...
    methods.forEach(method => {
        app[method] = (path,handler) => {
            //带路径参数的路由
            let params = [];
            if(path.includes(':')){//如果路径带有:就把路径转换成正则
                path = path.replace(/:([^\/]+)/g,function(){
                    params.push(arguments[1]);
                    return '([^\/]+)';
                })
                path = new RegExp(path);
                path.params = params;
                
            }
            app.routes.push({
                method,
                path,
                handler
            })
        }
    })
    ...
    return  app;
}
module.exports = createApplication
复制代码

目录:

最后我们完整的index.js


let http = require('http');
let url = require('url');
function createApplication(){
    //若果请求方法,路径一致,则执行回调
    let app = (req,res) => {
        let reqMethod = req.method.toLowerCase();
        let {pathname} = url.parse(req.url,true);
        let index = 0;
        function next(){
            if(index === app.routes.length){
                res.statusCode = 404;
                res.end(`Cannot ${reqMethod} ${pathname}`);
                return;
            } 
            let {method,path,handler} = app.routes[index++];
            if(method === 'middleware'){
                //中间件
                if(pathname === path || path == '/' || pathname.startsWith(path + '/')){
                    handler(req,res,next)
                }else{
                    next();//  没有迭代到 就执行下一个中间件
                }
            }else{
                //路由
                if(path.params){
                    // 到路径参数的路由
                    if(path.test(pathname)){
                        let params = {}
                        let values = pathname.match(path).slice(1);
                        values.forEach((value,index)=>{
                            params[path.params[index]] = value
                        });
                        req.params = params; // 把参数挂载到req上
                        handler(req,res);
                    }else{ 
                        next();
                    }
                }else{
                    if((method === reqMethod || method === all)&& (pathname == path|| pathname === '*')){
                        handler(req,res);
                    }else{
                        next();
                    }
                }
            }
        }
        next();
    }
    app.routes = [];
    //存储 方法 路径 回调
    let methods = ['get', 'post', 'put', 'delete', 'options'];
    app.use = function(path,handler){
        if(typeof handler != 'function'){
            handler = path;
            path = '/'
        }
        app.routes.push({
            method: 'middleware',
            path,
            handler
        })
    }
    app.all = function (path, handler) {
        app.routes.push({
            method: 'all',
            path,
            handler
        })
    }
    methods.forEach(method => {
        app[method] = (path,handler) => {
            //带路径参数的路由
            let params = [];
            if(path.includes(':')){//如果路径带有:就把路径转换成正则
                path = path.replace(/:([^\/]+)/g,function(){
                    params.push(arguments[1]);
                    return '([^\/]+)';
                })
                path = new RegExp(path);
                path.params = params;
                
            }
            app.routes.push({
                method,
                path,
                handler
            })
        }
    })
    app.listen = (...args) => {
        let server = http.createServer(app);
        server.listen(...args);
    }
    return  app;
}
module.exports = createApplication
复制代码

中间件-1

express内置了很多中间件,把想要的方法已经解析好了,可以直接使用

let express = require('./express');
let app = express();

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

app.get('/article', (req,res,next)=> {
  console.log(req.path,req.query);
  res.send({name:'zfpx'});
});
app.listen(3000);

复制代码

实现 app.use的内容

test: localhost:3000/article?a=1&b=2

app.use(function (req,res,next) {
    let {path,query} = url.parse(req.url,true);
    req.path = path;
    req.query = query;
    req.hostname = 'XXXX';
    //装饰模式
    let end = res.end.bind(res);
    res.send = function(value){
        console.log(typeof value);
        if(typeof value === 'object'){
            end(JSON.stringify(value))
        }else if(Buffer.isBuffer(value) || typeof value === 'string'){
            end(value)
        }else{
            end(value.toString())
        }
        
    }
    next();
});
复制代码

中间件-2

启用一个静态服务

index.js

let express = require('./express');
let app = express();

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

app.get('/article', (req,res,next)=> {
  console.log(req.path,req.query);
  res.send({name:'zfpx'});
});
app.listen(3000);

复制代码

use内容

let express = require('express');
let app = express();

let path = require('path');
let fs = require('fs')

function static(p){
    return (req ,res ,next) => {
        let newPath = path.join(p, req.path);
        fs.stat(newPath,(err,stat) => {
             if(err){
                 next();
             }else{
                 console.log(stat.isDirectory());
                 
                 if(stat.isDirectory()){
                     let p = path.join(newPath,'/index.html');
                     console.log(p);
                     
                     fs.createReadStream(p).pipe(res)
                 }else{
                     fs.createReadStream(newPath).pipe(res)
                 }
             }
        })
    }
}

复制代码
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值