node完成简易博客接口以及联调前端【未完...】

http请求概述

DNS解析,创建TCP链接,发送http请求php

server接收到http请求,处理而且返回数据html

客户端接收到返回数据,处理数据(渲染页面,执行JavaScript脚本)前端

nodejs处理http请求

get请求和querystringnode

post请求和postdatamysql

路由linux

nodejs处理get请求

const http = require('http')
const querystring = require('querystring')
const server = http.createServer((req, res) => {
  const url = req.url;
  const method = req.method;
  req.query = querystring.parse(url.split('?')[1])
  console.log("url:" + url);
  console.log("method:" + method)
  console.log(req.query)
  res.end(JSON.stringify(req.query))
})

server.listen(3000);
console.log('listen on port 3000')
复制代码

nodejs处理post请求

post请求,即客户端要向服务器传递数据,如新建文章nginx

经过post data传递数据git

浏览器没法直接模拟,须要手写js,或者使用postmangithub

安装postman,app或者离线安装插件版web

const http = require('http')

const server = http.createServer((req, res) => {
  if (req.method === 'POST') {
    let postData = '';
    console.log('content-type:' + req.headers['content-type'])
    req.on('data', chunk => {
      postData += chunk.toString()
    })
    req.on('end', () => {
      console.log(postData)
      res.end(postData)
    })
  }
})

server.listen(3000)
console.log('listen on port 3000')
复制代码

nodejs处理路由

github.com/

github.com/username

github.com/username/xx…

const http = require('http');

const server = http.createServer((req, res) => {
  const url = req.url
  const path = url.split('?')[0]
  // 返回路由
  res.end(path);
})

server.listen(3000)
console.log('listen on port 3000')
复制代码

综合示例

const http = require('http')
const querystring = require('querystring')
const server = http.createServer((req, res) => {
  const method = req.method
  const url = req.url
  const path = url.split('?')[0]
  const query = querystring.parse(url.split('?')[1])

  // 设置返回格式
  res.setHeader('Content-type', 'application/json')

  const resData = {
    method,
    url,
    path,
    query
  }

  if (resData.method === 'GET') {
    res.end(
      JSON.stringify(resData)
    )
  }
  if (resData.method === 'POST') {
    let postData = ''
    req.on('data', chunk => postData += chunk.toString())
    req.on('end', () => {
      resData.postData = postData
      res.end(
        JSON.stringify(resData)
      )
    })
  }
  
})

server.listen(3000)
console.log('ok')
复制代码

搭建开发环境

从0开始搭建,不使用框架

使用nodemon 监测文件变化,自动重启node

使用cross-env 设置环境变量,兼容mac linux windows

接口开发

初始化路由:根据以前技术方案的设计,作出路由

返回假数据,将路由和数据处理分离,以符合设计原则

接口设计

一、获取文章列表

接口:/api/blog/list

方法:get

url参数:author,keyword

二、获取文章详情页

接口:/api/blog/detail

方法:get

url参数:id

三、新增文章

接口:/api/blog/new

方法:post

描述:post新增的文章数据

四、文章更新

接口:/api/blog/update

方法:post

参数:id

描述:post更新的内容

五、文章删除

接口:/api/blog/del

方法:post

参数:id

六、登陆

接口:/api/user/login

方法:post

参数:id

描述:post用户名和密码

项目初始化

// 建立目录
mkdir blog-01 && cd blog-01

// 初始化package.json
npm init -y

// 安装工具lodash,主要是后续操做数据
npm install lodash --save-dev

// 全局安装nodemon,方便咱们修改文件的时候自动重启node
npm install nodemon -g

// 建立bin目录而且在该目录下建立主文件
mkdir bin && cd bin
type nul>www.js
复制代码

主文件:www.js

const http = require('http')
const serverHandle = require('../app.js')
const POST = 3000
const server = http.createServer(serverHandle)
server.listen(POST)
console.log('listen on port ' + POST)
复制代码

cd到根目录下,建立app.js

type nul>app.js
复制代码
const querystring = require('querystring')
const serverHandle = (req, res) => {
  res.setHeader('Content-type', 'application/json')
  res.end('hello')
}
module.exports = serverHandle
复制代码

cd到根目录下,建立对应的文件和文件夹

// 建立src目录,后续原生的代码几乎都放在这里
mkdir src && cd src

// 建立路由目录
mkdir router && cd router

// 分别建立博客路由和用户登陆路由文件
type nul>blog.js
type nul>user.js
复制代码

blog.js

const handleBlogRouter = (req, res) => {
  const method = req.method
  const path = req.url.split('?')[0]
  // 获取博客列表
  if (method === 'GET' && path === '/api/blog/list') {
    return {
      msg: '这是获取博客列表的接口'
    }
  }
  // 获取博客内容
  if (method === 'GET' && path === '/api/blog/detail') {
    return {
      msg: '这是获取博客详情的接口'
    }
  }
  // 新增文章
  if (method === 'POST' && path === '/api/blog/new') {
    return {
      msg: '这是新增文章的接口'
    }
  }
  // 文章更新
  if (method === 'POST' && path === '/api/blog/update') {
    return {
      msg: '这是文章更新的接口'
    }
  }
  // 文章删除
  if (method === 'POST' && path === '/api/blog/del') {
    return {
      msg: '这是文章删除的接口'
    }
  }
}
module.exports = {
  handleBlogRouter
}
复制代码

user.js

const handleUserRouter = (req, res) => {
  const method = req.method
  const path = req.url.split('?')[0]
  // 登陆
  if (method === 'POST' && path === '/api/user/login') {
    return {
      msg: '这是登陆的接口'
    }
  }
}
module.exports = {
  handleUserRouter
}
复制代码

app.js

const { handleBlogRouter } = require('./src/router/blog')
const { handleUserRouter } = require('./src/router/user')
const serverHandle = (req, res) => {
  res.setHeader('Content-type', 'application/json')
  const blogData = handleBlogRouter(req, res)
  if (blogData) {
    res.end(
      JSON.stringify(blogData)
    )
    return
  }
  const userData = handleUserRouter(req, res)
  if (userData) {
    res.end(
      JSON.stringify(userData)
    )
    return
  }
  // 未命中路由
  res.writeHead(404, {"Content-type": "text/plain"})
  res.write("404 Not Found!!\n")
  res.end()
}
module.exports = serverHandle
复制代码

cd到根目录

nodemon bin/www.js
复制代码

创建数据返回模型

cd到src目录下

// 建立模型目录
mkdir model && cd model

// 建立返回结果模型文件
type nul>resModel.js
复制代码

resModel.js

class BaseModel {
  constructor(data, message) {
    // 若是传过来的data是字符串,那就把他设置为message的值,而且将传过来的变量设置为null,使得下面的赋值不执行
    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.error = -1
  }
}
module.exports = {
  SuccessModel,
  ErrorModel
}
复制代码

