express框架使用心得

文章目录

  • 一、express 安装
    • 1、使用脚手架 express-generator
    • 2、使用webstorm
    • 3、添加依赖
    • 4、修改package.json
  • 二、app.js
  • 三、路由
    • 1、加路由
    • 2、完整示例:
  • 四、express中间件
  • 五、express登录
    • 1、使用 express-session 和 connect-redis
    • 2、req.session 保存登录信息到redis
    • 3、登录校验做成express中间件
  • 五、日志配置
    • 1、access log 记录 直接使用脚手架推荐的morgan
  • 六、中间件原理

一、express 安装

1、使用脚手架 express-generator

npm install express-generator -g #全局安装
express blog-express #创建一个名为blog-express的项目

2、使用webstorm

3、添加依赖

yarn add nodemon cross-env -D

4、修改package.json

"dev": "cross-env NODE_ENV=development nodemon ./bin/www",
"prod": "cross-env NODE_ENV=production nodemon ./bin/www"

二、app.js

var createError = require('http-errors');// 404 页面
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');//解析cookie req.cookies
var logger = require('morgan'); // 记录日志

var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');

var app = express();

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');

app.use(logger('dev')); //日志
app.use(express.json()); //处理 postData req.body
app.use(express.urlencoded({ extended: false })); //解析application/x-www-form-urlencoded 表单参数 req.body
app.use(cookieParser()); // 解析cookie req.cookies
app.use(express.static(path.join(__dirname, 'public')));

//处理路由
app.use('/', indexRouter);
app.use('/users', usersRouter);

// catch 404 and forward to error handler
app.use(function(req, res, next) {
  next(createError(404));
});

// error handler
app.use(function(err, req, res, next) {
  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = req.app.get('env') === 'development' ? err : {};

  // render the error page
  res.status(err.status || 500);
  res.render('error');
});

module.exports = app;

三、路由

1、加路由

var express = require('express');
var router = express.Router();

/* GET users listing. */
router.get('/list', function (req, res, next) {
    res.json({errno: 0, data: [1, 2, 3]})
});

module.exports = router;

app.js
var blogRouter = require('./routes/my/blog');
app.use('/api/blog', blogRouter);

2、完整示例:

var express = require('express');
const {SuccessModel, ErrorModel} = require('../../model/resModel');
var router = express.Router();
const loginCheck = require('../../middleware/loginCheck')

/* GET users listing. */
router.get('/list', function (req, res, next) {
    let author = req.query.author || '';
    let keyword = req.query.keyword || '';
    getList(author, keyword).then(list => {
        res.json(new SuccessModel(list))
    })
});

router.get('/detail', loginCheck, function (req, res, next) {
    let id = req.query.id;
    getDetail(id).then(data => {
        res.json(new SuccessModel(data))
    })
});

router.get('/new', loginCheck, function (req, res, next) {
    newBlog(body).then(data => {
        res.json(new SuccessModel(data))
    })
});

router.get('/update', loginCheck, function (req, res, next) {
    let id = req.query.id
    updateBlog(id, body).then(result => {
        if (result) {
            res.json(new SuccessModel())
        } else {
            res.json(new ErrorModel('更新博客失败'))
        }
    })
});

router.get('/del', loginCheck, function (req, res, next) {
    let id = req.query.id;
    let author = req.query.author || '';
    delBlog(id, author).then(result => {
        if (result) {
            res.json(new SuccessModel())
        } else {
            res.json(new ErrorModel('删除博客失败'))
        }
    })
});

module.exports = router;

四、express中间件

本质上就是请求的拦截器

const express = require('express')

// 本次 http 请求的实例
const app = express()

app.use((req, res, next) => {
    console.log('请求开始...', req.method, req.url)
    next()
})

app.use((req, res, next) => {
    // 假设在处理 cookie
    req.cookie = {
        userId: 'abc123'
    }
    next()
})

app.use((req, res, next) => {
    // 假设处理 post data
    // 异步
    setTimeout(() => {
        req.body = {
            a: 100,
            b: 200
        }
        next()
    })
})

app.use('/api', (req, res, next) => {
    console.log('处理 /api 路由')
    next()
})

app.get('/api', (req, res, next) => {
    console.log('get /api 路由')
    next()
})
app.post('/api', (req, res, next) => {
    console.log('post /api 路由')
    next()
})

// 模拟登录验证
function loginCheck(req, res, next) {
    setTimeout(() => {
        console.log('模拟登陆失败')
        res.json({
            errno: -1,
            msg: '登录失败'
        })

        // console.log('模拟登陆成功')
        // next()
    })
}

app.get('/api/get-cookie', loginCheck, (req, res, next) => {
    console.log('get /api/get-cookie')
    res.json({
        errno: 0,
        data: req.cookie
    })
})

app.post('/api/get-post-data', loginCheck, (req, res, next) => {
    console.log('post /api/get-post-data')
    res.json({
        errno: 0,
        data: req.body
    })
})

app.use((req, res, next) => {
    console.log('处理 404')
    res.json({
        errno: -1,
        msg: '404 not fount'
    })
})

app.listen(3000, () => {
    console.log('server is running on port 3000')
})

五、express登录

1、使用 express-session 和 connect-redis

yarn add express-session
app.js

let session = require('express-session'); // session

app.use(session({
    secret: 'asdXXnkj_asdnsk_90789_sad',
    cookie: {
        path: '/',//默认
        httpPnly: true,//默认
        maxAge: 24 * 60 * 60 * 1000
    }
})); // 处理session

测试:
router.get('/session-test', function (req, res, next) {
    let session = req.session;
    if (session.vievCount == null) {
        session.vievCount = 0;
    }
    session.vievCount++;
    res.json(session.vievCount)
});

