使用node开发博客系统

479 阅读7分钟

安装

官网下载安装 node官网:nodejs.cn/download/

介绍

  • Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境。
  • Node.js 使用了一个事件驱动、非阻塞式 I/O 的模型。Node 是一个让 JavaScript 运行在服务端的开发平台
  • 需要遵守ECMANScript语法

server开发和前端开发的区别

  • 服务器稳定性
  • 考虑内存和cpu
  • 日志记录
  • 安全性

nodejs基础语法介绍

http

http.createServer([options][, requestListener])

fs(文件系统)

fs.readFile读取文件


fs.readFile('文件', (err, data) => {
  if (err) throw err;
  console.log(data);
});

path(路径)

path.join()

使用平台特定的分隔符作为定界符将所有给定的 path 片段连接在一起,然后规范化生成的路径。零长度的 path 片段会被忽略。 如果连接的路径字符串是零长度的字符串,则返回 '.',表示当前工作目录。

path.resolve()

将路径或路径片段的序列解析为绝对路径。

path.resolve('/foo/bar', './baz');
// 返回: '/foo/bar/baz'

path.resolve('/foo/bar', '/tmp/file/');
// 返回: '/tmp/file'

path.resolve('wwwroot', 'static_files/png/', '../gif/image.gif');
// 如果当前工作目录是 /home/myself/node,
// 则返回 '/home/myself/node/wwwroot/static_files/gif/image.gif'

stream