返回假数据

cd到src目录下

// 建立控制器文件夹,该文件夹下的文件用于根据一些参数返回数据
mkdir controller && cd controller

// 建立命中blog路由时获取数据的函数
type nul>BlogController.js
复制代码

BlogController.js

// 获取文章列表
const getList = (author, keyword) => {
  // 先返回一些死数据,可是格式是对
  return [
    {
      id: 1,
      title: '标题1',
      content: '内容A',
      createTime: 1564136537100,
      author: 'zhangsan'
    },
    {
      id: 2,
      title: '标题2',
      content: '内容B',
      createTime: 1564136647326,
      author: 'lisi'
    }
  ]
}
// 获取文章详情
const getDetail = (id) => {
  if (id) {
    return {
      id: 1,
      title: '标题1',
      content: '内容A',
      createTime: 1564136647326,
      author: 'lisi'
    }
  } else {
    return null
  }
}
module.exports = {
  getList,
  getDetail
}
复制代码

app.js

const querystring = require('querystring')
const { handleBlogRouter } = require('./src/router/blog')
const { handleUserRouter } = require('./src/router/user')
const serverHandle = (req, res) => {
  req.path = req.url.split('?')[0]
  req.query = querystring.parse(req.url.split('?')[1])
  res.setHeader('Content-type', 'application/json')
  const blogData = handleBlogRouter(req, res)
  if (blogData) {
    res.end(
      JSON.stringify(blogData)
    )
    return
  }
  const userData = handleUserRouter(req, res)
  if (userData) {
    res.end(
      JSON.stringify(userData)
    )
    return
  }
  // 未命中路由
  res.writeHead(404, {"Content-type": "text/plain"})
  res.write("404 Not Found!!\n")
  res.end()
}
module.exports = serverHandle
复制代码

blog.js【router】

const { getList, getDetail } = require('../controller/BlogController')
const { SuccessModel, ErrorModel } = require('../model/resModel')
const handleBlogRouter = (req, res) => {
  // 获取博客列表
  if (req.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, '数据获取成功')
  }
  // 获取博客内容
  if (req.method === 'GET' && req.path === '/api/blog/detail') {
    const id = req.query.id || null
    const detailData = getDetail(id)
    if (detailData) {
      return new SuccessModel(detailData, '数据获取成功')
    } else {
      return new ErrorModel('数据获取失败')
    }
  }
  // 新增文章
  if (req.method === 'POST' && req.path === '/api/blog/new') {
    return 
  }
  // 文章更新
  if (req.method === 'POST' && req.path === '/api/blog/update') {
    return {
      msg: '这是文章更新的接口'
    }
  }
  // 文章删除
  if (req.method === 'POST' && req.path === '/api/blog/del') {
    return {
      msg: '这是文章删除的接口'
    }
  }
}
module.exports = {
  handleBlogRouter
}
复制代码

nodejs读取文件

cd到blog-01同级目录,新建一个目录名称为promise-test

// 建立而且进入该测试文件夹
mkdir promise-test && cd promise-test

// 建立入口文件
type nul>index.js

// 建立被读取的文件夹目录和文件
mkdir file && cd file
type nul>a.json
type nul>b.json
type nul>c.json
复制代码

a.json

{
  "path": "b.json",
  "data": "aaa"
}
复制代码

b.json

{
  "path": "c.json",
  "data": "bbb"
}
复制代码

c.json

{
  "path": "d.json",
  "data": "ccc"
}
复制代码

index.js

// fs是node里面一个操做文件的对象
const fs = require('fs')

// path是node里处理路径的对象
const path = require('path')

// 拼接等待读取的文件路径
const fullFileName = path.resolve(__dirname, 'file', 'a.json')

// 调用readFile方法,异步打印出读取的文件流
fs.readFile(fullFileName, (err, data) => {
  if (err) {
    console.error(err)
    return
  }
  console.log(data.toString())
})
复制代码

cd到promise-test目录

node index.js // 读取a.json的内容
复制代码

callback回调函数

修改promise-test下的index.js文件

const fs = require('fs')
const path = require('path')
function getFileContent(fileName, callback) {
  const fullFileName = path.resolve(__dirname, 'file', fileName)
  fs.readFile(fullFileName, (err, data) => {
    if (err) {
      console.error(err)
      return
    }
    callback(
      JSON.parse(data.toString())
    )
  })
}
// 回调地狱
getFileContent('a.json', (aData) => {
  console.log(aData)
  getFileContent(aData.path, (bData) => {
    console.log(bData)
    getFileContent(bData.path, (cData) => {
      console.log(cData)
    })
  })
})
复制代码
node index.js // 经过回调函数,读取各个文件的内容
复制代码

promise代替callback

修改promise-test下的index.js文件

const fs = require('fs')
const path = require('path')
function getFileContent(fileName) {
  return new Promise((resolve, reject) => {
    const fullFileName = path.resolve(__dirname, 'file', fileName)
    fs.readFile(fullFileName, (err, data) => {
      if (err) {
        reject(err)
        return
      }
      resolve(
        JSON.parse(data.toString())
      )
    })
  })
}
getFileContent('a.json').then(aData => {
  console.log(aData)
  return getFileContent(aData.path)
}).then(bData => {
  console.log(bData)
  return getFileContent(bData.path)
}).then(cData => {
  console.log(cData)
})
复制代码
node index.js // 经过return promise,读取各个文件的内容
复制代码

ok,有了上面的promise基础,如今从新回到blog-01处理post方法的接口

promise异步处理post数据

cd到blog-01目录下,修改app.js

const querystring = require('querystring')
const { handleBlogRouter } = require('./src/router/blog')
const { handleUserRouter } = require('./src/router/user')
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(
          JSON.parse(postData)
        )
      } else {
        resolve({})
        return
      }
    })
  })
  return promise
}
const serverHandle = (req, res) => {
  req.path = req.url.split('?')[0]
  req.query = querystring.parse(req.url.split('?')[1])
  res.setHeader('Content-type', 'application/json')
  getPostData(req).then(postData => {
    req.body = postData
    const blogData = handleBlogRouter(req, res)
    if (blogData) {
      res.end(
        JSON.stringify(blogData)
      )
      return
    }
    const userData = handleUserRouter(req, res)
    if (userData) {
      res.end(
        JSON.stringify(userData)
      )
      return
    }
    // 未命中路由
    res.writeHead(404, {"Content-type": "text/plain"})
    res.write("404 Not Found!!\n")
    res.end()
  })
}
module.exports = serverHandle
复制代码

修改router下的blog.js

