当前位置: 首页 > news >正文

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

特点

  1. jsonp不属于真正的ajax请求,因为他没有使用到XMLHttpRquest这个对象

  2. 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)
})
​
​

相关文章:

  • Plant Simulation中怎么更改机器人3D模型
  • 精挑20题:MySQL 8.0高频面试题深度解析——掌握核心知识点、新特性和优化技巧
  • WPF 布局中的共性尺寸组(Shared Size Group)
  • Git远程拉取和推送配置
  • Docker Compose部署MantisBT
  • 在本地跑通spark环境
  • 网络防火墙(Firewall)、Web防火墙(WAF)、入侵检测系统(IDS)、入侵防御系统(IPS)对比总结
  • webrtc3A算法
  • Python数据可视化工具:六西格玛及其基础工具概览
  • 进程管理笔记1-进程线程基础知识
  • R语言绘图:小提琴图
  • 在Mac上一键安装Mysql(解决所有安装问题)
  • sql-DDL
  • 《C语言中的“吃豆人”:%*c 的奇妙冒险》
  • Lineageos 22.1(Android 15)实现负一屏
  • CSS 选择器详解:类型、用法与示例
  • 微博ip属地不发微博会不会变
  • Oracle 数据迁移至 GaussDB 注意事项
  • Gone v2 Tracer 组件-给微服务提供统一的traceID
  • 科技资讯杂志科技资讯编辑部科技资讯杂志社2025年第2期目录
  • 第1现场|俄媒称乌克兰网上出售北约对乌军培训手册
  • 美国前总统拜登确诊前列腺癌
  • 《缶翁的世界》首发:看吴昌硕王一亭等湖州籍书画家的影响
  • “上海-日喀则”援藏入境旅游包机在沪首航
  • 15年全程免费,内蒙古准格尔旗实现幼儿园到高中0学费
  • 淮安市车桥中学党总支书记王习元逝世,终年51岁