2、req.session 保存登录信息到redis

yarn add redis connect-redis 
redis.js

const redis = require('redis')
const {REDIS_CONF} = require('../conf/db')

//创建客户端
let redisClient = redis.createClient(REDIS_CONF.port, REDIS_CONF.host);
redisClient.on('error', err => {
    console.log(err)
});
module.exports = redisClient
app.js

let RedisStore = require('connect-redis')(session);

let redisClient = require('./db/redis');
let sessionStore = new RedisStore({
    client: redisClient
});

app.use(session({
    secret: 'asdXXnkj_asdnsk_90789_sad',
    cookie: {
        path: '/',//默认
        httpPnly: true,//默认
        maxAge: 24 * 60 * 60 * 1000
    },
    store: sessionStore
})); // 处理session

3、登录校验做成express中间件

loginCheck.js

const {SuccessModel, ErrorModel} = require('../model/resModel');

module.exports = (re, res, next) => {
    if (require.session.username) {
        next()
    } else {
        res.json(new ErrorModel("未登录"))
    }
}

路由里面:

blog.js

const loginCheck = require('../middleware/loginCheck')

router.post('/new', loginCheck, (req, res, next) => {
    req.body.author = req.session.username
    const result = newBlog(req.body)
    return result.then(data => {
        res.json(
            new SuccessModel(data)
        )
    })
})

五、日志配置

1、access log 记录 直接使用脚手架推荐的morgan

app.use(logger('dev')); //直接打印控制台日志
  • 默认是输出到控制台,即:
app.use(logger('dev',{
	stream: process.stdout
}));
  • dev:表示日志的格式,可以改成combined

  • 配置详情:https://github.com/expressjs/morgan

  • 修改日志输出文件:

最终配置:
let env = process.env.NODE_ENV;
if (env === 'developement') {
    //开发环境或测试环境
    app.use(logger('dev')); //日志
} else {
    //线上环境
    let accessPath = path.join(__dirname, 'logs', 'access.log');
    let writeStream = fs.createWriteStream(accessPath, {
        flags: 'a'
    })
    app.use(logger('combined', {
        stream: writeStream
    })); //日志
}

六、中间件原理

  • app.use 用来注册中间件,先收集起来
  • 遇到http请求,根据path和method判断触发哪些
  • 实现next机制,即上一个通过next触发下一个

类似于express的代码:

const http = require('http');
const slice = Array.prototype.slice;

class LikeExpress {
    constructor() {
        //存放中间件的列表
        this.routes = {
            all: [],
            get: [],
            post: []
        }
    }

    register(path) {
        let info = {};
        if (typeof path === 'string') {
            info.path = path;
            //从第二个参数开始,转换为数组,存入stack
            info.stack = slice.call(arguments, 1)//数组
        } else {
            info.path = '/'
            //从第1个参数开始,转换为数组,存入stack
            info.stack = slice.call(arguments, 0)//数组
        }
        return info;
    }

    use() {
        let info = this.register.apply(this, arguments);
        this.routes.all.push(info);
    }

    get() {
        let info = this.register.apply(this, arguments);
        this.routes.get.push(info);
    }

    post() {
        let info = this.register.apply(this, arguments);
        this.routes.post.push(info);
    }

    match(method, url) {
        let stack = [];
        if (url === '/favicon.ico') {
            return stack;
        }
        //获取routers
        let currentRoutes = [];
        currentRoutes = currentRoutes.concat(this.routes.all);
        currentRoutes = currentRoutes.concat(this.routes[method]);
        currentRoutes.forEach(routerInfo => {
            // url === '/api/get-cookie' 且 routeInfo.path === '/'
            // url === '/api/get-cookie' 且 routeInfo.path === '/api'
            // url === '/api/get-cookie' 且 routeInfo.path === '/api/get-cookie'
            if (url.indexOf(routerInfo.path) === 0) {
                stack = stack.concat(routerInfo.stack);
            }
        })
        return stack;
    }

    //核心的next机制
    handle(req, res, stack) {
        let next = () => {
            //拿到第一个匹配的中间件
            let middleWare = stack.shift();
            if (middleWare) {
                middleWare(req, res, next)
            }
        };
        next();
    }

    callBack() {
        return (req, res) => {
            res.json = (data) => {
                res.setHeader('Content-Type', 'application/json');
                res.end(JSON.stringify(data));
            };
            let url = req.url;
            let method = req.method.toLowerCase();

            let resultList = this.match(method, url);
            this.handle(req, res, resultList);
        }
    }

    listen(...args) {
        let server = http.createServer(this.callBack());
        server.listen(...args)
    }
}


//工厂函数
module.exports = () => {
    return new LikeExpress();
}

测试:

let express = require('./simple')

//本次http请求的实例
let app = express();

app.use((req, res, next) => {
    console.log('请求开始...', req.method, req.url)
    next()
})

app.use((req, res, next) => {
    // 假设在处理 cookie
    console.log('处理 cookie ...')
    req.cookie = {
        userId: 'abc123'
    }
    next()
})

app.use('/api', (req, res, next) => {
    console.log('处理 /api 路由')
    next()
})

app.get('/api', (req, res, next) => {
    console.log('get /api 路由')
    next()
})

// 模拟登录验证
function loginCheck(req, res, next) {
    setTimeout(() => {
        console.log('模拟登陆成功')
        next()
    })
}

app.get('/api/get-cookie', loginCheck, (req, res, next) => {
    console.log('get /api/get-cookie')
    res.json({
        errno: 0,
        data: req.cookie
    })
})

app.listen(8000, () => {
    console.log('server is running on port 8000')
})



你可能感兴趣的:(后端,互联网技术,前端,nodejs,express)