const { getList, getDetail, newBlog, updateBlog, delBlog } = require('../controller/BlogController')
const { SuccessModel, ErrorModel } = require('../model/resModel')
const handleBlogRouter = (req, res) => {
  // 获取博客列表
  if (req.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, '数据获取成功')
  }
  // 获取博客内容
  if (req.method === 'GET' && req.path === '/api/blog/detail') {
    const id = req.query.id || null
    const detailData = getDetail(id)
    if (detailData) {
      return new SuccessModel(detailData, '数据获取成功')
    } else {
      return new ErrorModel('数据获取失败')
    }
  }
  // 新增文章
  if (req.method === 'POST' && req.path === '/api/blog/new') {
    const newData = newBlog(req.body)
    return new SuccessModel(newData)
  }
  // 文章更新
  if (req.method === 'POST' && req.path === '/api/blog/update') {
    const id = req.query.id
    const updateResult = updateBlog(id, req.body)
    if (updateResult) {
      return new SuccessModel("更新文章成功")
    } else {
      return new ErrorModel("更新文章失败")
    }
  }
  // 文章删除
  if (req.method === 'POST' && req.path === '/api/blog/del') {
    const id = req.query.id
    const delResult = delBlog(id)
    if (delResult) {
      return new SuccessModel()
    } else {
      return new ErrorModel("删除失败")
    }
  }
}
module.exports = {
  handleBlogRouter
}
复制代码

修改controller下的BlogController.js

// 获取文章列表
const getList = (author, keyword) => {
  // 先返回一些死数据,可是格式是对
  return [
    {
      id: 1,
      title: '标题1',
      content: '内容A',
      createTime: 1564136537100,
      author: 'zhangsan'
    },
    {
      id: 2,
      title: '标题2',
      content: '内容B',
      createTime: 1564136647326,
      author: 'lisi'
    }
  ]
}
// 获取文章详情
const getDetail = (id) => {
  if (id) {
    return {
      id: 1,
      title: '标题1',
      content: '内容A',
      createTime: 1564136647326,
      author: 'lisi'
    }
  } else {
    return null
  }
}
// 新增一篇文章
const newBlog = (blogData = {}) => {
  return {
    id: 3,
    blogData
  }
}
// 更新一篇文章
const updateBlog = (id, blogData = {}) => {
  return true
}
// 删除一篇文章
const delBlog = (id) => {
  return true
}
module.exports = {
  getList,
  getDetail,
  newBlog,
  updateBlog,
  delBlog
}
复制代码

登陆路由的建立

cd到src下的controller下

type nul>UserController.js
复制代码

UserController.js

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

router下的user.js

const { login } = require('../controller/UserController')
const { SuccessModel, ErrorModel } = require('../model/resModel')
const handleUserRouter = (req, res) => {
  // 登陆
  if (req.method === 'POST' && req.path === '/api/user/login') {
    const { username, password } = req.body
    const loginResult = login(username, password)
    if (loginResult) {
      return new SuccessModel("登陆成功")
    } else {
      return new ErrorModel("登陆失败")
    }
  }
}
module.exports = {
  handleUserRouter
}
复制代码

路由和假数据这块基本就已经完成了

总结:postman使用来进行接口测试,路由和数据分开,新建数据模型用于格式化数据,promise异步处理数据

mysql介绍与安装

web server中最流行的关系型数据库

官网能够免费下载

轻量级,易学易用

mysql下载地址:dev.mysql.com/downloads/m…

固然也能够经过下载集成有数据库环境的phpstudy来获取数据库环境

数据库图形工具workbench下载:dev.mysql.com/downloads/w…

数据库操做

在workbench中,schema就是库的意思

建库:

// 建立数据库myblog
CREATE SCHEMA `myblog`

// 删除数据库myblog
DROP SCHEMA `myblog`

// 使用数据库myblog
use myblog
复制代码

建表:user表和blog表

CREATE TABLE myblog.users (
  `id` INT NOT NULL AUTO_INCREMENT,
  `username` VARCHAR(20) NOT NULL,
  `password` VARCHAR(20) NOT NULL,
  `realname` VARCHAR(10) NOT NULL,
  PRIMARY KEY (id));
复制代码

CREATE TABLE `myblog`.`blogs` (
  `id` INT NOT NULL AUTO_INCREMENT,
  `title` VARCHAR(50) NOT NULL,
  `content` LONGTEXT NOT NULL,
  `createtime` BIGINT(20) NOT NULL DEFAULT 0,
  `author` VARCHAR(20) NOT NULL,
  PRIMARY KEY (`id`));
复制代码

操做表

增删改查

// 使用数据库,操做以前务必选中要使用的库
use myblog;

// 列出该数据库中的全部表
show tables;

// 注释
--show tables;

// 插入语句
insert into users(`username`, `password`, `realname`) values('zhangsan','123','张三');
insert into users(`username`, `password`, `realname`) values('lisi','123','李四');
insert into blogs(`title`, `content`, `createtime`, `author`) values('标题1','内容1','1564452004248', 'zhangsan');
insert into blogs(`title`, `content`, `createtime`, `author`) values('标题2','内容2','1564452013359', 'lisi');

// 查询users表全部的行和列
select * from users

// 查询某个列的数据
select username,password from users

// 查询符合某个条件的行(查询交集)
select * from users where `username`='zhangsan' and `password`='123';

// 查询符合某个条件的行(查询并集)
select * from users where `username`='zhangsan' or `password`='123';

// 模糊查询
select * from users where username like '%zhang%';

// 排序(desc倒叙,asc升序)
select * from users where username like '%zhang%' order by id desc

// 更新
update users set `realname`="李四" where `username`='lisi';

// 接触更新限制
SET SQL_SAFE_UPDATES=0

// 删除数据
delete from users where username='lisi';

// 通常建议新建一个字段,state设置0或1 来作伪删除。
ALTER TABLE `myblog`.`blogs` 
ADD COLUMN `state` INT NOT NULL DEFAULT 1 AFTER `author`;

// 设置state的状态来标记数据是否显示
update blogs set `state` = 0 where `username` = 'lisi'

// 注意:varchar(10)只能存储10个汉字,10个字母
复制代码

nodejs操mysql

新建demo进行演示,封装为系统可用的工具,让api直接操做数据库,再也不使用假数据

demo
mkdir node-mysql-test && cd node-mysql-test

type nul>index.js

npm init -y

npm install mysql

dir
复制代码

进入index.js文件

// 引入mysql
const mysql = require('mysql')
// 建立链接对象
const conn = mysql.createConnection({
  host: 'localhost',
  user: 'root',
  password: 'root',
  port: '3306',
  database: 'myblog'
})
// 链接对象进行链接
conn.connect()
// 定义sql语句
const sql = 'select * from blogs'
// 执行sql语句
conn.query(sql, (err, result) => {
  if (err) {
    console.log(err)
  } else {
    console.log(result)
  }
})
// 关闭链接
conn.end()
复制代码
从数据库读取项目数据

