首先导入
1.path.join()路径拼接
// 注意: ../
const pathStr = path.join('/a', '/b/c', '../../', './d', 'e')
console.log(pathStr) // \a\b\d\e
fs.readFile(__dirname + '/files/1.txt')
fs.readFile(path.join(__dirname, './files/1.txt'), 'utf8', function(err, dataStr) {
if (err) {
return console.log(err.message)
}
console.log(dataStr)
})
2.path.basename()查询文件名称
// 定义文件的存放路径
const fpath = '/a/b/c/index.html'
// const fullName = path.basename(fpath)
// console.log(fullName)
const nameWithoutExt = path.basename(fpath, '.html')
console.log(nameWithoutExt)
3.path.extname()查询文件扩展名
// 这是文件的存放路径
const fpath = '/a/b/c/index.html'
const fext = path.extname(fpath)
console.log(fext)
4.综合案例——对html文件进行拆解html/js/css结构
思想:1.读取html文件
2.正则表达式匹配对应内容(read)
3.根据正则表达式提取内容
3.将拆解出的写入新文件(write)
node.js提供的用来创建web服务器的模块
1.服务器相关概念
IP地址:互联网上每台计算机的唯一地址
使用ping 来获取其他网站服务器的IP地址
2.域名与域名服务器
域名:字符型的地址方案,与IP地址一一对应,
域名服务器:存放IP地址与域名对应的关系,提供两者之间转换的服务器
3.端口号:门牌号,每个web服务对应一个端口号,每个端口号只对应一个web服务,80端口号可以被省略
4.创建web服务器
首先调用该模块
// 1. 导入 http 模块
const http = require('http')
// 2. 创建 web 服务器实例
const server = http.createServer()
// 3. 为服务器实例绑定 request 事件,监听客户端的请求
server.on('request', function (req, res) {
console.log('Someone visit our web server.')
})
// 4. 启动服务器
server.listen(8080, function () {
console.log('server running at http://127.0.0.1:8080')
})
4.request response 请求 响应对象
const http = require('http')
const server = http.createServer()
// req 是请求对象,包含了与客户端相关的数据和属性
server.on('request', (req, res) => {
// req.url 是客户端请求的 URL 地址
const url = req.url
// req.method 是客户端请求的 method 类型
const method = req.method
const str = `Your request url is ${url}, and request method is ${method}`
console.log(str)
// 调用 res.end() 方法,向客户端响应一些内容
res.end(str)
})
server.listen(80, () => {
console.log('your server running at http://127.0.0.1')
})
5.解决中文乱码——设置响应头
6.时钟clock案例
1.模块分类:内置模块、自定义模块、第三方模块
加载内置模块与第三方模块相同
自定义模块加载,需要路径
const m1 = require('./06.m1.js')
console.log(m1)
2.模块作用域(与函数作用域一致)
3.module对象:每个自定义模块都有一个module对象,存储了和当前模块的相关信息。
4.module.exports():当外界用require()方法导入对象时,暴露给外界是module.exports指向的对象,exports对象具有覆盖性,最终指向最后挂载的对象。
5.exports对象,其作用与module.exports()相同,但最后暴露给外界的以module.exports指向的对象为准
const age = 20
// 向 module.exports 对象上挂载 username 属性
module.exports.username = 'zs'
// 向 module.exports 对象上挂载 sayHello 方法
module.exports.sayHello = function() {
console.log('Hello!')
}
module.exports.age = age
// 让 module.exports 指向一个全新的对象
module.exports = {
nickname: '小黑',
sayHi() {
console.log('Hi!')
}
}
node.js中第三方模块称为包,基于内置模块封装出来的
https://www.npmjs…com
1.包的安装:终端运行 npm install 包名称@包的版本号
2.package.json,置于项目根目录中,记录项目中安装的包
项目开始前,自动创建 npm init -y
3.npm install 根据package.jason一次性安装所有的包
npm uninstall 包名 卸载指定的包
4.package.json dependencies 包含项目所需包
devDependencies 包含项目开发过程中所需要的包,但是 项目上线后不需要的包 使用如下命令安装指定的包并将其放置在devDependecies下 npm i 包名 -D 或者 npm install 包名 --save-dev 包名
5.下载包的镜像源
查看当前的镜像源:npm config get registry
设置其他的镜像源
6.nrm,
首先下载nrm
nrm ls 查看所有可用的镜像源
nrm use 源(更换下载源)
7.包的分类:项目包
项目包:放置在node-modules内,分为开发依赖包与核心依赖包
全局包:npm install 命令时,使用了 -g的包,包的位置位于C:\Users\Amanda\AppData\Roaming\npm\node_modules\nrm
8.包的内部结构
包必须以单独的目录而存在、包的顶级目录下必须包含package.json这个包配置管理文件、package.json必须包含name/version/main这三个属性 main指包的入口
9.开发属于自己的包
包的结构:包的根目录:itheima-tools 文件夹
包含如下 三个文件:package.json
index.js(包的入口文件)
readme.md(包的说明文档)包的发布
注册——登陆(npm login)——发布(npm publish)
9.模块的加载机制:模块加载后都会缓存,优先从缓存中加载信息
内置模块:该模块优先级最高
自定义模块:需以.指定以/或者…/路径标识符为开头,否则会加载成内置模块或者第三方模块,注:在require()导入自定义模块时,若忽略了文件的扩展名,则分别按如下顺序加载文件:确切的文件名——.js——.json——.node
第三方模块:
目录作为模块加载:优先从被加载目录中查询package.json文件,并寻找main属性作为require加载入口——否则,则加载目录下的index.js文件——否则,报错
express服务器基本使用:导入——创建对象实例——启动
//引入express并创建应用对象
const express = require('express')
const app = express()
//监听端口启动服务
app.listen(8000, ()=>{
console.log('服务已经启动,端口8000监听中')
})
获取GET/POST请求
// 1. 导入 express
const express = require('express')
// 2. 创建 web 服务器
const app = express()
// 3. 监听客户端的 GET 和 POST 请求,创建路由规则,并向客户端响应具体的内容
app.get('/user', (req, res) => {
// 调用 express 提供的 res.send() 方法,向客户端响应一个 JSON 对象
res.send({ name: 'zs', age: 20, gender: '男' })
})
app.post('/user', (req, res) => {
// 调用 express 提供的 res.send() 方法,向客户端响应一个 文本字符串
res.send('请求成功')
})
//监听端口启动服务
app.listen(8000, ()=>{
console.log('服务已经启动,端口8000监听中')
})
req.query :获取客户端查询过来的参数
req.params:访问url中,通过: 匹配的参数
express.static():静态资源托管,被托管的文件夹名不会出现在路径URL中,先托管的文件先起作用
const express = require('express')
const app = express()
// 在这里,调用 express.static() 方法,快速的对外提供静态资源
// 在托管的静态资源访问路径之前,挂载路径前缀,则必须通过该前缀所在路径访问文件
app.use('/files', express.static('./files'))
// 普通的静态资源托管
app.use(express.static('./clock'))
app.listen(80, () => {
console.log('express server running at http://127.0.0.1')
})
nodemon 服务器的自动重启,一旦服务器内容发生改变,则自动重启服务器
路由:指客户端的请求与服务器处理函数之间的映射关系
express路由三部分组成:请求的类型、请求的url地址、处理函数
模块化的路由:
// 1. 导入 express
const express = require('express')
// 2. 创建路由对象
const router = express.Router()
// 3. 挂载具体的路由
router.get('/user/list', (req, res) => {
res.send('Get user list.')
})
router.post('/user/add', (req, res) => {
res.send('Add new user.')
})
// 4. 向外导出路由对象
module.exports = router
导入模块化的路由:
const express = require('express')
const app = express()
// app.use('/files', express.static('./files'))
// 1. 导入路由模块
const router = require('./03.router')
// 2. 注册路由模块, 挂载统一的访问前缀
app.use('/api', router)
// 注意: app.use() 函数的作用,就是来注册全局中间件
//启动服务器
app.listen(80, () => {
console.log('http://127.0.0.1')
})
1.其本质为function,与路由处理函数的不同之处在于有next形参
2.next函数就是next方法 next(),只有使用了next(),才可以使请求到达下一个路由或中间件
3.将函数注册为全局中间件:app.use()
const express = require('express')
const app = express()
// // 定义一个最简单的中间件函数
// const mw = function (req, res, next) {
// console.log('这是最简单的中间件函数')
// // 把流转关系,转交给下一个中间件或路由
// next()
// }
// // 将 mw 注册为全局生效的中间件
// app.use(mw)
// 这是定义全局中间件的简化形式
app.use((req, res, next) => {
console.log('这是最简单的中间件函数')
next()
})
app.get('/', (req, res) => {
console.log('调用了 / 这个路由')
res.send('Home page.')
})
app.get('/user', (req, res) => {
console.log('调用了 /user 这个路由')
res.send('User page.')
})
app.listen(80, () => {
console.log('http://127.0.0.1')
})
4.中间件的作用
多个中间件之间,共享同一份req与res,可基于此特性,在上游中间件中,为req或者res添加自定义的属性或方法,供下游的中间件或者路由使用
const express = require('express')
const app = express()
// 这是定义全局中间件的简化形式
app.use((req, res, next) => {
// 获取到请求到达服务器的时间
const time = Date.now()
// 为 req 对象,挂载自定义属性,从而把时间共享给后面的所有路由
req.startTime = time
next()
})
app.get('/', (req, res) => {
res.send('Home page.' + req.startTime)
})
app.get('/user', (req, res) => {
res.send('User page.' + req.startTime)
})
app.listen(80, () => {
console.log('http://127.0.0.1')
})
5.定义多个全局中间件
利用app.use()定义多个全局中间件,客户端请求到达服务器后,调用顺序根据中间件定义的先后顺序
6.局部中间件,不使用app.use()
7.定义并使用简单的局部中间件
// 导入 express 模块
const express = require('express')
// 创建 express 的服务器实例
const app = express()
// 1. 定义中间件函数
const mw1 = (req, res, next) => {
console.log('调用了局部生效的中间件')
next()
}
// 2. 创建路由
app.get('/', mw1, (req, res) => {
res.send('Home page.')
})
app.get('/user', (req, res) => {
res.send('User page.')
})
// 调用 app.listen 方法,指定端口号并启动web服务器
app.listen(80, function () {
console.log('Express server running at http://127.0.0.1')
})
8.同时使用多个局部中间件
// 导入 express 模块
const express = require('express')
// 创建 express 的服务器实例
const app = express()
// 1. 定义中间件函数
const mw1 = (req, res, next) => {
console.log('调用了第一个局部生效的中间件')
next()
}
const mw2 = (req, res, next) => {
console.log('调用了第二个局部生效的中间件')
next()
}
// 2. 创建路由
app.get('/', [mw1, mw2], (req, res) => {
res.send('Home page.')
})
app.get('/user', (req, res) => {
res.send('User page.')
})
// 调用 app.listen 方法,指定端口号并启动web服务器
app.listen(80, function () {
console.log('Express server running at http://127.0.0.1')
})
9.中间件的分类
9.1应用级别的中间件:app.use() app.get() app.post(),绑定到app实例上的中间件
app.use((req, res, next) => {
console.log('这是最简单的中间件函数')
next()
})
9.2 路由级别的中间件: express.Router() 绑定到express.Router()实例上的中间件
router.get( (req, res, next) => {
res.send('Get user list.')
next()
})
9.3 错误级别的中间件:捕获项目中发生的错误,其function函数中,参数必须有(err,req,res,next),
错误级别中间件必须设置在所有路由后面,以便捕捉错误
/ 导入 express 模块
const express = require('express')
// 创建 express 的服务器实例
const app = express()
// 1. 定义路由
app.get('/', (req, res) => {
// 1.1 人为的制造错误
throw new Error('服务器内部发生了错误!')
res.send('Home page.')
})
// 2. 定义错误级别的中间件,捕获整个项目的异常错误,从而防止程序的崩溃
app.use((err, req, res, next) => {
console.log('发生了错误!' + err.message) b
res.send('Error:' + err.message)
})
// 调用 app.listen 方法,指定端口号并启动web服务器
app.listen(80, function () {
console.log('Express server running at http://127.0.0.1')
})
9.4内置中间件
expres.static 快速托管静态资源中间件,例如:HTML文件、图片、css样式等,无兼容性
expres.json 解析json格式的请求体数据,有兼容性
expres.json 解析url-encoded 格式的请求体数据,有兼容性
// 导入 express 模块
const express = require('express')
// 创建 express 的服务器实例
const app = express()
// 通过 express.json() 这个中间件,解析表单中的 JSON 格式的数据
app.use(express.json())
// 通过 express.urlencoded() 这个中间件,来解析 表单中的 url-encoded 格式的数据
app.use(express.urlencoded({ extended: false }))
app.post('/user', (req, res) => {
// 在服务器,可以使用 req.body 这个属性,来接收客户端发送过来的请求体数据
// 默认情况下,如果不配置解析表单数据的中间件,则 req.body 默认等于 undefined
console.log(req.body)
res.send('ok')
})
app.post('/book', (req, res) => {
// 在服务器端,可以通过 req,body 来获取 JSON 格式的表单数据和 url-encoded 格式的数据
console.log(req.body)
res.send('ok')
})
// 调用 app.listen 方法,指定端口号并启动web服务器
app.listen(80, function () {
console.log('Express server running at http://127.0.0.1')
})
9.5第三方的中间件
目前常用body-parser,用来解析请求体数据
// 导入 express 模块
const express = require('express')
// 创建 express 的服务器实例
const app = express()
// 1. 导入解析表单数据的中间件 body-parser
const parser = require('body-parser')
// 2. 使用 app.use() 注册中间件
app.use(parser.urlencoded({ extended: false }))
// app.use(express.urlencoded({ extended: false }))
app.post('/user', (req, res) => {
// 如果没有配置任何解析表单数据的中间件,则 req.body 默认等于 undefined
console.log(req.body)
res.send('ok')
})
// 调用 app.listen 方法,指定端口号并启动web服务器
app.listen(80, function () {
console.log('Express server running at http://127.0.0.1')
})
9.6中自定义的中间件
自定义中间件
自定义中间件的模块化以及导入
基本步骤
1.创建基本服务器
2.创建路由模块
const express = require('express')
const router = express.Router()
// 在这里挂载对应的路由
router.get('/get', (req, res) => {
// 通过 req.query 获取客户端通过查询字符串,发送到服务器的数据
const query = req.query
// 调用 res.send() 方法,向客户端响应处理的结果
res.send({
status: 0, // 0 表示处理成功,1 表示处理失败
msg: 'GET 请求成功!', // 状态的描述
data: query, // 需要响应给客户端的数据
})
})
// 定义 POST 接口
router.post('/post', (req, res) => {
// 通过 req.body 获取请求体中包含的 url-encoded 格式的数据
const body = req.body
// 调用 res.send() 方法,向客户端响应结果
res.send({
status: 0,
msg: 'POST 请求成功!',
data: body,
})
})
// 定义 DELETE 接口
router.delete('/delete', (req, res) => {
res.send({
status: 0,
msg: 'DELETE请求成功',
})
})
module.exports = router
3.在1的基础上导入创建的2路由 模块
// 导入 express
const express = require('express')
// 创建服务器实例
const app = express()
// 配置解析表单数据的中间件
app.use(express.urlencoded({ extended: false }))
// 必须在配置 cors 中间件之前,配置 JSONP 的接口
app.get('/api/jsonp', (req, res) => {
// TODO: 定义 JSONP 接口具体的实现过程
// 1. 得到函数的名称
const funcName = req.query.callback
// 2. 定义要发送到客户端的数据对象
const data = { name: 'zs', age: 22 }
// 3. 拼接出一个函数的调用
const scriptStr = `${funcName}(${JSON.stringify(data)})`
// 4. 把拼接的字符串,响应给客户端
res.send(scriptStr)
})
// 一定要在路由之前,配置 cors 这个中间件,从而解决接口跨域的问题
const cors = require('cors')
app.use(cors())
// 导入路由模块
const router = require('./16.apiRouter')
// 把路由模块,注册到 app 上
app.use('/api', router)
// 启动服务器
app.listen(80, () => {
console.log('express server running at http://127.0.0.1')
})
接口跨域问题常见的解决方案:
1.jsonp中间件
2.cors中间件:安装此中间件–在编写接口中导入中间件–app.use(cors())配置中间件
附:cors跨域资源共享
一般的请求头: Access-Control-Allow-Origin可携带字段,当字段值为用通配符*,表示允许来自任何域的请求。
设置方式如下:
//设置响应头 设置允许跨域
response.setHeader('Access-Control-Allow-Origin', '*')
cors响应头部 Access-Control-Allow-Headers,一般仅支持九个请求头,若需要额外的请求头,则在服务器端通过Access-Control-Allow-Headers对额外的请求头进行声明
例:
//设置响应头 设置允许跨域
response.setHeader('Access-Control-Allow-Headers', 'Content-Type')
cors响应头部 Access-Control-Allow-Methods, 默认情况一般仅支持客户端发起的GET/POST/HEAD请求,若客户端想通过PUT/DELETE等额外的方式请求服务器资源,则在服务器端通过Access-Control-Allow-Methods来指明方法
//设置响应头 设置客户端请求的方法
response.setHeader('Access-Control-Allow-Methods', 'POST, GET, DELETE,HEAD')
//允许所有的请求方法
response.setHeader('Access-Control-Allow-Methods', '*')
3.cors请求分类
3.1简单请求:客户端与服务器之间只发起一次请求。
3.2预检请求:客户端与服务器发起两次请求。
请求方式为GET/POST/HEAD之外的请求;请求头中包含自定义头部字段;向服务器发送了json/application格式的数据(满足以上条件之一的都算)
4.JSONP
概念:浏览器端通过
// 必须在配置 cors 中间件之前,配置 JSONP 的接口
app.get('/api/jsonp', (req, res) => {
// TODO: 定义 JSONP 接口具体的实现过程
// 1. 得到函数的名称
const funcName = req.query.callback
// 2. 定义要发送到客户端的数据对象
const data = { name: 'zs', age: 22 }
// 3. 拼接出一个函数的调用
const scriptStr = `${funcName}(${JSON.stringify(data)})`
// 4. 把拼接的字符串,响应给客户端
res.send(scriptStr)
})
客户端的请求发起(或参照ajax学习):
$('#btnJSONP').on('click', function () {
$.ajax({
type: 'GET',
url: 'http://127.0.0.1/api/jsonp',
dataType: 'jsonp',
success: function (res) {
console.log(res)
},
})
})
mysql/oracle/SQL sever属于传统数据库(关系型数据库)
Mongodb新型数据库(非关系型数据库,NoSQL)
服务器发送给客户端的 HTML 页面,是在服务器通过字符串的拼接,动态生成的。因此,客户端不需要使用 Ajax 这样的技术额外请求页面的数据。
优点:前端耗时少;有利于SEO
缺点:占用服务器端资源;不利于前后端分离
前后端分离的开发模式,依赖于 Ajax 技术的广泛应用。简而言之,前后端分离的 Web 开发模式,就是后端只负责提供 API 接口,前端使用 Ajax 调用接口的开发模式。
优点:开发体验好;用户体验好;减轻服务器端的渲染压力
缺点:不利于SEO,所以需借助Vue、React等前端框架的ssr技术
① 服务端渲染: Session 认证机制
② 前后端分离: JWT 认证机制
1.Cookie 认证机制
1.1 HTTP 协议的无状态性:客户端的每次 HTTP 请求都是独立的,连续多个请求之间没有直接的关系,服务器不会主动保留每次 HTTP 请求的状态。
解决无状态性:使用Cookie
1.2 Cookie:Cookie 是存储在用户浏览器中的一段不超过 4 KB 的字符串。它由一个名称(Name)、一个值(Value)和其它几个用
于控制 Cookie 有效期、安全性、使用范围的可选属性组成。
不同域名下的 Cookie 各自独立,每当客户端发起请求时,会自动把当前域名下所有未过期的 Cookie 一同发送到服务器。
特点:
① 自动发送
② 域名独立
③ 过期时限
④ 4KB 限制
1.3 Cookie 在身份认证中的作用
在这里插入图片描述
缺点:不具有安全性,Cookie 是存储在浏览器中的,而且浏览器也提供了读写 Cookie
的 API,Cookie 很容易被伪造。不使用Cookie保存隐私数据。
2.Sesion 认证机制
2.1 原理:
2.2 Express中使用Session机制
步骤:导入Session模块——配置Session中间件
const session = require('express-session')
app.use(
session({
secret: 'itheima',
resave: false,
saveUninitialized: true,
})
)
2.3 Session中存储数据:通过req.session来访问与使用session对象,从而存储用户关键信息
2.4 从Session中取数据
2.5清空Session信息:req.session.destroy()
3.JWT(JSON Web Token) 认证机制
3.1工作原理:
3.2 JWT三个组成成分:Header、Payload、Signature
3.3 JWT使用方式
客户端收到服务器返回的 JWT 之后,通常会将它以localStorage 或 sessionStorage存储,此后,客户端每次与服务器通信,都要带上这个 JWT 的字符串,从而进行身份认证。推荐的做法是把 JWT 放在 HTTP 请求头的 Authorization 字段中。
3.3 EXPRESS中使用JWT方式:
a. 安装JWT相关包( jsonwebtoken 用于生成 JWT 字符串/express-jwt 用于将 JWT 字符串解析还原成 JSON 对象)
b.导入相关包
c.定义secret密钥(用于JWT字符串生成时加密与JWT字符串转成JSON对象时解密)
d.调用jsonwebtoken提供jwt.sign()方法,生成JWT字符串
e.使用express.jwt中间件:在客户访问有权限的接口时,通过请求头中的Authorization字段,将Token字符串发送到服务器进行身份验证,使用express.jwt中间件将用户发送过来的Token解析还原成JSON对象。
f.req.user对象从JWTz字符串中解析获取用户信息(express新版本中有效负载为req.auth)
代码如下:
g.捕获解析JWT失败后产生的错误:使用EXPRESS错误捕捉中间件
// 导入 express 模块
const express = require('express')
// 创建 express 的服务器实例
const app = express()
// TODO_01:安装并导入 JWT 相关的两个包,分别是 jsonwebtoken 和 express-jwt
const jwt = require('jsonwebtoken')
const expressJWT = require('express-jwt')
// 允许跨域资源共享
const cors = require('cors')
app.use(cors())
// 解析 post 表单数据的中间件
const bodyParser = require('body-parser')
app.use(bodyParser.urlencoded({ extended: false }))
// TODO_02:定义 secret 密钥,建议将密钥命名为 secretKey
const secretKey = 'itheima No1 ^_^'
// TODO_04:注册将 JWT 字符串解析还原成 JSON 对象的中间件
// 注意:只要配置成功了 express-jwt 这个中间件,就可以把解析出来的用户信息,挂载到 req.user 属性上;unless可以配置不需要权限的用户
app.use(expressJWT({ secret: secretKey }).unless({ path: [/^\/api\//] }))
// 登录接口
app.post('/api/login', function (req, res) {
// 将 req.body 请求体中的数据,转存为 userinfo 常量
const userinfo = req.body
// 登录失败
if (userinfo.username !== 'admin' || userinfo.password !== '000000') {
return res.send({
status: 400,
message: '登录失败!',
})
}
// 登录成功
// TODO_03:在登录成功之后,调用 jwt.sign() 方法生成 JWT 字符串。并通过 token 属性发送给客户端
// 参数1:用户的信息对象
// 参数2:加密的秘钥
// 参数3:配置对象,可以配置当前 token 的有效期
// 记住:千万不要把密码加密到 token 字符中
const tokenStr = jwt.sign({ username: userinfo.username }, secretKey, { expiresIn: '30s' })
res.send({
status: 200,
message: '登录成功!',
token: tokenStr, // 要发送给客户端的 token 字符串
})
})
// 这是一个有权限的 API 接口
app.get('/admin/getinfo', function (req, res) {
// TODO_05:使用 req.user 获取用户信息,并使用 data 属性将用户信息发送给客户端
console.log(req.user)
res.send({
status: 200,
message: '获取用户信息成功!',
data: req.user, // 要发送给客户端的用户信息
})
})
// TODO_06:使用全局错误处理中间件,捕获解析 JWT 失败后产生的错误
app.use((err, req, res, next) => {
// 这次错误是由 token 解析失败导致的
if (err.name === 'UnauthorizedError') {
return res.send({
status: 401,
message: '无效的token',
})
}
res.send({
status: 500,
message: '未知的错误',
})
})
// 调用 app.listen 方法,指定端口号并启动web服务器
app.listen(8888, function () {
console.log('Express server running at http://127.0.0.1:8888')
})