Node.js是一个基于"Chrome V8"JS解析引擎的JS运行环境。官网
浏览器是JS的前端运行环境;Node.js是JS的后端运行环境。
内置API是由运行环境提供的特殊接口,只能在所属运行环境中被调用。
// 导入fs模块
// require()方法加载模块时,会执行被加载模块中的代码
const fs = require('fs')
// 读取指定文件[path存放路径;option编码格式(一般'utf8')]
fs.readFile(path, [option], function(err, dataStr) {
// 失败的结果
console.log(err)
// 成功的内容
console.log(dataStr)
})
// 写入指定文件[path存放路径;data写入的内容;option编码格式(一般'utf8')]
fs.writeFile(path, data, [option], function (err) {
if (err) {
return console.log('文件写入失败!' + err.message)
}
console.log('文件写入成功!')
})
路径path使用相对路径容易出现“路径动态拼接错误”的问题,代码会以执行node命令的目录为当前目录。提供完整路径可以解决此问题。
// 导入path模块
const path = require('path')
// 拼接路径[__dirname表示当前文件所处的目录]
path.join(path1, path2, ...)
// 获取路径中的文件名(ext可以获得去除扩展名的文件名)
const fpath = '/a/b/c/index.html'
path.basename(fpath,[ext])
// 获取路径中的扩展名
path.extname(fpath)
1)服务器基础知识
2)http模块
// 导入http模块
const http = require('http')
// 创建web服务器实例
const server = http.createServer()
// 为服务器绑定request事件监听客户端请求
server.on('request', (req, res)=>{
// req.url 客户端请求的URL地址
// req.method 客户端请求的method类型
// res.end 向客户端响应的内容
// 设置Content-Type响应头,解决中文乱码的问题
res.setHeader('Content-Type','text/html; charset=utf-8')
})
// 启动web服务器
server.listen(80,()=>{
console.log('server running at http://127.0.0.1')
})
1)模块化概念
解决复杂问题时,自顶向下逐层把系统划分成若干模块的过程。把一个大文件拆成独立并相互依赖的多个小模块,模块是可组合、分解和更换的单元。提高了代码的复用性、可维护性,可以实现按需加载。
2)模块化规范
对代码进行模块化的拆分和组合时需要遵循的规则。降低沟通成本,极大方便了各个模块之间的相互调用。Node.js遵循CommonJS模版规范:
3)Node.js中模块分类
4)模块作用域
在自定义模块中定义的变量、方法等成员,只能在当前模块内被访问。防止了全局变量污染问题,通过module对象向外共享模块作用域中的成员,外界使用require方法导入自定义模块时得到modules.exports所指向的对象。Node提供exports对象与modules.exports指向同一个对象。
1)包
包是由第三方基于内置模块开发出来,提供更高级、方便的API,免费供所有人使用,极大的提高了开发效率。
2)npm
npm(Node Package Manager)包管理工具可以从上述服务器把需要的包下载到本地使用。npm随着Node.js自动安装。
3)npm常用命令
// 在执行命令目录中快速创建package.json文件
npm init -y
// 安装指定包[可以指定版本:大版本.功能版本.修复版本]
npm install/i 包名@版本号
// 安装全局包
npm install/i 包名 -g
// 一次性安装dependencies节点所有依赖包
npm install/i
// 安装指定包并记录在devDependencies节点
// 【开发依赖包】只在开发阶段用到,上线阶段不会用到的记录在devDependencies节点
// 【核心依赖包】开发阶段和上线阶段都用到的记录在dependencies节点
npm install/i 包名 -D
// 卸载指定包
npm uninstall 包名
// 监听项目文件,实现自动重启项目
nodemon 文件名
// 切换镜像服务器加快下载速度【不方便】
npm config get registry
npm config set registry=https://registry.npm.taobao.org/
// 通过nrm包切换镜像服务器【方便】
npm i nrm -g
nrm ls
nrm use taobao
4)模块加载机制
1)express基本用法
express是基于Node.js平台,基于内置http模块进一步封装的专门用来创建Web服务器的Web开发框架。能够极大的提高开发服务器的效率。官网
// 创建Web网站服务器
const express = require('express')
const app = express()
// 监听GET请求
app.get('url',(req, res)=>{
// 向客户端响应JSON对象
res.send({name:'zs',age:20})
// 向客户端响应文本字符串
res.send('ok')
// req.query获取客户端发送的查询参数,默认为空对象
console.log(req.query)
// req.params获取动态匹配的URL参数,默认为空对象
console.log(req.params)
})
// 监听POST请求
app.post('url',(req, res)=>{
// req.body获取解析后的请求体数据,默认为空对象
console.log(req.body)
})
// 启动服务器
app.listen(端口号,()=>{})
// 创建静态资源服务器
// 对外提供upload目录下的内容,挂载路径前缀/upload
app.use('/upload', express.static('upload'))
2)express路由
在express中,路由指客户端的请求与服务端处理函数之间的映射关系。由请求类型、请求URL地址、处理函数三部分组成。
// 把路由挂载到服务器上
app.get('/', (req, res)=>{
res.send('Get Request')
})
app.post('/', (req, res)=>{
res.send('Post Request')
})
// 模块化管理路由:路由写在单独的router.js文件,通过module.exports向外共享路由对象
const router = require('./router')
app.use('/api', router) // 可以添加访问前缀
3)express中间件
中间件(Middleware)指业务流程的中间处理环节。当请求到达express服务器后,可以连续调用多个中间件进行预处理,本质是function处理函数。
const mw = function(req, res, next){
// 多个中间件之间共享一份req和res,按先后顺序依次调用
// next() 实现多个中间件连续调用,把流转关系交给下一个中间件/路由
next()
}
// 全局中间件
app.use((req, res, next)=>{
next()
})
// 局部中间件:mw1、mw2为已定义的中间件
app.get('/',[mw1, mw2], (req, res)=>{})
1—应用级别的中间件:绑定在服务器实例app上的中间件
2—路由级别的中间件:绑定在路由实例router上的中间件
3—错误级别的中间件:捕捉整个项目中发生的异常错误,防止项目异常崩溃
app.use((err, req, res, next)=>{
res.send(err.message)
})
4—Express内置的中间件:
// 1. 托管静态资源-例如HTML文件、CSS文件、图片...
app.use(express.static('upload'))
// 2. 解析JSON格式的请求体数据,4.16.0版本以上可以
app.use(express.json())
// 3. 解析URL-encoded格式请求体数据,4.16.0版本以上可以
app.use(express.urlencoded({extended:false}))
5—第三方提供的中间件:
// 例如:body-parser 解析URL-encoded格式请求体数据
app.use(parser.urlencoded({extended:false}))
4)express接口
编写接口步骤如下:
CORS(Cross-Origin Resource Sharing,跨域资源请求)是express的第三方中间件,可以方便的解决跨域请求问题。由一系列的HTTP响应头组成,这些响应头决定浏览器是否阻止跨域获得资源【浏览器的同源安全策略默认阻止跨域】
CORS只在服务器端进行配置,客户端浏览器无需进行任何额外配置。
CORS有兼容性问题,只有支持XMLHttpRequest Level2的浏览器支持。
1)CORS的基本使用
// 安装cors中间件
npm install cors
// 导入cors中间件
const cors = require('cors')
// 全局配置cors中间件[要在路由前调用]
app.use(cors())
2)CORS响应头部
// 指定允许访问该资源的外域URL
res.setHeader('Access-Control-Allow-Origin','https://www.baidu.com/')
// 对额外的请求头进行声明[默认仅支持9个:Accept\Accept-Language\Content-Language\DPR\Downlink\Save-Data\Viewport-Width\Width\Content-Type]
res.setHeader('Access-Control-Allow-Headers','...')
// 指定允许的HTTP请求方法
res.setHeader('Access-Control-Allow-Methods','POST,GET,DELETE,HRAD')
3)CORS请求分类:根据请求方法和请求头的不同进行划分
1)数据库
数据库(database)是用来组织、存储和管理数据的仓库。
2)SQL语句
SQL(Structured Query Language,结构化查询语言),是用来访问和处理数据库的编程语言。在关系型数据库中使用。
// 查
select * from users;
select username, password from users;
// 增
insert into users(username, password) values ('tony','123456');
// 改
update users set password='000000',status=1 where id=5;
// 删
delete from users where id=5;
操作符 | 描述 |
---|---|
= | 等于 |
<> 或 != | 不等于 |
> 、<、>=、<= | 大于、小于、大于等于、小于等于 |
BETWEEN | 在某个范围内 |
LIKE | 模糊搜索 |
AND | 同时满足多个条件 |
OR | 满足任意一个条件 |
// 【默认,ASC可省略】升序ASC
select * from users order by id;
// 降序DESC
select * from users order by id DESC;
select * from users order by id DESC, username ASC;
// as 可以指定列名
select count(*) as total from users where status=0;
3)mysql模块
// 1. 安装第三方mysql模块
// 2. 连接到mysql数据库
const mysql = require('mysql')
const db = mysql.createPool({
host:'127.0.0.1'
user:'root'
password:'111111'
database:'mydata'
})
// 3. 执行sql语句操作数据库
// ? 作为占位符
const sql = 'select * from users where id=?'
db.query(sql,user.id,(res,results)=>{
if(err) return console.log(err.message)
console.log(results)
})
// set ? 传递参数的简便方式
const sql = 'update users set ? where id=?'
db.query(sql,[user,user.id],(err,results)=>{
if(err) return console.log(err.message)
if(results.affectedRows === 1){
console.log('更新数据成功')
}
})
// 为保险期间,删除数据时通过“标记删除”来模拟删除:设置修改status状态字段标记为删除
目前主流的Web开发模式有两种:
1)基于服务器渲染的传统Web开发模式:服务器发送给客户端通过字符串拼接动态生成的HTML页面,客户端不需要额外请求页面数据。
2)基于前后端分离的新型Web开发模式:依赖于Ajax技术的广泛应用,后端只负责提供API接口,前端使用Ajax调用接口。
身份认证(Authentication)是通过一定的手段完成对用户身份的确认。不同的Web开发模式有不同的身份认证方式:
Session工作原理:在服务器端生成Cookie 保存在服务器,响应给客户端。客户端再次请求时,通过请求头自动将当前域名下的所有Cookie发送给服务器,服务器通过Cookie查找对应的用户信息进行身份认证。【Cookie是存储在用户浏览器中的一段字符串,由名称、值和用于控制有效期、安全性、使用范围的可选属性组成】
// 1. 安装express-session中间件
// 2. 配置express-session中间件
app.use(session({
secret: 'cat',
resave: false,
saveUninitialized: true
}))
// 3. 登录成功后向session中存数据
req.session.user = req.body // 用户信息
req.session.islogin = true // 用户登录状态
// 4. 从session中取数据
if (!req.session islogin) {
return res.send({ status:1 ,msg:'fail' })
}
res.send({ status:0 ,msg:'success',username:req.session.user.username })
// 5. 清空当前用户的session信息
req.session.destroy()
Session认证需要配合Cookie,但Cookie不支持跨域访问,需要额外的配置。需要跨域请求的可以使用JWT认证机制。
JWT(JSON Web Token)是目前最流行的跨域认证解决方案,用户信息通过加密后生成Token字符串保存在客户端浏览器上,服务端通过还原Token字符串的形式认证用户身份。
JWT类型的Token由三部分组成:Header(头部).Payload(有效荷载).Signature(签名),其中,Payload是用户信息经过加密后生成的字符串;Header和Signature是安全性相关信息。
// 1. 安装JWT相关的包:jsonwebtoken和express-jwt
// 2. 导入JWT相关的包
const jwt = require('jsonwebtoken')
const expressJWT = require('express-jwt')
// 3. 定义secret密钥:保证JWT的安全性,用于加密和解密
const secretKey = 'im sillyxue'
// 4. 登录成功后生成JWT字符串:不要把密码加密到Token
const tokenStr = jwt.sign({ username: userinfo.username }, secretKey, { expiresIn: '30s' })
// 5. 将客户端通过请求头Authorization发送的Token解析成JSON对象
app.use(expressJWT({ secret: secretKey }).unless({ path: [/^\/api\//] }))
// 当解析成功后,可以使用req.user获取解析到的用户信息
console.log(req.user)
// 6. 捕获解析失败错误:Token字符串过期/不合法会导致解析失败 UnauthorizedError
// 写在错误级别的中间件中
if (err.name === 'UnauthorizedError') {
return res.send({
status: 401,
message: '无效的token',
})
}
为了保证密码的安全性,不建议在数据库以明文的形式保存用户密码,推荐对密码进行加密存储。可以使用bcryptjs对用户密码进行加密:加密后的密码无法被逆向破解;同一明文密码多次加密的结果不同,保证了安全性。
// 导入bcryptjs
const bcrypt = require('bcryptjs')
// 对密码加密
userinfo.password = bcrypt.hashSync(明文密码,随机盐的长度)
// 判断密码是否一致[false表示密码错误]
const compareResult = bcrypt.compareSync(用户提交的密码,数据库中存储的密码)
单纯使用if验证数据合法性效率低下、出错率高、维护性差。使用第三方数据验证模块,可以降低出错率,提高验证的效率和可维护性。
1)非formdata格式请求体
使用@hapi/joi中间件定义验证规则:
const joi = require('@hapi/joi')
// 定义验证规则
const username = joi.string().alphanum().min(1).max(10).required()
const password = joi.string().pattern(/^[\S]{6,12}$/).required()
// 向外暴露
exports.reg_login_schema = {
body:{username, password}
}
使用@escook/express-joi中间件自动对表单数据验证:
const expressJoi = require('@escook/express-joi')
// 导入验证对象
const {reg_login_schema} = require('../schema/user')
// 声明局部中间件进行数据验证
router.post('/reguser',expressJoi(reg_login_schema),userHandler.regUser)
在错误级别的中间件中间件中捕获验证错误:
if (err instanceof joi.ValidationError) res.send(err)
2)formdata格式请求体
以上中间件无法解析multipart/form-data格式的请求体数据,可以使用multer中间件。
const multer = require('multer')
// 创建multer实例对象,通过dest属性置顶文件的存放路径
const upload = multer({dest: path.join(__dirname,'../uploads')})
// 声明局部中间件解析formdata格式的表单数据
// 文本类型的挂载到req.body 文件类型的挂载到req.file
router.post('/add',upload.single('cover_img'),article_handler.addArticle)