cd到blog-01

cd blog-01

// 安装mysql
npm install mysql --save-dev

// 安装cross-env
npm install cross-env --save-dev

// 进入package.json,在 scripts下添加
    "dev": "cross-env NODE_ENV=dev nodemon ./bin/www.js",
    "prd": "cross-env NODE_ENV=production nodemon ./bin/www.js"

// cd到src下
cd src

// 建立conf文件夹
mkdir conf && cd conf

// 建立db.js,用于数据库配置
type nul>db.js
复制代码

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: 'myblog2'
  }
}
module.exports = {
  MYSQL_CONF
}
复制代码

cd到src目录

mkdir db && cd db

// 建立mysql链接文件,用于链接数据库
type nul>mysql.js
复制代码

mysql.js

const mysql = require('mysql')
const { MYSQL_CONF } = require('../conf/db')
const conn = mysql.createConnection(MYSQL_CONF)
conn.connect()
function exec(sql) {
  return new Promise((resolve, reject) => {
    conn.query(sql, (err, result) => {
      if (err) {
        reject(err)
        return
      } else {
        resolve(result)
      }
    })
  })
}
module.exports = {
  exec
}
复制代码

BlogController.js

const { exec } = require('../db/mysql')
// 获取文章列表
const getList = (author, keyword) => {
  let sql = `select * from blogs where 1 = 1`
  sql += author ? ` and author = '${author}'` : ''
  sql += keyword ? ` and title like '%${keyword}%'` : ''
  sql += ` order by createtime desc;`
  return exec(sql)
}
// 获取文章内容
const getDetail = (id) => {
  let sql = `select * from blogs where id = ${id}`
  return exec(sql).then(rows => {
    return rows[0]
  })
}
// 新增一篇文章
const newBlog = (blogData = {}) => {
  const { title=null, content=null, author=null } = blogData
  const createTime = Date.now()
  let sql = `insert into blogs(title, content, createtime, author) values('${title}','${content}',${createTime}, '${author}')`;
  return exec(sql).then(result => {
    return {
      insertId: result.insertId
    }
  })
}

// 更新一篇文章
const updateBlog = (id, blogData = {}) => {
  const { title, content } = blogData
  let sql = `update blogs set id = ${id}`
  sql += title ? `, title = '${title}'` : ''
  sql += content ? `, content = '${content}'` : ''
  sql += ` where id = ${id}`
  if (id !== null) {
    return exec(sql).then(result => {
      return {
        affectedRows: result.affectedRows
      }
    })
  } else {
    return new Promise((resolve, reject) => {
      resolve(false)
    })
  }
}
// 删除一篇文章
const delBlog = (id, author) => {
  let sql = `delete from blogs where id = ${id} and author = '${author}'`
  if (id) {
    return exec(sql).then(result => {
      return {
        affectedRows: result.affectedRows
      }
    })
  } else {
    return new Promise((resolve, reject) => {
      resolve(false)
    })
  }
}
module.exports = {
  getList,
  getDetail,
  newBlog,
  updateBlog,
  delBlog
}
复制代码

router下blog.js

const { getList, getDetail, newBlog, updateBlog, delBlog } = require('../controller/BlogController')
const { SuccessModel, ErrorModel } = require('../model/resModel')
const handleBlogRouter = (req, res) => {
  // 获取博客列表
  if (req.method === 'GET' && req.path === '/api/blog/list') {
    const author = req.query.author || ''
    const keyword = req.query.keyword || ''
    return getList(author, keyword).then(listData => {
      return new SuccessModel(listData, '数据获取成功')
    })
  }
  // 获取博客内容
  if (req.method === 'GET' && req.path === '/api/blog/detail') {
    const id = req.query.id || null
    if (id === null) {
      return new Promise((resolve) => {
        resolve(false)
      }).then(() => new ErrorModel("缺乏文章id"))
    } else {
      return getDetail(id).then(detailData => {
        return new SuccessModel(detailData, '数据获取成功')
      })
    }
  }
  // 新增文章
  if (req.method === 'POST' && req.path === '/api/blog/new') {
    req.body.author = 'wangwu'
    return newBlog(req.body).then(blogId => {
      return new SuccessModel(blogId)
    })
  }
  // 文章更新
  if (req.method === 'POST' && req.path === '/api/blog/update') {
    const id = req.query.id || null
    const updateResult = updateBlog(id, req.body)
    return updateResult.then(result => {
      if (result) {
        return new SuccessModel(result, "更新文章成功")
      } else {
        return new ErrorModel("更新失败,没有指定文章id")
      }
    })
  }
  // 文章删除
  if (req.method === 'POST' && req.path === '/api/blog/del') {
    // 假数据,为了防止被其余用户经过id误删文章因此要获取当前登陆用户的用户名
    const author = 'lisi'
    const id = req.query.id
    return delBlog(id, author).then(result => {
      if (!result) {
        return new ErrorModel("没有指定删除的文章id")
      }
      if (result.affectedRows === 0) {
        return new SuccessModel("删除0条数据")
      } else {
        return new SuccessModel(result)
      }
    })
  }
}
module.exports = {
  handleBlogRouter
}
复制代码

UserController.js

const { exec } = require('../db/mysql')
const login = (username, password) => {
  let sql = `select count(*) as suitRows,username, realname from users where username='${username}' and password = '${password}'`
  return exec(sql).then(res => {
    return res[0]
  })
}
module.exports = {
  login
}
复制代码

router下的user.js

const { login } = require('../controller/UserController')
const { SuccessModel, ErrorModel } = require('../model/resModel')
const handleUserRouter = (req, res) => {
  // 登陆
  if (req.method === 'POST' && req.path === '/api/user/login') {
    const { username, password } = req.body
    return login(username, password).then(row => {
      if (row.suitRows) {
        return new SuccessModel(row.realname + "登陆成功")
      } else {
        return new ErrorModel("登陆失败")
      }
    })
  }
}
module.exports = {
  handleUserRouter
}
复制代码

app.js

const querystring = require('querystring')
const { handleBlogRouter } = require('./src/router/blog')
const { handleUserRouter } = require('./src/router/user')
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(
          JSON.parse(postData)
        )
      } else {
        resolve({})
        return
      }
    })
  })
  return promise
}
const serverHandle = (req, res) => {
  req.path = req.url.split('?')[0]
  req.query = querystring.parse(req.url.split('?')[1])
  res.setHeader('Content-type', 'application/json')
  getPostData(req).then(postData => {
    req.body = postData
    const blogData = handleBlogRouter(req, res)
    if (blogData) {
      blogData.then(result => {
        if (result) {
          res.end(
            JSON.stringify(result)
          )
        }
      })
      return
    }

    const userData = handleUserRouter(req, res)
    if (userData) {
      userData.then(result => {
        res.end(
          JSON.stringify(result)
        )
      })
      return
    }
    // 未命中路由
    res.writeHead(404, {"Content-type": "text/plain"})
    res.write("404 Not Found!!\n")
    res.end()
  })
}
module.exports = serverHandle
复制代码
总结

