美文网首页
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