Writable - 可写入数据的流(例如 fs.createWriteStream())
Readable - 可读取数据的流(例如 fs.createReadStream()

全局对象process

process 对象是一个全局变量,它提供有关当前 Node.js 进程的信息并对其进行控制。 作为一个全局变量,它始终可供 Node.js 应用程序使用,无需使用 require()。

流对象

process.stdin
process.stdout
process.stderr

博客需求分析

开发一个博客系统,用户可登录,查看所有博客列表,对自己的博客内容增删改查。项目采用前后端分离方式开发。server端和前端

博客系统

  • 登录功能
  • 博客列表
  • 当前用户博客列表
  • 新增博客、编辑博客、删除博客

技术分析

用户信息表 | user

id account password realname
1 alis 123 爱丽丝
2 bob 123 鲍勃

博客表 | blog

id title content text author userid createtime updatetime
1 title content text 爱丽丝 1 1587977866756 1587977866234
2 title content text 鲍勃 2 15879778667343 1587977866764

接口设计

描述 接口 方法 参数
用户登录 /api/user/login POST username、password
用户登出 /api/user/logout POST
描述 接口 方法 参数
获取博客列表 /api/blog/list GET author、keyword
获取博客详情 /api/blog/detail GET id
新增博客 /api/blog/new POST title、content 、text
更新博客 /api/blog/update POST id、title、content 、text
删除博客 /api/blog/delete POST id

数据存储

使用mysql数据库进行数据存储

mysql安装

安装地址:dev.mysql.com/downloads/m…

安装的过程中需要输入root密码。需记住密码。

安装可视化工具navicat/workbench安装

navicat安装地址:xclient.pipipan.com/fs/13114864…
解压密码:xclient.info
安装地址:dev.mysql.com/downloads/w…

安装完成之后,建库,建表,表操作。

MySQL的数据类型

主要包括以下五大类:

整数类型:BIT、BOOL、TINY INT、SMALL INT、MEDIUM INT、 INT、 BIG INT
浮点数类型:FLOAT、DOUBLE、DECIMAL
字符串类型:CHAR、VARCHAR、TINY TEXT、TEXT、MEDIUM TEXT、LONGTEXT、TINY BLOB、BLOB、MEDIUM BLOB、LONG BLOB
日期类型:Date、DateTime、TimeStamp、Time、Year
其他数据类型:BINARY、VARBINARY、ENUM、SET、Geometry、Point、MultiPoint、LineString、MultiLineString、Polygon、GeometryCollection等

数据类型的属性

MySQL关键字 含义
NULL 数据列可包含NULL值
NOT NULL 数据列不允许包含NULL值
DEFAULT 默认值
PRIMARY KEY 主键
AUTO_INCREMENT 自动递增,适用于整数类型
UNSIGNED 无符号
CHARACTER SET name 指定一个字符集

sql基本语句

show databases;  
create schema blogs;  
use blogs;  
-- show tables;  
create table `user` (
`id`  int not null auto_increment,
`username` varchar(25) not null,
`realname` varchar(25) not null,
`password` varchar(20) not null,
primary key (`id`)
)

insert into user (username,`password`,realname) values ('alis','123','爱丽丝')
insert into user (username,`password`,realname) values ('bob','123','鲍勃')
select * from user
select * from user where username like 'alis' order by id desc
update user set username='amanda' where id=1

delete from user where id=5

create table `blog` (
`id`  int not null auto_increment,
`uid` varchar(20) not null,
`author` varchar(20) not null,
`title` varchar(50) not null,
`content` longtext not null,
`text` longtext not null,
`createtime` varchar(20) not null,
`updatetime` varchar(20),
primary key (`id`)
)

insert into blog (uid,author,title,content,text,createtime) values (1,'amanda','title','content','text',1588915947198)

set SQL_SAFE_UPDATES=0;// 安全模式开关

如何连接数据库

npm i -S mysql
const mysql = require('mysql')

// 创建链接对象
const con = mysql.createConnection({
    host: 'localhost',
    user: 'root',
    password: '*****',
    port: '3306',
    database: 'blogs'
})

// 开始连接
con.connect()

// 执行 sql 语句
const sql = `select * from user`
con.query(sql, (err, result) => {
    if (err) {
        console.error(err)
        return
    }
    console.log(result)
})

// 关闭连接
con.end()

cookie&&session

在cookie中存储uid,redis存储对应的userinfo

cookie

存储在浏览器的一段字符串(最大5kb),跨域不共享,每次发送http请求,讲请求域的cookie一起发送给server端,server可以修改cookie并且返回给浏览器,浏览器也可以通过javascript修改cookie

session

session存储userinfo,多进程无法共享。内存有限,访问量过大会有问题。

  • 访问频繁,对性能要求高
  • 可以不需要考虑断电丢失的问题
  • 数据量不会很大

redis

特点:

  • 缓存数据库
  • 数据存储在内存中
  • 相比mysql访问速度更快(内存和硬盘的区别)
  • 成本高,存储数据量小

redis安装

windows:www.runoob.com/redis/redis…
mac: brew install redis

安装完成之后运行

redis-server
redis-cli

redis常用操作命令

key *
set
get 
del

如何连接redis

npm i -S redis
const redis = require('redis')

// 创建客户端
const redisClient = redis.createClient(6379, '127.0.0.1')
redisClient.on('error', err => {
    console.error(err)
})

// 测试
redisClient.set('myname', 'alis', redis.print)
redisClient.get('myname', (err, val) => {
    if (err) {
        console.error(err)
        return
    }
    console.log('val ', val)

    // 退出
    redisClient.quit()
})

rendis-server启动后可拿到端口信息

数据库连接配置

Node.js ORM 框架 sequelize

安全

sql注入

使用mysql的escape解决sql注入问题

比如用户在登录的时候恶意输入用户名为alis --,导致拼接sql之后后面的语句不执行,直接登录

use blogs;
select * from user 
where username='alis';delete from blog;
const author = escape(userData.title)
    const password = escape(userData.password)
    const sql = `
        select * from user
        where username=${author} password=${password};
    `

xss攻击

比如用户在新建博客的时候,输入,如果按照原样存储则会窃取到用户的隐私cookie

解决办法,使用xss插件 拼接sql的时候,先把参数通过xss处理。
如xss(title) 将如<>(尖括号)、”(引号)、 ‘(单引号)、%(百分比符号)、;(分号)、()(括号)、&(& 符号)、+(加号)等进行转义。

密码加密

crypto 模块提供了加密功能
npm i -S crypto

const crypto = require('crypto')

// 密匙
const SECRET_KEY = '*********'

// md5 加密
function md5(content) {
    let md5 = crypto.createHash('md5')
    return md5.update(content).digest('hex')
}

// 加密函数
function genPassword(password) {
    const str = `password=${password}&key=${SECRET_KEY}`
    return md5(str)
}

module.exports = {
    genPassword
}

接口开发

使用express-generator脚手架开发,express是nodeJs开发最常用的webserver 框架

全局安装脚手架

npm i -g express-generator
express express-test
npm i 
npm start

脚手架初始化后内容介绍

public/views //可忽略是用来写前端代码的,前后端不分离开发使用
bin/www //启动node服务

routers //路由
app.js //注册中间件

app.js代码分析

var createError = require('http-errors'); //处理异常,如404
var express = require('express');// cookie处理,注册后,可以通过req.cookies访问cookie
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');// 日志插件
 
var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');

var app = express(); //通过express初始化app

app.use(logger('dev'));// 注册日志
app.use(express.json());// 处理postdata


app.use(express.urlencoded({ extended: false })); //表单数据处理
app.use(cookieParser());

app.use('/', indexRouter);//注册路由
app.use('/users', usersRouter);

// catch 404 and forward to error handler
app.use(function(req, res, next) {
  next(createError(404));
});

// error handler
app.use(function(err, req, res, next) {
  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = req.app.get('env') === 'development' ? err : {};

  // render the error page
  res.status(err.status || 500);
  res.render('error');
});

module.exports = app;

处理post旧的处理办法

    let postData = ''
    req.on('data', chunk => {
      postData += chunk.toString()
    })
    req.on('end', () => {
      if (postData) {
        resolve(JSON.parse(postData))
        return
      }
      resolve({})
    })

morgan的日志配置可参考网址:github.com/expressjs/m…
dev::method :url :status :response-time ms - :res[content-length]
combined::remote-addr - :remote-user [:date[clf]] ":method :url HTTP/:http-version" :status :res[content-length] ":referrer" ":user-agent"

crontab定时任务配置

crontab -e //进行定时任务编辑部署
* * * * * sh path//crontab命令格式 分 时 日 月 周 
* 0 * * * sh /Users/wangxin/Documents/share/nodejs/blog-express/logs

nodemon和pm2

测试环境使用nodemon正式环境使用pm2启动项目

pm2.conf.json

{
    "apps": {
        "name": "pm2-server",// 项目名  
        "script": "./bin/www",// 执行文件
        "watch": true,// 是否监听文件变动然后重启
        "ignore_watch": [
            "node_modules",
            "logs"
        ],
        "instances": 4,// 应用启动实例个数
        "error_file": "logs/err.log",// 错误日志文件
        "out_file": "logs/out.log",// 日志文件
        "log_date_format": "YYYY-MM-DD HH:mm:ss"// 指定日志文件的时间格式
    }
}