nodejs链接mysql,执行sql语句

根据NODE_ENV 区分mysql配置

封装 exec函数,API使用exec函数操做数据库

登陆

核心是登陆校验和登陆信息存储

cookie和session

session写入 redis

开发登陆功能,前端联调(使用 nginx反向代理)

cookie

什么是cookie,JavaScript操做cookie,浏览器中查看cookie

server端操做cookie,实现登陆验证

什么是cookie

存储在浏览器中的一段字符串(最大5kb)

跨域不共享

格式如:k1=v1;k2=v2;k3=v3;所以能够存储结构化数据

每次发送http请求,会将请求域的cookie数据一块发送给server端,好比我在百度请求淘宝的文件,那么我会将淘宝的cookie数据发送给server端

server能够修改cookie而且返回给浏览器

浏览器也能够经过JavaScript修改cookie(有限制)

查看cookie的方式有不少

一、能够经过控制台的Application/Storage/Cookies下查看

二、也能够经过在控制台的Network里面点击请求的URL,而后查看Request Headers里面的Cookie

三、还能够直接在控制台输入document.cookie查看当前域下的cookie

document.cookie
复制代码

四、增长cookie,cookie是累加的模式,会自动在cookie后面接上【注意一次添加一个cookie】

// 客户端修改cookie,用的很少
document.cookie = "key1=100;"
复制代码
server端操做cookie

查看cookie,下面是片断

// 查看cookie
req.headers.cookie

// cookie提取,转化
req.cookie = {}
const cookieStr = req.headers.cookie || ''
cookieStr.split(";").forEach(element => {
  const arr = element.split("=");
  const key = arr[0].trim()
  const val = arr[1].trim()
  req.cookie[key] = val
})
复制代码

cd到src目录,修改app.js,添加上面的部分代码,完成cookie的解析

const querystring = require('querystring')
const { handleBlogRouter } = require('./src/router/blog')
const { handleUserRouter } = require('./src/router/user')
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(
          JSON.parse(postData)
        )
      } else {
        resolve({})
        return
      }
    })
  })
  return promise
}
const serverHandle = (req, res) => {
  req.path = req.url.split('?')[0]
  req.query = querystring.parse(req.url.split('?')[1])
  req.cookie = {}
  const cookieStr = req.headers.cookie || ''
  cookieStr.split(";").forEach(element => {
    if (!element) {
      return
    }
    const arr = element.split("=");
    const key = arr[0].trim()
    const val = arr[1].trim()
    req.cookie[key] = val
  })
  res.setHeader('Content-type', 'application/json')
  getPostData(req).then(postData => {
    req.body = postData
    const blogData = handleBlogRouter(req, res)
    if (blogData) {
      blogData.then(result => {
        if (result) {
          res.end(
            JSON.stringify(result)
          )
        }
      })
      return
    }
    const userData = handleUserRouter(req, res)
    if (userData) {
      userData.then(result => {
        res.end(
          JSON.stringify(result)
        )
      })
      return
    }
    // 未命中路由
    res.writeHead(404, {"Content-type": "text/plain"})
    res.write("404 Not Found!!\n")
    res.end()
  })
}
module.exports = serverHandle
复制代码

为了方便直接测试登陆,如今想将登陆换成get请求,这样能够在地址栏直接输入username和password进行登陆

修改user.js

const { login } = require('../controller/UserController')
const { SuccessModel, ErrorModel } = require('../model/resModel')
const handleUserRouter = (req, res) => {
  // 登陆
  // if (req.method === 'POST' && req.path === '/api/user/login') {
  //   const { username, password } = req.body
  //   return login(username, password).then(row => {
  //     if (row.suitRows) {
  //       return new SuccessModel(row.realname + "登陆成功")
  //     } else {
  //       return new ErrorModel("登陆失败")
  //     }
  //   })
  // }
  // GET请求,用于测试登陆后 写入cookie
  if (req.method === 'GET' && req.path === '/api/user/login') {
    const username = req.query.username
    const password = req.query.password
    return login(username, password).then(row => {
      if (row.suitRows) {
        res.setHeader('Set-Cookie', `username=${row.username}; path=/`)
        return new SuccessModel(row.realname + "登陆成功")
      } else {
        return new ErrorModel("登陆失败")
      }
    })
  }

  if (req.method === 'GET' && req.path === '/api/user/login-test') {
    if (req.cookie.username) {
      return Promise.resolve(new SuccessModel("已经登录,登陆用户名为" + req.cookie.username))
    } else {
      return Promise.resolve(new ErrorModel("未登陆"))
    }
  }
}
module.exports = {
  handleUserRouter
}
复制代码

上面经过获取地址栏的username和password 去数据库对比,匹配到的话,使用

res.setHeader('Set-Cookie', `username=${row.username}; path=/`)
复制代码

设置了cookie,可是这样不安全,非法用户仍是能够经过客户端修改cookie,好比

// 在控制台清除掉已经设置的cookie

// 再修改成其余的cookie
document.cookie="username=lisi"
复制代码

因此咱们须要在设置cookie的时候加一点东西httpOnly,以下

res.setHeader('Set-Cookie', `username=${row.username}; path=/; httpOnly;`)

复制代码

最后,cookie通常都最好设置一个过时时间,用来规定这个cookie多久失效

res.setHeader('Set-Cookie', `username=${row.username}; path=/; httpOnly; expires=${getCookieExpires()}`)

const getCookieExpires = (days=1) => {
  const d = new Date()
  d.setTime(d.getTime() + (days * 24 * 60 * 60 * 1000))
  return d.toGMTString()
}
复制代码

因此如今完整的user.js文件是这样的

const { login } = require('../controller/UserController')
const { SuccessModel, ErrorModel } = require('../model/resModel')
const getCookieExpires = (days=1) => {
  const d = new Date()
  d.setTime(d.getTime() + (days * 24 * 60 * 60 * 1000))
  return d.toGMTString()
}
const handleUserRouter = (req, res) => {
  // 登陆
  // if (req.method === 'POST' && req.path === '/api/user/login') {
  //   const { username, password } = req.body
  //   return login(username, password).then(row => {
  //     if (row.suitRows) {
  //       return new SuccessModel(row.realname + "登陆成功")
  //     } else {
  //       return new ErrorModel("登陆失败")
  //     }
  //   })
  // }
  // GET请求,用于测试登陆后 写入cookie
  if (req.method === 'GET' && req.path === '/api/user/login') {
    const username = req.query.username
    const password = req.query.password
    return login(username, password).then(row => {
      if (row.suitRows) {
        res.setHeader('Set-Cookie', `username=${row.username}; path=/; httpOnly; expires=${getCookieExpires(2)};`)
        return new SuccessModel(row.realname + "登陆成功")
      } else {
        return new ErrorModel("登陆失败")
      }
    })
  }

  if (req.method === 'GET' && req.path === '/api/user/login-test') {
    if (req.cookie.username) {
      return Promise.resolve(new SuccessModel("已经登录,登陆用户名为" + req.cookie.username))
    } else {
      return Promise.resolve(new ErrorModel("未登陆"))
    }
  }
}
module.exports = {
  handleUserRouter
}
复制代码

