nodejs - 基础知识
知识前景
1.浏览器中的JS组成部分
-
js的核心语法 (变量类型,循环,判断,函数,作用等等)
-
webApi(BOM, DOM,基于XMLHttpRequrest的ajax等等 )
为什么JavaScript为什么可以在浏览器中执行?
执行的js代码被JavaScript的解析引擎解析执行
2.不同浏览器的解析引擎?
-
chorme => v8
-
firefox => OdinMonkey(奥丁猴)
-
Safri => jsCore
-
IE => Chakra(查克拉)
3.为什么JavaScript可以操作BOM,DOM?
每个浏览器都内置了BOM,DOM的Api函数
4.浏览器中JavaScript运行环境
v8引擎 + 内置api
-
内置的api是由运行环境提供的特殊接口
-
v8引擎负责解析和执行javaScript接口
一.基础简介
1.什么是nodejs
Node.js是基于chorme的v8引擎的js运行环境
2.nodejs的js运行环境
v8引擎+内置api(fs, path, http, js内置对象,querystring等等)
3.nodejs可以做什么?
基于node提供的基础功能,可以让前端程序员做更多工作岗位
-
基于express框架,快速构建web应用
-
基于electron,构建跨平台的桌面应用
-
基于restify,可以快速构建api应用
-
对文件进行读写
等等
4.Node.js环境的安装
官网下载,傻瓜式安装
//查看版本号
node -v
执行代码
进入对应文件夹(cd 文件夹)
(windows,进入对应文件夹地址栏cmd)
(mac 进入终端 cd 把文件拖入)
node 文件夹名
node index.js
常用命令
-
tab键补全
-
上箭头 上一次执行命令
-
mac(clear清空), window(cls清空)
二.内置模块介绍
1.fs
1.1定义
fs模块是nodejs官方提供的用来操作文件的模块
1.2使用
引入,使用
const fs = require('fs')
1.3相关知识点
(1)fs.readFile(path[, options], callback)
读取文件
-
参数
path: 必选, 读取文件的路径
options: 可选, 以什么编码格式读取文件
callback: 必选, 文件读取成功,回调函数读取成功后的内容
-
例子
const fs = require('fs')
fs.readFile('./file/1.txt', 'utf8', function(err, dataStr){
console.log(err)
console.log(dataStr)
if(err) {
//失败,err是错误对象,dataStr是undefined
return console.log(err, 'err')
}
//成功, err是null, datastr成功读取文件对象
conso.log(dataStr)
})
(2)fs.writeFile(file, data[,options], callback)
写入文件
-
参数
file: 必选参数,指定一个文件路径的字符串,表示文件的存放地址(只能创建文件,不能创建路径)
data:必选,表示写入的内容
options:选填, 表示以什么编码格式写入
callbak: 必选, 回调函数
-
例子
const fs = require('fs')
//向2.txt写入aaa
fs.writeFile('./file/2.txt', 'aaaaa', function(err){
//写入成功,err - null
//写入失败, err是错误对象
if(err) {
return console.log(err, 'err')
}
})
(3) fs路径动态拼接问题
fs操作文件,相对路径容易出现拼接错误
代码运行会执行node命令所在的目录。动态拼接被操作文件的完整路径
-
例子
c:\users\escook\desk node index.js
执行(c:\users\escook\desk\inde.js )
解决问题 - 提供完成的绝对路径
-
__dirname(当前文件所处的目录)
__dirname + './1/txt'
2.path
2.1定义
path是nodejs官方提供用于处理路径的模块,它提供了一系列的方法和属性用来满足对路径的处理需求
2.1使用
引入使用
const path = require('path')
2.3相关知识点
(1)path.join()
将多个路径片段拼接成一个完整的路径字符串
参数可以是多个路径
const path = require('path')
const testPath = path.join('/a', '/b/c', '../d', 'e')
//testPath: '\a\b\d\e'(../会抵消前一层路径)
const testPath2 = path.join(__dirname, './1.txt')
//testPath2: 当前文件目录所在位置\1.txt
(2)path.basename(path[,后缀名])
当前文件所在目录的文件名
-
path: 必选,读取文件的路径
-
[.文件后缀名]:可选, 文件的后缀名
例子
const str = '/a/b/c/index.html'
const test = path.basename(str)
//test: index.html
const test2 = path.basename(str, '.html')
//test2: index
(3)path.extname(path)
获取路径中文件的扩展名
const str = '/a/b/c/index.html'
const text = path.extname(str)
//test: .html
3.http
3.1定义
在网络节点中,负责消费资源的电脑叫做客户端,负责对外提供网络资源的电脑叫做服务器
http模块是nodejs官方提供,用于创建web服务器的模块
3.2使用
const http = require('http')
3.3相关知识点
服务器和普通电脑的区别在于,服务器安装了web服务器软件,例如iis,apache等,通过安装这些服务器软件,就能把一台普通电脑编程一台web服务器
在nodejs中不需要第三方服务器,因为nodejs提供的http模块可以手写服务器软件
(1) ip地址
ip地址就是互联网上的每一台计算机的唯一标识。
IP地址的格式: 点分十进制(a,b,c,d),其中abcd都是0-255之间的十进制整数。例如(192.168.1.1)
(2)域名与域名服务器
ip地址不便于记忆,出现了域名,域名和ip地址是一一对应关系,这份关系存放在一种叫做域名服务器(DNS)的电脑中
ping www.baidu.com
//可以看到真实IP
注意:
-
本机的启动的服务器ip是127.0.0.1对应的域名是localhost
(3) 端口号
(4)创建服务器
//引入http
const http = require('http')
//创建实例
const server = http.createServer()
//参数一: 监听的事件名称,参数二: 监听后的回调
server.on('request', (req, res) => {
//req 与客户端相关的对象和属性 请求对象,里面有method, url, headers等等
//res 服务器端相关的对象和属性
console.log(req, 'req')
console.log(res, 'res')
//设置请求头,避免中文乱码,告诉客户端用utf-8来解析
res.setHeader('Content-Type', 'text/html', 'charset=uft-8')
//res.end 向客服端响应内容并结束这次请求
res.end('xxxx')
})
//监听端口,进行启动
server.listen(80, () => {
console.log('启动了服务器:80')
})
三.模块化
1模块化的基本概念
1.1 什么是模块化
模块化:模块化就是遵守固定的规则,把一个大文件拆分成独立并互相依赖的多个小模块
好处
-
提高代码的复用性
-
提高代码的可维护性
-
实现按需加载
1.2模块化规范
模块化规范就是对代码进行模块化的拆分和组合时,需要遵守哪些规则
1.3 模块的分类和引入
在nodejs中模块根据来源不同进行分类:
-
内置模块 - nodejs官方提供的模块例如fs,path,http等
-
自定义模块 - 用户创建的js文件
-
第三方模块 - 第三方库提供的模块
引入
//内置模块
cosnt fs = require('fs')
//自定模块
const my = require('./mine')
//第三方模块
const time = require('moment')
注意:require会执行加载模块代码
1.4 模块作用域和Module对象
模块作用域: 在自定义模块中的变量,方法等成员,只能在当前模块内部使用
Module对象:
Module对象里面存放了模块的一些属性
1.5 module.exports和exports
module.exports对象: 在自定义模块用,可以使用module.exports将模块内的成员贡献出去,供外界使用,reuqire接收的就是module.exports对象暴露的对象{}
//mine.js
module.exports.name = 'xx'
module.exports.sayHello = function() {}
//index.js
cosnt mine = reuqire('./mine.js')
//mine = {name: 'xxx', sayHello: function(){}}
exports对象:由于module.exports写起来更复杂,为了简化,node提供了exports对象,默认exports和module.exports指向同一个对象。最终共享结果,以module.exports为准
例如:
//例1
exports.name = 'a'
module.exports = {name: 'a'}
// 一开始两个指向同一地址,后面exports改变了指向地址,最终暴露的是{name: 'a'}
//例2
exports.a = {name: 'a'}
module.exports.age = 'a'
//一开始只指向同一地址,后面再添加,{a:{name: a}, age: a}
//例如3
module.exports.name = '23'
exports = {name: 'a'}
//一开始都name=23,exports变更,最终暴露的是module.exports => name = '23'
nodejs中的模块化规划是遵循commonjs的模块化规范
commjs规定
-
每个模块内部,module的变量代表当前模块
-
module变量是一个对象,他的exprots属性是对外的接口
-
加载某个模块,其实是加载该模块的module.exports属性.require()方法相当于加载模块
四.包
1.包
什么是包?
包就是nodejs中的第三方模块,只是叫法不同
包的来源?
包是由第三方或者团队开发出来的,免费供所有人使用。
为什么需要包?
nodejs内置模块提供了一些底层的api.导致内置模块进行项目开发时,效率很低。
包是基于内置模块封装出来的,提供了更高级,更方便的api.极大的提高了开发效率
包和内置模块之间的关系,类似于jQuery和浏览器内置api之间的关系
从哪里下载包?
国外有一家it公司,叫做npm lnc这家公司有一个网站https://www.nomjs.com/,他是全球最大的包共享平台。npm lnc提供了网址为htttps://registry.npmjs.com的服务器对下载自己的包
如何下载包
npm lnc提供了一个包管理工具,npm -v可以查看版本号
五 nodemon
5.1 什么是nodemon
nodemon是会监听改动自动重启,node xx文件,文件变更需要手动重启,使用nodemon就可以不在手动重启
5.2 nodemon的使用
下载
npm i -g nodemon
使用
nodemon xxx.js
六 express
1.express的基本介绍
1.1什么是express
Express是基于nodejs,快速,开发,极简的web开发框架(express的作用和node内置模块http类似,专门用于创建web服务器)
express的本质就是npm的一个三方包,提供快读创建web服务器的便捷方法
1.2 express能做什么
对于前端程序员来说,最常见的两种服务器
-
web网站服务器:专门对外提供web网页资源的服务器
-
api接口服务器:专门对外提供api接口的服务器
使用express就可以快速创建这种两种服务器
1.3如何快速创建服务器
下载安装 - npm i express
创建使用
//引入
const express = require('express')
//创建web服务器
cosnt app = express()
//监听服务器启动
app.listen(80, () => {
consloe.log('express server runing at http://127.0.0.1')
})
1.4 监听get,post请求
const express = require('express')
cosnt app = express()
//监听get请求
//req:请求对象,包含了与请求相关的属性和方法
//RES:响应对象,包含了与响应相关的属性与方法
app.get('请求url', (req,res) => {
//send方法响应给客户端
//req.query - 获取的请求地址栏的查询参数 (urL :/xx) /xx?a=1&b=2 => req.query: {a:1, b:2}
//req.params - 获取url中动态参数的值 - (url: /xx:a:b)/xx:1:2 => req.pamras: {a:1, b:2}
res.send(req.query)
})
//监听post请求
app.post('请求url', (req, res) => {res.send()})
app.listen(80, () => {
consloe.log('express server runing at http://127.0.0.1')
})
1.5 托管静态资源
1.5 .1express.static()
用法:
app.use(express.static('public'))
使用这个后,启动服务可以直接访问public下面的所有的静态资源文件,路径不包含public
例如: http://localhost:3000/images/bg.jpg
使用托管,托管多个静态资源目录
app.use(express.static('public'))
app.use(express.static('files'))
访问静态资源文件,express.static函数会根据目录的添加顺序查找所需文件
1.5.2 挂载路径前缀
app.use('/public', express.static('./public'))
当前public文件下的所有资源,都需要添加前缀public来进行访问
七 路由
7.1 路由的相关用法
对于请求url,如果全部都挂载在app上,会不方便管理,因此就单独模块化,挂载路由
例子:
创建单独的router.js
const express = require('express')
//创建router
const router = express.Router()
//路由管理
router.get('/getname', (req,res) => {})
roter.post('/a', (req, res) => {})
module.exports = router
引入router
const express = require('express')
const app = express()
const useRouter = require('./router/index.js')
//app.use - 注册路由模块
app.use(useRouter)
//添加统一的路由前缀
//app.use('/api', useRouter)
app.listen(80, () => {})
八 中间件
8.1 什么是中间件
express中间件的调用流程,当请求到达express之后,会连续调用多个中间件,从而对这次进行预处理
express的本质就是一个function的处理函数,他的格式如下
app.get('/', function(req,res,next) => {})
中间件函数的参数列表中,必须包含next参数,而路由处理函数中只需要包含req 和res
8.2定义中间件
定义中间件就是定义一个函数
全局中间件 - 客户端发起的任何请求,到达服务器之后,都会触发的中间件就是全局中间件 ,app.use(中间件函数)定义全局生效的中间件(有点像路由守卫)
const express = require('express')
const app = express()
const mw = funciton(req,res,next) {
//next是转交给下一个中间件
next()
}
//如果是定义全局中间件
//app.use(mw)
app.listen(80, () => {})
8.3 中间件的作用
多个中间件之间,共享一份req和res,基于这样的特性,可以在上游的中间件中,统一给req或res对象添加自定义的属性和方法,供下游的中间件或路由进行使用
例如:
const express = require('express')
const app = express()
//定义为全局中间件
app.use(funciton(req, res, next) {
req.startTime = new Date()
next()
})
app.get('/', (req, res) {
res.send(req.startTime)
})
app.listen(80, () => {})
8.4 定义多个全局中间件
app.use()连续定义多个全局中间件,客户端的请求到达服务器之后,会按照中间件定义的先后顺序依次进行调用
app.use(function(req, res, next) {
console.log('调用了第一个全局中间件')
next()
})
app.use(function(req, res, next) {
console.log('调用了第二个全局中间件')
next()
})
app.get('/', (req, res)=> {
console.log('调用了接口/')
res.send()
})
8.5定义局部使用的中间件
const mw = function(req, res, next) {
next()
}
app.get('/', mw, (req, res) => {})
多个中间件使用
const mw1 = function(req, res, next) {
next()
}
const mw2 = function(req, res, next) {
next()
}
app.get('/', mw1, mw2, (req, res) => {})
app.get('/aa', [mw1, mw2], (req, res) => {})
8.6 中间件的5个注意事项
-
一定要在路由直接注册中间件
-
客服端发送的请求,可以连续调用多个中间件进行处理
-
执行完中间件的业务代码后,记得调用next
-
为了防止代码混乱,next后不在写额外代码
-
连续调用多个中间件时,多个中间件之间,共享req和res对象
8.7 中间件的分类
8.7.1 应用级别的中间件
通过app.use()或者app.get(),app.post,绑定到app实例的中间件都叫做应用级别的中间件
//全局中间件
app.use((req, res, next) => {next()})
const mw = function(req, res, next) {next()}
局部中间件
app.get('/', mw, (req, res)=> {res.send('a')})
8.7.2 路由级别的中间件
绑定到了express.Router()实例上的中间件,就是路由级别的中间件
const express = require('express')
cosnt app = express()
const router = express.Router()
roter.use(function(req, res, next) {
next()
})
app.use('/', router)
8.7.3 错误级别的中间件
错误级别的中间件的作用:专门用来捕获整个项目中发生的异常错误,从而防止项目异常崩溃
他的区别,是参数多了一个err
错误级别的中间件要注册再所有路由之后, 前面的路由发生错误,没有错误级别的中间件,项目会直接错误,而有之后,会继续执行错误级别中间件
例如:
app.get('/', (req,res){
throw new Error('路由发生错误')
res.send()
})
//如果未定义错误级别中间件,访问/会发生错误
//定义了,会继续执行错误级别中间件
app.use(function(err, req, res, next) {
res.send('error!' + err.message) //发生错误,向客户端响应
})
8.7.4 express内置的中间件
从express4.16.0版本开始,express内置3个常用中间件,极大的提高express的效率
-
express.static 快速托管静态资源的内置中间件
-
express.json解析json格式的请求体数据(有兼容性,仅在4.16.0+版本中可以用)
-
express .urlencoded解析url-encoded格式的请求体数据(有兼容性,仅在4.16.0+版本中可以用)
例如
app.use(express.json()) //req.body才会有post传递为josn的数据
app.use(express.urlencoded({extended: false})) //req.body才会有application/x-www-form-urlencoded格式的数据
app.post('/user', (req, res) {
//req.body - 如果没有配置解析格式,req.body将会是undefined
consloe.log(req.body)
res.send('ok')
})
8.7.5 第三方中间件
例如 - body-parser
下载 - npm instll body-parser
使用
const parser = reuqire('body-parser')
app.use(parser.urlencoded({extended: false})) //req.body才会有application/x-www-form-urlencoded格式的数据
app.post('/user', (req, res) {
//req.body - 如果没有配置解析格式,req.body将会是undefined
consloe.log(req.body)
res.send('ok')
})
express .urlencoded是根据body-parser封装的
8.7.6 自定义中间件
手动创建中间件(urlencoded) - 解析参数并挂在在body上
-
监听req的data事件
在中间件中,需要监听req对象的data事件,获取客户端发送到服务器的数据
如果数据量较大,无法一次性发送完毕,则客户端会把数据切割后,分批发送到服务器,所以data会被触发多次
-
监听req的end事件
当请求体数据加收完毕,会触发end事件
-
questring
nodejs内置了一个querystring模块,专门处理查询字符串,通过模块的parse()可以将查询的字符串转换为对象格式
创建fun.js
const qs = require('querystring')
function bodyParser(req, res, next) {
let str = ''
req.on('data', (chunk) => {
str += chunk
})
req.on('end', () => {
req.body = qs.parse(str)
res.send(req.body)
})
}
module.exports = bodyParser
创建index.js
const express = require('express')
const app = express()
const myBodyparser = require('./fun')
app.use(myBodyparser)
app.post('/', (req, res) => {
res.send(req.body)
})
app.listen(80, () => {
console.log('启动服务啦')
})
8.7.7 解决跨域问题
当协议地址或者端口三个有一个不一样浏览器就会产生跨域行为,解决跨域的方案有很多
-
cors(主流方案,服务器端设置响应头)
-
jsonp(通过script标签,有个问题,只支持get请求)
cors解决跨域
什么是cors?
Cors(cross-origin resource sharing,跨域资源共享)由一系列的http响应头组成,这些http响应头决定,浏览器是否阻止前端js代码跨域获取资源
浏览器的通源安全策略默认会阻止网页跨域获取资源。但是如果是服务器配置了cors相关的http响应头,就可以解除浏览器端跨域访问限制
node的epxress怎么配置cors
下载中间件 - npm install cors
使用
const cors = require('cors')
//在路由之前调用配置中间件
app.use(cors())
注意点
-
cors主要是在服务器端进行配置,浏览器端无须做任何额外的配置
-
cors在浏览器中有兼容性,只支持XMLHttpRequest Level2的浏览器才可以开启cors的服务器端(如:IE10+,chrome4+.firefox3.5+)
CORS的请求头
-
Access-Control-Allow-Origin
响应头部中可以携带这个字段,表示允许访问该资源的外部url,语法
Access-Control-Allow-Origin: <origin> | *
例如: 只允许http://itcast.con的请求
res.setHeader('Access-Control-Allow-Origin', 'http://itcast.con')
允许所有
res.setHeader('Access-Control-Allow-Origin', '*')
-
Access-Control-Allow-Headers
默认情况下,cors仅支持客户端向服务器端发送如下9个请求头
Accept, Accept-Language, Content-Language, DPR, Downlink, Save-Data, Viewport-Width, Width,Content-Type(值仅限于text/plain, multipart/form-data, application/x-www-form-urlencoded之一)
如果是客户端向浏览器发送额外的请求头信息,则需要在服务器端,通过该属性进行额外的声明,否则会请求失败
res.setHeader('Access-Control-Allow-Headers', 'Content-Type', 'X-Custom-Header')
-
Access-Control-Allow-Methods
默认情况下,cors仅支持客户端发起,get,post.head请求
如果是客户端希望通过put.delet等请求,需要设置
res.setHeader('Access-Control-Allow-Methods', 'POST, GET,DELET')
//允许所有的http请求
res.setHeader('Access-Control-Allow-Methods', '*')
cors的请求分类
cors的请求根据请求头和请求方式的不同分为简单请求和预检请求
-
简单请求
特点:客户端只会发送一次请求
只要同时满足以下两大条件就是简单请求
1.请求方式是get,post,head三者之一
2. http的头部信息不超过一下几种字段: 无自定义字段,Accept, Accept-Language, Content-Language, DPR, Downlink, Save-Data, Viewport-Width, Width,Content-Type(值仅限于text/plain, multipart/form-data, application/x-www-form-urlencoded之一)
-
预检请求
特点:客户端与服务器之间会发送两次请求,option预检请求成功之后,才会发起真正的请求
满足以下任何条件之一都是预检请求
1.请求方式是get.post,head之外的请求类型
2.请求头中包含自定义头部字段
3.向服务器发送了application/json格式的数据
jsonp实现跨域
-
什么是jsonp
jsonp是浏览器通过<script>标签的src属性,请求服务器的数据,同时服务器返回一个函数的调用,这种请求就叫做jsonp
特点
-
jsonp不属于真正的ajax请求,因为他没有使用到XMLHttpRquest这个对象
-
jsonp只支持get请求
-
创建jsonp
jsonp要注意,如果是已经配置cors跨域,必须要在cors之前配置jsonp的接口,否则jsonp的接口会被处理成为开启cors的接口
app.get('api/jsonp', (req, res) => {
const fucName = req.query.callback
cosnt data = {nane:'xx'}
res.send(`${fucName}${JSON.stringfy(data)}`)
})
const cors = require('cors')
//在路由之前调用配置中间件
app.use(cors())
九 数据库与身份认证
9.1 数据库的基本概念
9.1.1 什么数据库
数据库(database)是用来组织,存储和管理数据的仓库
为了方便管理互联网世界中的数据,就有了数据库管理系统的概念(简称,数据库),用户可以对数据库的数据进行新增,查询,更新,删除等操作
9.1.2 常见的数据库及分类
市面上的数据库有很多种,最常见的数据库有如下几个
-
MySQL数据库(目前使用最广泛,流行度最高的开源免费数据库:community+enterprise)
-
Oracle数据库(收费)
-
SQL Server数据库(收费)
-
Mongdb数据库(community + enterprise)
其中前三种(MySQL,Oracle,SQL Server)属于传统型数据库(又叫做关系型数据库或sql数据库),这三者的时间理念相同,用法也比较相似
9.1.3 传统型数据库的组织结构
数据的组织结构:数据以什么样的结构进行存储
在传统数据库中,数据的组织结构分为数据库(database),数据表(table),数据行(row),字段(field)这四大部分组成
9.2 安装并配置MySQl
对于开发人员来说,只需要安装MySQL Server 和 MySQL Workbench这两个软件,这样就可以满足开发需求
-
MySQL Server:专门用来提供数据存储和服务的软件
-
MySQL Workbench::可视化的MySQL管理工具,通过他,可以方便的操作存储在MySQL Server中的数据
mac电脑
mysql-8.0.0.macos10.15-x86_64.dmg
Mysql-workbench-community-8.0.19-macos-x86_64.dmg
下载地址: https://dev.mysql.com/downloads/mysql/
9.3 数据库的相关知识点
DataType数据类型
-
int - 整数
-
Varchar(len) - 字段串(最大长度)
-
Tunyint(1) -布尔值
字段的特殊标识
-
PK(Primary Key) - 主键,唯一标识
-
NN(Not Null) - 值不允许为空
-
UQ(Unique) - 值唯一
-
AI(Auto Increment) - 值自动增长
9.4 SQL
9.4.1 什么是SQL
SQL(Structured Query Language)是结构化查询语言,专门用来访问和处理数据库的编程语言,能够让我们以编程的形式,操作数据库里面的数据
-
SQL是一门数据库编程语音
-
使用SQL语言编写出来的代码,叫做SQL语句
-
SQL语言只能在关系型数据库中使用(MySQL, oracle, SQL server)。非关系型数据库不支持SQL语言
9.4.2 sql语句
9.4.2.1.查询
-- 从表中查询所有的列
select * from 表名
-- 从表中查询该列名称
select 列名称 from 表名称
--select username, password from user
sql语句对大小写不敏感
9.4.2.2.插入
-- 注意值和名要一一对应
insert into 表名 (列名1,列名2,列名3) values (值1, 值2)
insert into user (username, passwrod) values ('aa', '111')
9.4.2.3.更新修改
update 表名 set 列名称 = 新值, 列名称 = 新值 where 列名称 = 某值
update user set username='aaa', password='123456' where id = 1
9.4.2.4.删除
-- 删除行 delete from 表名 where 列 = 值
9.4.2.5.可以在where中使用的运算符
操作符 | 语法 |
---|---|
= | 等于 |
<> | 不等于 |
> | 大于 |
< | 小于 |
>= | 大于等于 |
<= | 小于等于 |
BETWEEN | 在某个范围内 |
LIKE | 搜索某种模式 |
and 和 or
-
and 同时满足两个条件
-
or 只满足其中一个条件
9.4.2.5.order by
order是用于进行排序的
select * from user order by id
--asc 升序
select * from user order by id asc
--desc 降序
select * from user order by id desc
--多重排序
select * from user order by id desc, status asc
9.4.2.6.count(*)
count(*)是为了查询统计多少行
--查询status为0的所有总数
select count(*) from user wherer status = 0
-- 输出 count 10
-- as 为count设置别名
select count(*) as total from users
--输出 total 10
9.5 在项目中使用MySQL
9.5.1 MySQL在项目中运行原理
9.5.1.1 安装MySQL
npm install mysql
9.5.1.2 配置MySQL,建立连接
const mysql = require('mysql')
//创建连接
const db = mysql.createPool({
host: '127.0.0.1', //本机数据库
user: 'root', //登录名
password: 'admin123', //数据库密码
database: 'my_db_01' //数据库名称
})
//测试数据库是否正常 -SELECT 1 只是测试数据库是否可用无任何含义
db.query('SELECT 1', (err, results) => {
if (err) return console.log(err.message)
console.log(results)
})
db.query('SELECT * FROM users', (err, results) => {
if (err) return console.log(err.message)
// 返回一个数组
console.log(results)
})
// 使用占位符 插入数据表
const user = {
name: 'test-name',
password: '123456',
}
// 注意数据表的id设置了唯一性,如果有数据id为1 被删除,后续插入的数据也不会有id为1的数据
const sql = 'INSERT INTO users (name, password) VALUES (?, ?)'
// 动态参数 - 传参插入数据库
db.query(sql, [user.name, user.password], (err, results) => {
if (err) return console.log(err.message)
// results数组, affectedRows 影响行数
if (results.affectedRows === 1) {
console.log('插入数据成功')
}
})
// 自动字段插入
const user = {
name: 'test-name2',
password: '13233',
status: 1
}
const sql = 'INSERT INTO users SET ?'
db.query(sql, user, (err, results) => {
if (err) return console.log(err.message)
if (results.affectedRows === 1) {
console.log('掺入数据成功')
}
})
// 更新数据
const user = {
id: 5,
name: 'edit-name',
password: '111'
}
const sql = 'UPDATE users SET name=?, password=? WHERE id=?'
db.query(sql, [user.name, user.password, user.id], (err, results) => {
if (err) console.log(err.message)
if (results.affectedRows === 1) {
console.log('更新数据成功')
}
})
// 更新数据的简化方式
const user = {
id: 6,
name: 'edit-name1',
password: '111'
}
const sql = 'UPDATE users SET ? WHERE id=?'
db.query(sql, [user, user.id], (err, results) => {
if (err) console.log(err.message)
if (results.affectedRows === 1) {
console.log('更新数据成功')
}
})
// 删除数据 - 推荐用唯一id来删除
const sql = 'DELETE FROM users WHERE id=?'
db.query(sql, 5, (err, results) => {
if (err) console.log(err.message)
if (results.affectedRows === 1) {
console.log('删除数据成功')
}
})
十 web开发概念
10.1 web开发模式
服务器端渲染
服务器端动态生成内容,发送给浏览器端渲染
优点:
-
前端耗时少,因为服务端负责生成html内容,浏览器只需要直接渲染即可,尤其是移动端,更省点
-
有利于seo ,因为服务端响应的是完整的html页面内容,爬虫更容易取得信息
缺点
-
占用服务器端资源,即服务器端完成HTML页面内容拼接,如果请求过多,会造成服务器端一定压力
-
不利于前后端分离,开发效率低,开发服务端渲染,无法进行分工合作,对于前后端复杂度高的项目,不利于高效开发
浏览器端渲染
优点
-
开发体验好,前端专注于UI开发,后端专注以api开发
-
用户体验好,ajax的技术广泛应用,使得页面局部刷新
-
减轻服务器端渲染压力
缺点
-
不利于seo(解决方案,利用前端ssr框架来解决)
如何选择哪种渲染
-
服务器端渲染:企业级别网页,主要功能无复杂交互,需要良好的seo
-
浏览器端渲染:后台管理类,不需要考虑seo
开发模式不是绝对的,有时为了同时兼顾首页渲染和前后端分类的开发效率,一些网站会采用首屏服务器端渲染 + 其他页面前后端分离的开发模式
10.2 身份认证
身份认证 - 又称身份验证,指通过一定手段,完善对用户身份的确认
简单说来,就是让你看到你自己的信息
不同开发模式下的身份认证
对于服务端渲染和前后端分离者两种开发模式来说,分别又不同的认证方式
-
服务器端 - Session认证
-
前后端分离 - JWT认证机制
10.3 Session认证机制
http协议的无状态性
http协议的无状态 - 客户端每次发送的每次http请求都是独立连接的,每一次的请求对于服务器端都是一个最新的请求
突破HTTP无状态的限制 - cookie
cookie是一个存储在用户浏览器中一段不超过4kb的字符串,主要是有name和value合其他几个用户控制cookie有效期,安全性,使用范围的可选属性组成
作用
起到一个身份认证 - 客户端第一次请求,服务端会通过响应头,向客户端发送一个身份认证的cookie,客户端会保存在浏览器中,下一次请求,浏览器会自动将身份认证相关的cookie,通过请求头的形式发送服务端,服务端会通过cookie进行客户端认证
不同域名的cookie是独立的,每当客户端发起请求,会自动将当前域名下所有未过期的cookie一同发送服务器
特性
-
自动发送
-
域名独立
-
过时期限
-
4kb限制
cookie是不具有安全性(存储在浏览器端,可查看的透明的),因此不能存储重要的隐私数据,比如用户身份信息,密码等
提高身份验证的安全性 - session验证
session验证-- cookie + 服务器端验证(将cookie和用户信息对应,由服务器进行验证)
10.4 node端使用 Session验证
安装插件
npm i express-session
导入使用和使用
var session = require('express-session')
app.use(session({
secret: 'skeyboard.cat', // 属性值为字符串
resave: false, // 固定写法
saveUninitialized: true, // 固定写法
}))
app.post('/login', (req, res) => {
if (req.body.name !== 'admin' || req.body.password !== '123456') {
return res.send({ status: 1, msg: '登录失败' })
}
req.session.user = req.body // 将用户信息存在session中
req.session.isLogin = true
res.send({ status: 0, msg: '登录成功' })
})
app.get('/user', (req, res) => {
if (!req.session.isLogin) {
return res.send({ status: 1, msg: 'fail' })
}
res.send({ status: 0, msg: 'success', name: req.session.user.name })
})
app.post('/logout', (req, res) => {
req.session.destroy()
res.send({
status: 0,
msg: '退出成功'
})
})
Session 认证机制需要配合Cookie才可以实现,由于cookie默认不支持跨域访问,所以当前后端跨域请求时,需要做很多额外的配置,才能实现跨域session认证
10.4 jwt认证制
是一种用于在网络应用中安全传输信息的开放标准(RFC 7519),它通常用于在客户端和服务器之间传递身份验证和授权信息。目前最流行的的解决跨域认证解决方案,以下为你详细介绍 JWT 认证机制
注意
-
当前端请求后端接口,不存在跨域问题的时候,推荐实用session身份认证
-
当前端需要跨域请求后端接口,推荐实用JWT认证机制
jwt的组成
JWT 是一个紧凑的、自包含的字符串,由三部分组成,各部分之间用点(.
)分隔,格式为 Header.Payload.Signature
。
-
Header(头部):包含两部分信息,令牌的类型(通常是 JWT)和使用的签名算法
-
PayloadLL:真正的用户信息,通过加密之后生成的字符串
-
Signature:安全性相关和header一样,只是为了保证token的安全性
jwt的使用
安装包
npm install jsonwebtoken express-jwt
-
jsonwebtoken: 生成jwt字符串
-
express-jwt: 将jwt字符串解析还原成json对象
导入并使用
为了保证jwt字符串的安全性,防止jwt字符串在网络传输过程中被人破译,我们需要专门定义一个加密和解密的secret的秘钥
-
当生成jwt字符串,需要秘钥进行加密
-
当解析成json对象,需要秘钥进行解密
// const Koa = require('koa')
const express = require('express')
const path = require('path')
const fs = require('fs')
const app = new express()
require('./dbInfo.js')
// 配置解决跨域
const cors = require('cors')
app.use(cors())
// 解析post表单数据中间件
const bodyparser = require('body-parser')
app.use(bodyparser.urlencoded({ extended: false }))
const JWT = require('jsonwebtoken')
const { expressjwt } = require('express-jwt');
const secret = 'xxxsabdsadasdas' // 越复杂越好的字符串
// 设置使用中间件,配置以 /api/ 开头的所有路由路径都不需要进行 JWT 验证(unless,不需要验证的路劲)
app.use(expressjwt({ secret, algorithms: ['HS256'] }).unless({ path: [/^\/api\//] }))
// 注册错误处理中间件
app.use((err, req, res, next) => {
// token解析失败的导致的错误
if (err.name === 'UnauthorizedError') {
res.send({
status: 401,
message: 'token过期'
})
}
res.send({
status: 500,
message: '位置错误'
})
})
app.post('/login', (req, res) => {
const { name, password } = req.body;
if (name == 'text' && password == '123456') {
// jwt
// 参数- 用户信息(一般为了安全不存储密码)
// 参数二 - 秘钥
// 参数三 - 配置对象(如过期时间 2h,30s登)
res.send({
status: 200,
message: '登录成功',
token: JWT({ name }, secret, { expiresIn: '2h' })
})
}
res.send({
status: 401,
message: "用户名或密码错误"
})
})
app.get('/my/user', (req, res) => {
// 配置了jwt的路由会新增一个user获取信息
console.log(req.user.name)
res.send({
status: 200,
message: '成功',
data: {
name: req.user.name
}
})
})
app.listen('8080', (http) => {
console.log('我启动了', http)
})