zoukankan      html  css  js  c++  java
  • Node.js 从零开发 web server博客项目[express重构博客项目]

    web server博客项目

    1. Node.js 从零开发 web server博客项目[项目介绍]
    2. Node.js 从零开发 web server博客项目[接口]
    3. Node.js 从零开发 web server博客项目[数据存储]
    4. Node.js 从零开发 web server博客项目[登录]
    5. Node.js 从零开发 web server博客项目[日志]
    6. Node.js 从零开发 web server博客项目[安全]
    7. Node.js 从零开发 web server博客项目[express重构博客项目]
    8. Node.js 从零开发 web server博客项目[koa2重构博客项目]
    9. Node.js 从零开发 web server博客项目[上线与配置]

    express 下载 , 安装和使用 , express 中间件机制

    Express 应用程序生成器

    安装 ( 使用脚手架 express-generator )

    • 安装脚手架工具
    • cnpm i express-generator -g
    • 使用 express 脚手架创建项目
    • express blog-express
    • 进入项目目录
    • cd blog-express
    • 安装项目初始化所需要的插件
    • cnpm i
    • 安装热更新插件与环境插件
    • cnpm i nodemon cross-env -D
    • 修改相应的配置
    • package.json增加script项 : "dev": "cross-env NODE_ENV=dev nodemon ./bin/www"
    • 启动项目
    • npm run dev

    初始化代码介绍 , 处理路由

    介绍 app.js

    • 各个插件的作用
    • 思考各个插件的实现原理
    • 处理 get 请求和 post 请求
    var createError = require('http-errors'); // 处理不存在或者错误的页面提供一个友好的提示
    var express = require('express');
    var path = require('path');
    var cookieParser = require('cookie-parser'); // 解析 cookie , 使用 req.cookies 访问 cookie
    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', 'jade');
    
    app.use(logger('dev')); // 日志功能
    app.use(express.json()); // 解析postdata中的json格式
    app.use(express.urlencoded({ extended: false })); // 解析postdata中的from(x-www-form-urlencoded)格式
    app.use(cookieParser());// 解析 cookie , 使用 req.cookies 访问 cookie
    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;
    

    router/users.js

    var express = require('express');
    var router = express.Router();
    
    /* GET users listing. */
    router.get('/', function(req, res, next) {
      res.send('respond with a resource');
    });
    
    module.exports = router;
    

    使用中间件

    Writing middleware for use in Express apps
    Using middleware

    开发接口 , 连接数据库 , 实现登录 , 日志记录

    初始化环境

    • 安装插件 mysql xss
    • mysql controller resModel 相关代码可以复用
    • 初始化目录

    express 开发接口

    • 初始化项目 , 之前的部分代码可以复用

      • 开发路由 , 实现登录

        • 登录
          • 使用 express-session 和 connect-redis , 简单方便

    cnpm i express-session -S
    app.js

    var session = require('express-session')
    		...
    app.use(session({
      secret: 'keyboard cat',
      cookie: {
        path: '/',
        httpOnly: true,
        maxAge: 24 * 60 * 60 * 1000
      }
    }))
    		...
    

    cnpm i redis connect-redis -S
    db/redis.js

    const redis = require('redis')
    const { REDIS_CONF } = require('../conf/db.js')
    
    // 创建客户端
    const redisClient = redis.createClient(REDIS_CONF.port, REDIS_CONF.host)
    redisClient.on('error', err => {
        console.error(err)
    })
    
    module.exports = redisClient
    

    使用
    app.js

    ...
    var RedisStore = require('connect-redis')(session)
    var redisClient = require('./db/redis')
    ...
    var sessionStore = new RedisStore({
      client: redisClient
    })
    
    app.use(session({
      secret: 'abcdefg',
      cookie: {
        path: '/',
        httpOnly: true,
        maxAge: 24 * 60 * 60 * 1000
      },
      store: sessionStore
    }))
    ...
    

    登录之后可查看到redis信息

    express>redis-cli
    127.0.0.1:6379> flushAll
    OK
    127.0.0.1:6379> keys *
    (empty list or set)
    127.0.0.1:6379> keys *
    1) "sess:QDTy6D6sLv-bFU9qxTOk_zqf9msMQ-jL"
    127.0.0.1:6379> get sess:QDTy6D6sLv-bFU9qxTOk_zqf9msMQ-jL
    "{"cookie":{"originalMaxAge":86400000,"expires":"2019-07-12T10:52:47.777Z","httpOnly":true,"path":"/"}}"
    127.0.0.1:6379> get sess:QDTy6D6sLv-bFU9qxTOk_zqf9msMQ-jL
    //  登录后
    "{"cookie":{"originalMaxAge":86400000,"expires":"2019-07-12T10:54:54.582Z","httpOnly":true,"path":"/"},"username":"zhangsan","realname":"xe5xbcxa0xe4xb8x89"}"
    127.0.0.1:6379>
    
    • req.session 保存登录信息 , 登录校验做成 express 中间件

    创建middleware/loginCheck.js

    const { ErrorModel } = require('../model/resModel')
    
    module.exports = (req, res, next) => {
      if (req.session.username) {
        next()
        return
      }
      res.json(
        new ErrorModel('未登录')
      )
    }
    
    • 使用
      router/blog.js
    var express = require('express');
    var router = express.Router();
    const {
      getList,
      getDetail,
      newBlog,
      updateBlog,
      delBlog
    } = require('../controller/blog')
    const { SuccessModel, ErrorModel } = require('../model/resModel')
    const loginCheck = require('../middleware/loginCheck')
    
    
    /* GET users listing. */
    router.get('/list', (req, res, next) => {
      let author = req.query.author || ''
      const keyword = req.query.keyword || ''
    
      if (req.query.isadmin) {
        // 管理员界面
        if (req.session.username == null) {
          // 未登录
          res.json(
            new ErrorModel('未登录')
          )
          return
        }
        // 强制查询自己的博客
        author = req.session.username
      }
    
      const result = getList(author, keyword)
      return result.then(listData => {
        res.json(
          new SuccessModel(listData)
        )
      })
    });
    
    // 获取博客详情
    router.get('/detail', (req, res, next) => {
      const result = getDetail(req.query.id)
      return result.then(data => {
        res.json(
          new SuccessModel(data)
        )
      })
    })
    
    // 新建一篇博客
    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)
        )
      })
    })
    
    // 更新一篇博客
    router.post('/update', loginCheck, (req, res, next) => {
      const result = updateBlog(req.query.id, req.body)
      return result.then(val => {
        if (val) {
          res.json(
            new SuccessModel()
          )
        } else {
          res.json(
            new ErrorModel('更新博客失败')
          )
        }
      })
    })
    
    // 删除一篇博客
    router.post('/del', (req, res, next) => {
      const author = req.session.username
      const result = delBlog(req.query.id, author)
      return result.then(val => {
        if (val) {
          res.json(
            new SuccessModel()
          )
        } else {
          res.json(
            new ErrorModel('删除博客失败')
          )
        }
      })
    })
    
    module.exports = router;
    
    • 记录日志

      • access log 记录 , 直接使用脚手架推荐的 Morgan

    package.json
    "prd": "cross-env NODE_ENV=production nodemon ./bin/www"
    app.js

    const morgan = require('morgan');
    const ENV = process.env.NODE_ENV
    if (ENV !== 'production') {
      // 开发环境 / 测试环境
      app.use(morgan('dev'))
    } else {
      // 线上环境
      const logFileName = path.join(__dirname, 'logs', 'access.log')
      const writeStream = fs.createWriteStream(logFileName, {
        flags: 'a'
      })
      app.use(morgan('combined'), {
        stream: writeStream
      })
    }
    
    • 自定义日志使用 console.log 和 console.error 即可
      • 上线与配置会讲到
    • 日志文件拆分 , 日志内容分析 ,
      • 见上章节

    分析 express 中间件原理

    • 分析如何实现

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

    middleware-express

    const http = require('http')
    const slice = Array.prototype.slice
    
    class LikeExpress {
        constructor() {
            // 存放中间件的列表
            this.routes = {
                all: [],   // app.use(...)
                get: [],   // app.get(...)
                post: []   // app.post(...)
            }
        }
    
        register(path) {
            const info = {}
            if (typeof path === 'string') {
                info.path = path
                // 从第二个参数开始,转换为数组,存入 stack
                info.stack = slice.call(arguments, 1)
            } else {
                info.path = '/'
                // 从第一个参数开始,转换为数组,存入 stack
                info.stack = slice.call(arguments, 0)
            }
            return info
        }
    
        use() {
            const info = this.register.apply(this, arguments)
            this.routes.all.push(info)
        }
    
        get() {
            const info = this.register.apply(this, arguments)
            this.routes.get.push(info)
        }
    
        post() {
            const info = this.register.apply(this, arguments)
            this.routes.post.push(info)
        }
    
        match(method, url) {
            let stack = []
            if (url === '/favicon.ico') {
                return stack
            }
    
            // 获取 routes
            let curRoutes = []
            curRoutes = curRoutes.concat(this.routes.all)
            curRoutes = curRoutes.concat(this.routes[method])
    
            curRoutes.forEach(routeInfo => {
                if (url.indexOf(routeInfo.path) === 0) {
                    // url === '/api/get-cookie' 且 routeInfo.path === '/'
                    // url === '/api/get-cookie' 且 routeInfo.path === '/api'
                    // url === '/api/get-cookie' 且 routeInfo.path === '/api/get-cookie'
                    stack = stack.concat(routeInfo.stack)
                }
            })
            return stack
        }
    
        // 核心的 next 机制
        handle(req, res, stack) {
            const next = () => {
                // 拿到第一个匹配的中间件
                const 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)
                    )
                }
                const url = req.url
                const method = req.method.toLowerCase()
    
                const resultList = this.match(method, url)
                this.handle(req, res, resultList)
            }
        }
    
        listen(...args) {
            const server = http.createServer(this.callback())
            server.listen(...args)
        }
    }
    
    // 工厂函数
    module.exports = () => {
        return new LikeExpress()
    }
    
  • 相关阅读:
    圣诞放长假了
    设计模式Top10排行榜
    成长的烦恼
    小三的故事发家篇
    《家庭财务总管》升级(1.0.0.2)
    紧急项目处理方法
    隐身大法,使民无知
    如何维护自己文章的版权
    在深度学习的视觉VISION领域数据预处理的魔法常数magic constant、黄金数值: mean=[0.485, 0.456, 0.406],std=[0.229, 0.224, 0.225]
    ImageNet数据集下载
  • 原文地址:https://www.cnblogs.com/izhaong/p/12154263.html
Copyright © 2011-2022 走看看