session

cookie会暴露username,很危险

如何解决:cookie中存储userid,server端利用这个userid对应的查出登陆后的一些用户信息

server端比较安全,并且空间大一些。cookie只有5kb的存储空间

解决方案:session存储用户信息

思路:定义一个全局的对象SESSION_DATA = {}; 每次调用接口的时候,判断cookie是否有有userid这个key,若是没有,随机生成一个userid而后保存到cookie中。若是有了的话,SESSION_DATA[userId] = {}, req.session = SESSION_DATA[userid],登陆成功以后,将登陆信息设置到req.session中,如req.session.username = rows[0].username

app.js

const querystring = require('querystring')
const { handleBlogRouter } = require('./src/router/blog')
const { handleUserRouter } = require('./src/router/user')
// session数据
const SESSION_DATA = {}
// 设置cookie的过时时间
const getCookieExpires = (days=1) => {
  const d = new Date()
  d.setTime(d.getTime() + (days * 24 * 60 * 60 * 1000))
  return d.toGMTString()
}
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(
          JSON.parse(postData)
        )
      } else {
        resolve({})
        return
      }
    })
  })
  return promise
}
const serverHandle = (req, res) => {
  req.path = req.url.split('?')[0]
  req.query = querystring.parse(req.url.split('?')[1])
  req.cookie = {}
  const cookieStr = req.headers.cookie || ''
  cookieStr.split(";").forEach(element => {
    if (!element) {
      return
    }
    const arr = element.split("=");
    const key = arr[0].trim()
    const val = arr[1].trim()
    req.cookie[key] = val
  })
  // 标志,规定是否须要设置cookie
  let needSetCookie = false
  // 解析cookie后,提取里面的userid出来保存起来
  let userId = req.cookie.userid
  if (!userId) {
    // 若是userid不存在,那么规定等下须要设置cookie,而且把要设定的cookie的值随机生成好
    needSetCookie = true
    userId = `${Date.now()}_${Math.random()}`
    SESSION_DATA[userId] = {}
  } else {
    // 若是有userid这个cookie,那么
    if (!SESSION_DATA[userId]) {
      SESSION_DATA[userId] = {}
    }
  }
  req.session = SESSION_DATA[userId]

  res.setHeader('Content-type', 'application/json')
  getPostData(req).then(postData => {
    req.body = postData
    const blogData = handleBlogRouter(req, res)
    if (blogData) {
      blogData.then(result => {
      	// 请求返回的时候,判断是否须要设置cookie
        if (needSetCookie) {
          res.setHeader('Set-Cookie', `userid=${userId}; path=/; httpOnly; expires=${getCookieExpires()}`)
        }
        if (result) {
          res.end(
            JSON.stringify(result)
          )
        }
      })
      return
    }
    const userData = handleUserRouter(req, res)
    if (userData) {
      userData.then(result => {
     	// 请求返回的时候,判断是否须要设置cookie
        if (needSetCookie) {
          res.setHeader('Set-Cookie', `userid=${userId}; path=/; httpOnly; expires=${getCookieExpires()}`)
        }
        res.end(
          JSON.stringify(result)
        )
      })
      return
    }
    // 未命中路由
    res.writeHead(404, {"Content-type": "text/plain"})
    res.write("404 Not Found!!\n")
    res.end()
  })
}
module.exports = serverHandle
复制代码

user.js

const { login } = require('../controller/UserController')
const { SuccessModel, ErrorModel } = require('../model/resModel')
const handleUserRouter = (req, res) => {
  // GET请求,用于测试登陆后 写入cookie
  if (req.method === 'GET' && req.path === '/api/user/login') {
    const username = req.query.username
    const password = req.query.password
    return login(username, password).then(row => {
      if (row.suitRows) {
      	// 若是帐号密码匹配成功,那么将用户信息保存到对应cookie里userid的req.session中
        req.session.username = row.username
        req.session.realname = row.realname
        return new SuccessModel(row.realname + "登陆成功")
      } else {
        return new ErrorModel("登陆失败")
      }
    })
  }

  if (req.method === 'GET' && req.path === '/api/user/login-test') {
    if (req.session.username) {
      return Promise.resolve(new SuccessModel("已经登录,真名为" + req.session.realname))
    } else {
      return Promise.resolve(new ErrorModel("未登陆"))
    }
  }
}
module.exports = {
  handleUserRouter
}
复制代码

可是!!! session的问题也是存在的

session直接是js变量,放在nodejs进程内存中,可是进程内存有限,访问量过大,内存暴涨确定不行;还有正式上线以后,运行的是多进程,进程之间内存没法共享

解决方案: redis

redis是web server最经常使用的缓存数据库,数据存放在内存中,优势是读取快,缺点是内存昂贵有限,断电数据丢失。相比于mysql,redis更快更强,毕竟硬盘速度方面干不过内存。

在现实的开发中,通常把redis和mysql结合使用,互相搭配,干活不累

为何session和redis那么适合在一块儿?

session访问频繁,对性能要求极高,而redis很合适。

session能够不考虑断电丢失数据的问题

session的数据不会特别大, 通常在内存的控制范围

为何网站数据不适合用redis

由于操做频率不是很高

断电后数据不可以丢失!!!

数据量比较大,内存成本过高

redis

安装redis

mac:

brew install redis
复制代码

windows:www.runoob.com/redis/redis…

下载完redis压缩包以后,解压,而后将文件夹改名为redis,放入c盘(最好去环境变量添加一下)。而后在命令行进入到该目录下执行命令

redis-server.exe
复制代码

这样的话redis服务端就已经启动了

这时候不要关闭原来的服务端窗口,另外打开一个cmd窗口,进入到redis目录下,执行命令。

redis-cli.exe -h 127.0.0.1 -p 6379

复制代码

redis是利用set和get和del来操做键值对的。因此能够利用

// 在redis里存储key1值为val1
set key1 val1

// 在redis里获取key1的值
get key1

set key2 val2

// 删除key1
del key1

列出全部key
keys *
复制代码
redis-test
mkdir redis-test && cd redis-test

