美文网首页
Node.js 开发web server博客项目[4-5]

Node.js 开发web server博客项目[4-5]

作者: Mark同学 | 来源:发表于2019-12-05 11:09 被阅读0次

    第4章 开发博客项目之接口

    系统架构设计的四层抽象:

    第一层: www.js [开启 Server]
    第二层:app.js [通信设置层]
    第三层:router.js [业务逻辑层]
    第四层:controller.js [数据层]

    4-5 搭建开发环境

    mkdir blog-node
    cd blog-node
    npm init -y
    npm i cross-env -D
    npm i nodemon -D

    1.基本设置代码

    blog-node
    ——app.js

    const serverHandle = (req, res) => {
      // 设置返回格式 JSON
      res.setHeader('Content-type', 'application/json');
      
      const resData = {
        name: '双越',
        site: 'imooc',
        env: process.env.NODE_ENV
      };
     
      res.end(JSON.stringify(resData));
    };
    
    module.exports = serverHandle;
    

    2.配置环境

    blog-node
    ——package.json

    {
      "name": "blog-node",
      "version": "1.0.0",
      "description": "",
      "main": "bin/www.js",
      "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1",
        "dev": "cross-env NODE_ENV=dev nodemon ./bin/www.js",
        "prd": "cross-env NODE_ENV=production nodemon ./bin/www.js"
      },
      "keywords": [],
      "author": "",
      "license": "ISC",
      "devDependencies": {
        "cross-env": "^6.0.3",
        "nodemon": "^2.0.1"
      }
    }
    

    3.创建 Server 的代码

    blog-node
    ——bin
    ————www.js

    const http = require('http');
    const PORT = 8000;
    const serverHandle = require('../app');
    const server = http.createServer(serverHandle);
    server.listen(PORT);
    

    npm run dev

    4-6 初始化路由

    1.拆分路由处理

    blog-node
    ——app.js

    const serverHandle = (req, res) => {
      // 设置返回格式 JSON
      res.setHeader('Content-type', 'application/json');
    };
    
    module.exports = serverHandle;
    //process.env.NODE_ENV
    

    2.实现5个接口

    blog-node
    ——src
    ————router
    ——————blog.js

    const handleBlogRouter = (req, res) => {
      const method = req.method;
    
      // 获取博客列表
      if(method === 'GET' && req.path === '/api/blog/list') {
        return {
          msg: '这是获取博客列表的接口'
        }
      }
    
      // 获取博客详情
      if(method === 'GET' && req.path === '/api/blog/detail') {
        return {
          msg: '这是获取博客详情的接口'
        }
      }
    
      // 新建一篇博客
      if(method === 'POST' && req.path === '/api/blog/new') {
        return {
          msg: '这是新建博客的接口'
        }
      }
    
      // 更新一篇博客
      if(method === 'POST' && req.path === '/api/blog/update') {
        return {
          msg: '这是更新博客的接口'
        }
      }
    
      // 删除一篇博客
      if(method === 'POST' && req.path === '/api/blog/del') {
        return {
          msg: '这是删除博客的接口'
        }
      }
    };
    
    module.exports = handleBlogRouter;
    

    3.实现1个接口

    blog-node
    ——src
    ————router
    ——————user.js

    const handleUserRouter = (req, res) => {
      const method = req.method;
    
      // 登录
      if(method === 'POST' && req.path === '/api/user/login') {
        return {
          msg: '这是登录的接口'
        }
      }
    };
    
    module.exports = handleUserRouter;
    

    4.在 app.js 里调用路由

    blog-node
    ——app.js

    const handleBlogRouter = require('./src/router/blog');
    const handleUserRouter = require('./src/router/user');
    const serverHandle = (req, res) => {
      // 设置返回格式 JSON
      res.setHeader('Content-type', 'application/json');
    
      // 获取 path
      const url = req.url;
      req.path = url.split('?')[0];
    
      // 处理 blog 路由
      const blogData = handleBlogRouter(req, res);
      if (blogData) {
        res.end(
          JSON.stringify(blogData)
        )
        return
      }
    
      // 处理 user 路由
      const userData = handleUserRouter(req, res);
      if (userData) {
        res.end(
          JSON.stringify(userData)
        )
        return
      }
    
      // 未命中路由,返回 404
      res.writeHead(404, {"Content-type": "text/plain"});
      res.write("404 Not Found\n");
      res.end();
    };
    
    module.exports = serverHandle;
    // process.env.NODE_ENV
    

    4-7 开发路由(博客列表路由)

    1.开发数据模型

    src
    ——model
    ————resModel.js

    class BaseModel {
      constructor(data, message) {
        if (typeof data === 'string') {
          this.message = data
          data = null
          message = null
        }
        if (data) {
          this.data = data
        }
        if (message) {
          this.message = message
        }
      }
    }
    
    class SuccessModel extends BaseModel {
      constructor(data, message) {
        super(data, message)
        this.errno = 0
      }
    }
    
    class ErrorModel extends BaseModel {
      constructor(data, message) {
        super(data, message)
        this.errno = -1
      }
    }
    
    module.exports = {
      SuccessModel,
      ErrorModel
    }
    

    2.解析 query

    blog-node
    ——app.js

    const querystring = require('querystring');
    
    const serverHandle = (req, res) => {
      // 设置返回格式 JSON
      // ...
    
      // 获取 path
      // ...
    
      // 解析 query
      req.query = querystring.parse(url.split('?')[1]);
    
      // 处理 blog 路由
      // ...
    
      // 处理 user 路由
      // ...
    
      // 未命中路由,返回 404
      // ...
    };
    
    module.exports = serverHandle;
    // process.env.NODE_ENV
    

    3.返回格式正确的假数据

    controller 只关心数据

    blog-node
    ——src
    ————controller
    ——————blog.js

    const getList = (author, keyword) => {
      return [
        {
          id: 1,
          title: '标题A',
          content: '内容A',
          createTime: 1546610491112,
          author: 'zhangsan'
        },
        {
          id: 2,
          title: '标题B',
          content: '内容B',
          createTime: 1546610524373,
          author: 'lisi'
        },
      ]
    };
    module.exports = {
      getList
    };
    

    4.在路由里面引用 controller

    blog-node
    ——src
    ————router
    ——————blog.js

    const { getList } = require('../controller/blog');
    const { SuccessModel, ErrorModel} = require('../model/resModel');
    
    const handleBlogRouter = (req, res) => {
      const method = req.method;
    
      // 获取博客列表
      if(method === 'GET' && req.path === '/api/blog/list') { 
        const author = req.query.author || '';
        const keyword = req.query.keyword || '';
        const listData = getList(author, keyword);
        return new SuccessModel(listData);
      }
    
      // 获取博客详情
      // ...
    
      // 新建一篇博客
      // ...
    
      // 更新一篇博客
      // ...
    
      // 删除一篇博客
      // ...
    };
    
    module.exports = handleBlogRouter;
    

    4-8 开发路由(博客详情路由)

    1.

    blog-node
    ——src
    ————controller
    ——————blog.js

    const getList = (author, keyword) => {
      // ...
    };
    
    const getDetail = (id) => {
      return {
        id: 1,
        title: '标题A',
        content: '内容A',
        createTime: 1546610491112,
        author: 'zhangsan'
      }
    };
    
    module.exports = {
      getList,
      getDetail
    };
    

    2.

    blog-node
    ——src
    ————router
    ——————blog.js

    const { getList, getDetail } = require('../controller/blog');
    
    const handleBlogRouter = (req, res) => {
      const method = req.method;
    
      // 获取博客列表
      // ...
    
      // 获取博客详情
      if(method === 'GET' && req.path === '/api/blog/detail') {
        const id = req.query.id;
        const data = getDetail(id);
        return new SuccessModel(data);
      }
    
      // 新建一篇博客
      // ...
    
      // 更新一篇博客
      // ...
    
      // 删除一篇博客
      // ...
    };
    
    module.exports = handleBlogRouter;
    

    3.用 promise 处理Post 接口

    files
    ——a.json

    {
        "next": "b.json",
        "msg": "this is a"
    }
    

    ——b.json

    {
        "next": "c.json",
        "msg": "this is b"
    }
    

    ——c.json

      
    {
        "next": null,
        "msg": "this is c"
    }
    
    1.回调地狱

    用 callback 方式获取一个文件的内容

    const fs = require('fs')
    const path = require('path')
    
    function getFileContent(fileName, callback) {
      const fullFileName = path.resolve(__dirname, 'files', fileName)
        fs.readFile(fullFileName, (err, data) => {
          if (err) {
            console.error(err)
            return
          }
          callback(
            JSON.parse(data.toString())
          )
      })
    }
    
    测试 callback-hell
    getFileContent('a.json', aData => {
       console.log('a data', aData)
       getFileContent(aData.next, bData => {
          console.log('b data', bData)
          getFileContent(bData.next, cData => {
              console.log('c data', cData)
          })
       })
    })
    
    2.介绍Promise,解决回调地狱

    用 promise 方式获取文件内容

    function getFileContent(fileName) {
        const promise = new Promise((resolve, reject) => {
            const fullFileName = path.resolve(__dirname, 'files', fileName)
            fs.readFile(fullFileName, (err, data) => {
                if (err) {
                    reject(err)
                    return
                }
                resolve(
                    JSON.parse(data.toString())
                )
            })
        })
        return promise
    }
    
    getFileContent('a.json').then(aData => {
      console.log('a data', aData)
      return getFileContent(aData.next)
    }).then(bData => {
       console.log('b data', bData)
       return getFileContent(bData.next)
     }).then(cData => {
       console.log('c data', cData)
    })
    
    3.介绍 async await,用同步的方式写异步

    用 async await 方式获取文件内容

    async function readFileData() {
        // 同步写法
        try {
            const aData = await getFileContent('a.json')
            console.log('a data', aData)
            const bData = await getFileContent(aData.next)
            console.log('b data', bData)
            const cData = await getFileContent(bData.next)
            console.log('c data', cData)
        } catch (err) {
            console.error(err)
        }
    }
    
    readFileData()
    
    // async await 要点:
    // 1. await 后面可以追加 promise 对象,获取 resolve 的值
    // 2. await 必须包裹在 async 函数里面
    // 3. async 函数执行返回的也是一个 promise 对象
    // 4. try-catch 截获 promise 中 reject 的值
    

    4-9 开发路由(处理 POSTData)

    1.使用 promise 处理 post data

    将路由处理的相关函数放到获取到 post 数据之后执行

    blog-node
    ——app.js

    // 用于处理 post data
    const getPostData = (req) => {
      const promise = new Promise((resolve, reject) => {
        if (req.method !== 'POST') {
          resolve({});
          return
        } 
        if (req.headers['content-type'] !== 'application/json') {
          resolve({});
          return
        }
        let postData = '';
        req.on('data', chunk => {
          postData += chunk.toString();
        })
        req.on('end', () => {
          if (!postData) {
            resolve({});
            return
          }
          resolve(
            JSON.parse(postData)
          );
        })
      });
      return promise;
    };
    
    const serverHandle = (req, res) => {
      // 设置返回格式 JSON
      // ...
    
      // 获取 path
      // ...
    
      // 解析 query
      // ...
    
      // 处理 post data
      getPostData(req).then(postData => {
        req.body = postData;
    
        // 处理 blog 路由
        // ...
    
        // 处理 user 路由
        // ...
    
        // 未命中路由,返回 404
        // ...
      });
    };
    
    module.exports = serverHandle;
    // process.env.NODE_ENV
    

    4-10 开发路由(新建和更新、删除博客路由)

    1.

    blog-node
    ——src
    ————controller
    ——————blog.js

    // ...
    const newBlog = (blogData = {}) => {
      // blogData 是一个博客对象,包含 title content 属性
      console.log('newBlog', id, blogData);
      return {
        id: 3
      }
    };
    
    const updateBlog = (id, blogData = {}) => {
      // id 就是要更新博客的 id
      // blogData 是一个博客对象,包含 title content 属性
      console.log('updateBlog', id, blogData);
      return true;
    };
    
    const delBlog = (id) => {
      // id 就是要更新博客的 id
      console.log('delBlog', id);
      return true;
    };
    
    module.exports = {
      getList,
      getDetail,
      newBlog,
      updateBlog,
      delBlog
    };
    

    2.

    blog-node
    ——src
    ————router
    ——————blog.js

    const { getList, getDetail, newBlog, updateBlog, delBlog } = require('../controller/blog');
    
    const handleBlogRouter = (req, res) => {
      const method = req.method;
      const id = req.query.id;
    
      // 获取博客列表
      // ...
    
      // 获取博客详情
      // ...
    
      // 新建一篇博客
      if(method === 'POST' && req.path === '/api/blog/new') {
        const data = newBlog(req.body);
        return new SuccessModel(data);
      }
    
      // 更新一篇博客
      if(method === 'POST' && req.path === '/api/blog/update') {
        const result = updateBlog(id, req.body);
        if (result) {
          return new SuccessModel();
        } else {
          return new ErrorModel('更新博客失败');
        }
      }
    
      // 删除一篇博客
      if(method === 'POST' && req.path === '/api/blog/del') {
        const result = delBlog(id)
        if (result) {
          return new SuccessModel()
        } else {
          return new ErrorModel('删除博客失败')
        }
      }
    };
    
    module.exports = handleBlogRouter;
    

    4-11 开发路由(登录路由)

    1.

    blog-node
    ——src
    ————controller
    ——————user.js

    const login = (username, password) => {
      if (username === 'zhangsan' && password === '123') {
        return true;
      }
      return false;
    }
    
    module.exports = {
      login
    };
    

    2.

    blog-node
    ——src
    ————router
    ——————user.js

    const { login } = require('../controller/user');
    const { SuccessModel, ErrorModel} = require('../model/resModel');
    
    const handleUserRouter = (req, res) => {
      const method = req.method;
    
      // 登录
      if (method === 'POST' && req.path === '/api/user/login') {
        const { username, password } = req.body;
        const result = login(username, password);
        if (result) {
          return new SuccessModel();
        } 
        return new ErrorModel('登录失败');
      }
    }
    
    module.exports = handleUserRouter;
    

    4-12 补充:路由和API

    第5章 开发博客项目之数据存储

    nodejs web 博客.jpeg nodejs web 博客.jpeg
    use myblog;
    show tables;
    select * from blogs;
    select * from users;
    insert into users (username, `password`, realname) values ('zhangsan', '123', '张三');
    insert into users (username, `password`, realname) values ('lisi', '456', '李四');
    insert into blogs (title,content,createtime,author) values ('标题A','内容A',1575539061028,'zhangsan');
    insert into blogs (title,content,createtime,author) values ('标题B','内容B',1575539103536,'lisi');
    insert into blogs (title,content,createtime,author) values ('标题C','内容C',1575539160333,'zhangsan');
    

    5-5 nodejs 链接 mysql 做成工具

    npm i mysql --save --registry=https://registry.npm.taobao.org

    npm run dev

    1.根据环境连接不同的数据库

    blog-node
    ——src
    ————conf
    ——————db.js

    const env = process.env.NODE_ENV;
    
    let MYSQL_CONF;
    
    if (env === 'dev') {
      MYSQL_CONF = {
        host: 'localhost',
        user: 'root',
        password: 'root',
        port: '3306',
        database: 'myblog'
      }
    }
    
    if (env === 'production') {
      MYSQL_CONF = {
        host: 'localhost',
        user: 'root',
        password: 'root',
        port: '3306',
        database: 'myblog'
      }
    }
    
    module.exports = {
      MYSQL_CONF
    };
    

    2.数据库操作的封装

    blog-node
    ——src
    ————db
    ——————mysql.js

    const mysql = require('mysql');
    const { MYSQL_CONF } = require('../conf/db');
    
    // 创建连接对象
    const con = mysql.createConnection(MYSQL_CONF);
    
    // 开始连接
    con.connect();
    
    // 统一执行 sql 的函数
    function exec(sql) {
      const promise = new Promise((resolve, reject) => {
        con.query(sql, (err, result) => {
          if(err) {
            reject(err)
          }else {
            resolve(result)
          }
        })
      });
      return promise;
    };
    
    module.exports = {
      exec
    };
    

    5-6 API对接mysql(博客列表)

    1.连接数据库替换掉假数据

    blog-node
    ——src
    ————controller
    ——————blog.js

    const { exec } = require('../db/mysql');
    
    const getList = (author, keyword) => {
      let sql = `select * from blogs where 1=1 `;
      if (author) {
        sql += `and author='${author}' `;
      }
      if (keyword) {
        sql += `and title like '%${keyword}%' `;
      }
      sql += `order by createtime desc;`;
      return exec(sql);
    }
    

    2.

    blog-node
    ——src
    ————router
    ——————blog.js

      // 获取博客列表
      if (method === 'GET' && req.path === '/api/blog/list') {
        //...
        // const listData = getList(author, keyword);
        // return new SuccessModel(listData);
        const result = getList(author, keyword);
        return result.then(listData => {
          return new SuccessModel(listData);
        });
      }
    

    3.

    blog-node
    ——app.js

        // 处理 blog 路由
        // const blogData = handleBlogRouter(req, res);
        // if (blogData) {
        //   res.end(
        //     JSON.stringify(blogData)
        //   );
        //   return
        // }
        const blogResult = handleBlogRouter(req, res);
        if (blogResult) {
          blogResult.then(blogData => {
            res.end(
              JSON.stringify(blogData)
            );
          });
          return
        }
    

    5-7&5-8 API对接mysql(博客详情,增、删、改、查)

    1.连接数据库替换掉假数据

    blog-node
    ——src
    ————controller
    ——————blog.js

    // 获取博客详情
    const getDetail = (id) => {
      let sql = `select * from blogs where id='${id}'`;
      return exec(sql).then(rows => {
        return rows[0];
      });
    };
    
    // 新增一篇博客
    const newBlog = (blogData = {}) => {
      const { title, content, author } = blogData;
      const createtime = Date.now();
      
      let sql = `
        insert into blogs (title, content, createtime, author) 
        values ('${title}', '${content}', ${createtime}, '${author}');
      `;
    
      return exec(sql).then(insertData => {
        console.log('insertData is...', insertData);
        return {
          id: insertData.insertId
        };
      })  
    };
    
    // 更新一篇博客
    const updateBlog = (id, blogData = {}) => {
      const { title, content } = blogData;
    
      let sql = `update blogs set title='${title}', content='${content}' where id=${id};`;
      
      return exec(sql).then(updateData => {
        console.log('updateData is... ', updateData);
        if (updateData.affectedRows > 0) {
          return true;
        }
        return false;
      });
    };
    
    // 删除一篇博客
    const delBlog = (id, author) => {
      let sql = `delete from blogs where id='${id}' and author='${author}'`;
      return exec(sql).then(delData => {
        if (delData.affectedRows > 0) {
          return true;
        }
        return false;
      })
    };
    

    2.

    blog-node
    ——src
    ————router
    ——————blog.js

     // 获取博客详情
      if(method === 'GET' && req.path === '/api/blog/detail') {
        // const data = getDetail(id);
        // return new SuccessModel(data);
        const result = getDetail(id);
        return result.then(data => {
          return new SuccessModel(data);
        });
      }
    
     // 新增一篇博客
      if (method === 'POST' && path === '/api/blog/new') {
        // const data = newBlog(req.body);
        // return new SuccessModel(data);
        req.body.author = 'zhangsan'; // 假数据, 待开发登陆时再改成真实数据
        const result = newBlog(req.body);
        return result.then(data => {
          return new SuccessModel(data);
        });
      }
    
     // 更新一篇博客
      if(method === 'POST' && req.path === '/api/blog/update') {
        const result = updateBlog(id, req.body);
        return result.then(val => {
          if (val) {
            return new SuccessModel();
          } else {
            return ErrorModel('更新博客失败');
          }
        })
      }
    
     // 删除一篇博客
      if(method === 'POST' && req.path === '/api/blog/del') {
        const author = 'zhangsan';// 假数据, 待开发登陆时再改成真实数据
        const result = delBlog(id, author);
        return result.then(val => {
          if (val) {
            return new SuccessModel();
          } else {
            return new ErrorModel('删除博客失败');
          }
        })
      }
    

    5-9 API对接mysql(登录)

    1.

    blog-node
    ——src
    ————controller
    ——————user.js

    const { exec } = require('../db/mysql');
    
    const login = (username, password) => {
      const sql = `select username, realname from users where username='${username}' and password='${password}';`;
      return exec(sql).then(rows => {
        return rows[0] || {};
      })
    }
    
    module.exports = {
      login
    };
    

    2.

    blog-node
    ——src
    ————controller
    ——————user.js

    const { login } = require('../controller/user');
    const { SuccessModel, ErrorModel} = require('../model/resModel');
    
    const handleUserRouter = (req, res) => {
      const method = req.method;
    
      // 登录
      if(method === 'POST' && req.path === '/api/user/login') {
        const { username, password } = req.body;
        const result = login(username, password);
        return result.then(data => {
          if (data.username) {
            return new SuccessModel();
          } else {
            return new ErrorModel('登录失败');
          }
        });
      }
    }
    
    module.exports = handleUserRouter;
    

    3.

    blog-node
    ——app.js

        // 处理 user 路由
        // const userData = handleUserRouter(req, res)
        // if (userData) {
        //   res.end(
        //     JSON.stringify(userData)
        //   )
        //   return
        // }
        const userResult = handleUserRouter(req, res);
        if (userResult) {
          userResult.then(userData => {
            res.end(
              JSON.stringify(userData)
            );
          });
          return
        } 
    

    相关文章

      网友评论

          本文标题:Node.js 开发web server博客项目[4-5]

          本文链接:https://www.haomeiwen.com/subject/ybqdgctx.html