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
複製代碼
相關文章
相關標籤/搜索