npm init -y

type nul>index.js

npm install redis --save-dev
复制代码

redis-test下的index.js

// 引入安装好的redis
const redis = require('redis')
// 建立redis客户端
const redisClient = redis.createClient(6379, '127.0.0.1')
// 监听错误
redisClient.on('error', err => {
  console.error(err)
})
// set方法设置值,redis.print参数表示要打印出设置的状态
redisClient.set("myname", "zhangsan", redis.print)
// get方法读取值,是一个异步过程,后续须要封装进promise里面
redisClient.get("myname", (err, val) => {
  if (err) {
    console.log(err)
    return
  }
  console.log('val:' + val)
  // 退出
  redisClient.quit()
})
复制代码

还能够存储对象形式的,改进以后的index.js

const redis = require('redis')
const redisClient = redis.createClient(6379, '127.0.0.1')
redisClient.on('error', err => {
  console.error(err)
})
const session = {
  username: 'lzx',
  realname: '小罗罗'
}
const sessionStr = JSON.stringify(session)
redisClient.set("mysession", sessionStr, redis.print)
redisClient.get("mysession", (err, val) => {
  if (err) {
    console.log(err)
    return
  }
  console.log('username:' + JSON.parse(val).username)
  console.log('realname:' + JSON.parse(val).realname)
  redisClient.quit()
})
复制代码
node index.js
复制代码
应用到项目中

cd到blog-01

npm install redis --save-dev
复制代码

进入src/conf下的db.js,咱们配置一下redis的配置参数

const env = process.env.NODE_ENV
let MYSQL_CONF
// 定义redis配置变量
let REDIS_CONF
if (env === 'dev') {
  MYSQL_CONF = {
    host: 'localhost',
    user: 'root',
    password: 'root',
    port: '3306',
    database: 'myblog'
  }
  // 开发环境下的端口和主机
  REDIS_CONF = {
    port: 6379,
    host: '127.0.0.1'
  }
}
if (env === 'production') {
  MYSQL_CONF = {
    host: 'localhost',
    user: 'root',
    password: 'root',
    port: '3306',
    database: 'myblog'
  }
  // 生产环境下的端口和主机(暂不改动)
  REDIS_CONF = {
    port: 6379,
    host: '127.0.0.1'
  }
}
module.exports = {
  MYSQL_CONF,
  // 导出
  REDIS_CONF
}
复制代码

cd到src/db目录

type nul>redis.js
复制代码

redis.js

const redis = require('redis')
const { REDIS_CONF } = require('../conf/db')
const redisClient = redis.createClient(REDIS_CONF.port, REDIS_CONF.host)
redisClient.on('error', (err) => {
  console.log(err)
})
function set(key, val) {
  if (typeof val === 'object') {
    val = JSON.stringify(val)
  }
  redisClient.set(key, val, redis.print)
}
function get(key) {
  const promise = new Promise((resolve, reject) => {
    redisClient.get(key, (err, val) => {
      if (err) {
        reject(err)
        return
      }
      if (val === null) {
        resolve(null)
        return
      }
      try {
        resolve(
          JSON.parse(val)
        )
      } catch (error) {
        resolve(val)
      }
    })
  })
  return promise
}
module.exports = {
  set,
  get
}
复制代码

app.js

const querystring = require('querystring')
// 引入redis的设置和读取方法
const { set, get } = require('./src/db/redis')
const { handleBlogRouter } = require('./src/router/blog')
const { handleUserRouter } = require('./src/router/user')
const getCookieExpires = (days=1) => {
  const d = new Date()
  d.setTime(d.getTime() + (days * 24 * 60 * 60 * 1000))
  return d.toGMTString()
}
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(
          JSON.parse(postData)
        )
      } else {
        resolve({})
        return
      }
    })
  })
  return promise
}
const serverHandle = (req, res) => {
  res.setHeader('Content-type', 'application/json')
  req.path = req.url.split('?')[0]
  req.query = querystring.parse(req.url.split('?')[1])
  req.cookie = {}
  const cookieStr = req.headers.cookie || ''
  cookieStr.split(";").forEach(element => {
    if (!element) {
      return
    }
    const arr = element.split("=");
    const key = arr[0].trim()
    const val = arr[1].trim()
    req.cookie[key] = val
  })
  let needSetCookie = false
  let userId = req.cookie.userid
  if (!userId) {
    // 若是没有userid这个cookie
    needSetCookie = true
    userId = `${Date.now()}_${Math.random()}`
    // 若是没有名为userid的cookie,那么将空对象赋值给一个随机值key,而后写入redis库
    set(userId, {})
  }
  req.sessionId = userId
  // 从redis中读取key为req.cookie.userid对应的值或者刚产生的随机值userId做为key所对应的值
  get(req.sessionId).then(sessionData => {
    if (sessionData === null) {
      req.session = {}
      set(req.sessionId, {})
    } else {
      // 若是值不为空,那么设置到session中,方便后续login-test读取 
      req.session = sessionData
    }
    // 当req.session赋值完成时,开始获取post的数据,该方法是返回一个promise
    return getPostData(req)
  })
  .then(postData => {
    req.body = postData
    const blogData = handleBlogRouter(req, res)
    if (blogData) {
      blogData.then(result => {
        // 若是当前没有cookie,那么设置一下
        if (needSetCookie) {
          res.setHeader('Set-Cookie', `userid=${userId}; path=/; httpOnly; expires=${getCookieExpires()}`)
        }
        if (result) {
          res.end(
            JSON.stringify(result)
          )
        }
      })
      return
    }
    const userData = handleUserRouter(req, res)
    if (userData) {
      userData.then(result => {
        if (needSetCookie) {
          res.setHeader('Set-Cookie', `userid=${userId}; path=/; httpOnly; expires=${getCookieExpires()}`)
        }
        res.end(
          JSON.stringify(result)
        )
      })
      return
    }
    // 未命中路由
    res.writeHead(404, {"Content-type": "text/plain"})
    res.write("404 Not Found!!\n")
    res.end()
  })
}
module.exports = serverHandle
复制代码

user.js

const { login } = require('../controller/UserController')
// 引入写入redis的方法
const { set } = require('../db/redis')
const { SuccessModel, ErrorModel } = require('../model/resModel')
const handleUserRouter = (req, res) => {
  // GET请求,用于测试登陆后 写入cookie
  if (req.method === 'GET' && req.path === '/api/user/login') {
    const username = req.query.username
    const password = req.query.password
    return login(username, password).then(row => {
      if (row.suitRows) {
        req.session.username = row.username
        req.session.realname = row.realname
        // 将req.session设置到 redis中保存起来(同步到redis)
        set(req.sessionId, req.session)
        return new SuccessModel(row.realname + "登陆成功")
      } else {
        return new ErrorModel("登陆失败")
      }
    })
  }
  if (req.method === 'GET' && req.path === '/api/user/login-test') {
    if (req.session.username) {
      return Promise.resolve(new SuccessModel("已经登录,真名为" + req.session.realname))
    } else {
      return Promise.resolve(new ErrorModel("未登陆"))
    }
  }
}
module.exports = {
  handleUserRouter
}

复制代码

在login-test中,经过req.session.username来判断是否已经登陆。若是删掉了cookie,从新渲染时,那么req.sessionId会发生改变,而后登陆信息req.session也会被初始化为空对象。那么就至关于退出了登陆状态。

进入某些页面前的校验

由于如今已经能够经过req.session.username的真假来判断是否已经登陆,好比在进入新建博客页面,修改博客页面,删除博客页面以前,都要校验一下是否已经登陆,若是没登陆, 应该返回什么信息而且拒绝接下来的操做。这个项目主要是blog路由的校验,因此咱们能够定义个函数来处理这部分的校验

// 统一的登陆验证函数
const loginCheck = (req) => {
  if (!req.session.username) {
    return Promise.resolve(
      new ErrorModel('还没有登陆')
    )
  }
}
复制代码

修改一下blog.js

const { getList, getDetail, newBlog, updateBlog, delBlog } = require('../controller/BlogController')
const { SuccessModel, ErrorModel } = require('../model/resModel')
// 登陆校验函数
const loginCheck = (req) => {
  if (!req.session.username) {
    return Promise.resolve(
      new ErrorModel('还没有登陆')
    )
  }
}
const handleBlogRouter = (req, res) => {
  // 获取博客列表
  if (req.method === 'GET' && req.path === '/api/blog/list') {
    const author = req.query.author || ''
    const keyword = req.query.keyword || ''
    return getList(author, keyword).then(listData => {
      return new SuccessModel(listData, '数据获取成功')
    })
  }
  // 获取博客内容
  if (req.method === 'GET' && req.path === '/api/blog/detail') {
    const id = req.query.id || null
    if (id === null) {
      return new Promise((resolve) => {
        resolve(false)
      }).then(() => new ErrorModel("缺乏文章id"))
    } else {
      return getDetail(id).then(detailData => {
        return new SuccessModel(detailData, '数据获取成功')
      })
    }
  }
  // 新增文章
  if (req.method === 'POST' && req.path === '/api/blog/new') {
    const loginCheckResult = loginCheck(req)
    if (!!loginCheckResult) {
      // 未登陆
      return loginCheck(req)
    }
    req.body.author = req.session.username
    return newBlog(req.body).then(blogId => {
      return new SuccessModel(blogId)
    })
  }
  // 文章更新
  if (req.method === 'POST' && req.path === '/api/blog/update') {
    const loginCheckResult = loginCheck(req)
    if (!!loginCheckResult) {
      // 未登陆
      return loginCheck(req)
    }
    const id = req.query.id || null
    const updateResult = updateBlog(id, req.body)
    return updateResult.then(result => {
      if (result) {
        return new SuccessModel(result, "更新文章成功")
      } else {
        return new ErrorModel("更新失败,没有指定文章id")
      }
    })
  }
  // 文章删除
  if (req.method === 'POST' && req.path === '/api/blog/del') {
    const loginCheckResult = loginCheck(req)
    if (!!loginCheckResult) {
      // 未登陆
      return loginCheck(req)
    }
    // 假数据,为了防止被其余用户经过id误删文章因此要获取当前登陆用户的用户名
    const author = req.session.username
    const id = req.query.id
    return delBlog(id, author).then(result => {
      if (!result) {
        return new ErrorModel("没有指定删除的文章id")
      }
      if (result.affectedRows === 0) {
        return new SuccessModel("删除0条数据")
      } else {
        return new SuccessModel(result)
      }
    })
  }
}
module.exports = {
  handleBlogRouter
}
复制代码

修改一下user.js,将login-test去掉,而且把login为GET改回POST

const { login } = require('../controller/UserController')
const { set } = require('../db/redis')
const { SuccessModel, ErrorModel } = require('../model/resModel')
const handleUserRouter = (req, res) => {
  // 登陆
  if (req.method === 'POST' && req.path === '/api/user/login') {
    const { username, password } = req.body
    return login(username, password).then(row => {
      if (row.suitRows) {
        req.session.username = row.username
        req.session.realname = row.realname
        set(req.sessionId, req.session)
        return new SuccessModel(row.realname + "登陆成功")
      } else {
        return new ErrorModel("登陆失败")
      }
    })
  }
}
module.exports = {
  handleUserRouter
}
复制代码

至此,接口已经完成了。接下来就是与前端页面的联调了

和前端联调

由于cookie跨域不共享,因此前端和server端必需要同域

须要用nignx作代理,让先后端同域

启动服务器

在blog-01的所在目录下

复制代码

安装http-server

npm install http-server -g

http-server -p 8001
复制代码

把你的静态页面index.html放到http-server中,就能够打开http://192.168.1.50:8001/index.html进行访问了

nginx介绍

高性能web服务器,开源免费

通常用于作静态服务,负载均衡(暂时用不到)

反向代理:遇到/...就代理到html的服务器上,而遇到/api/...就代理到node.js服务器

nginx下载

mac: brew install nginx

windows: nginx.org/en/download…

下载最新版本的或者稳定版本的

下载完成以后解压里面的内容到c盘的nginx目录下,添加一下环境变量

而后操做的时候cd到nginx目录操做

nginx配置

window: c:\nginx\conf\nginx.conf

mac:/usr/local/etc/nginx/nginx.conf

nginx命令

mac下

测试配置文件格式是否正确 nginx -t

启动:nginx; 重启:nginx -s reload;中止: nginx -s stop

windows下,须要cd到nginx目录下

启动:start nginx

测试:nginx -t

正式操做

mac

sudo vi /usr/local/etc/nginx/nginx.conf
复制代码

windows直接用管理员权限打开文件nginx/nginx.conf记事本

第三行:

// 用双核,不写默认是单核
worker-processes 2;
复制代码

找到server{},监听这个端口

listen 8080;
复制代码

找到location,用#号注释,而后本身写代理

// 若是是/... 代理到http://localhost:8000
location / {
    proxy_pass http://localhost:8001;
}
// 若是是/api/... 代理到http://localhost:3000
location /api/ {
    proxy_pass http://localhost:3000;
    proxy_set_header Host $host;
}
复制代码

测试:

nginx -t
// 返回is ok 和 is successful 就代表没有问题

// 语法没问题就直接启动,mac下直接使用nginx执行
nginx

// -------------windows
// 测试
nginx -t
// 启动
start nginx
// 重载
nginx -s reload
// 中止
nginx -s stop
复制代码
相关文章
相关标签/搜索