Node.js
Node.js是一个基于Chrome V8引擎的JavaScript运行环境
官方地址:https://nodejs.org/zh-cn/
- 浏览器是
JavaScript
的前端运行环境 Node.js
是JavaScript
的后端运行环境Node.js
中无法调用DOM
和BOM
等浏览器内置API
主要学习:JavaScript+Node.js
内置API模块(fs
、path
、http
等)+第三方API模块(express
、mysql
等)
环境安装:官网下载安装(推荐LTS版本)
查看版本:node-v
执行代码:node app.js
fs文件系统模块
fs模块
是Node.js
官方提供的、用来操作文件的模块,它提供了一系列的方法和属性,用来满足用户对文件的需求。
fs.readFile()
方法:用来读取指定文件中的内容。fs.writeFile()
方法:用来向指定文件中写入内容。
在JavaScript
代码中,使用fs
模块之前,需要先导入它:
const fs = require('fs')
读取指定文件中的内容
fs.readFile('path','options',callback)
- 参数1:必选参数,字符串,表示文件的路径.
- 参数2:可选参数,表示什么编码格式读取文件,通常为(
utf8
) - 参数3:必选参数,文件读取完成后,通过回调函数拿到读取的结果。
示例:
fs.readFile('./files/12.txt','utf8',function(err,dataStr){
//2.1打印失败的结果
//如果读取成功,则err的值为null
//如果读取失败,则err的值为错误对象,dataStr的值为underfined
console.log(err);
console.log('--------');
//2.2打印成功的结果
console.log(dataStr);
})
可以判断err
对象是否为null
,从而知晓文件读取的结果:
const fs = require('fs');
fs.readFile('./files/111.txt', 'utf-8', function (err, dataStr) {
if (err) {
return console.log('读取文件失败' + err.message); //读取文件失败ENOENT: no such file or directory, open 'E:\Web\Node.js\Day1\files\111.txt'
}
console.log('读取文件成功,结果是' + dataStr); //读取文件成功,结果是111
});
向指定文件中写入内容
fs.writeFile('file','data','options',callback)
- 参数1:必选参数,需要指定一个文件路径的字符串,表示文件的存放路径
- 参数2:必选参数,要写入的内容
- 参数3:可选参数,写入文件格式,默认是
utf8
- 参数4:必选参数,文件写入完成后的回调函数。
示例(判断是否写入成功):
//1.导入fs文件系统模块
const fs=require('fs')
fs.writeFile('./files/3.txt','ok123',function(err){
//如果文件写入成功,则err的值等于null
//如果文件写入失败,则err的值等于一个错误对象
// console.log(err);
if(err){
return console.log('文件写入失败!'+err.message);
}
console.log('文件写入成功');
})
练习:考试成绩整理
原数据:
小红=99 小白=100 小黄=70 小黑=90
整理后:
小红:99
小白:100
小黄:70
小黑:90
代码:
const fs=require('fs')
//调用fs.readFile()读取文件内容
fs.readFile('./files/成绩.txt','utf-8',function(err,dataStr){
//判断是否读取成功
if(err){
return console.log('读取文件失败'+err.message);
}
// console.log('读取成功'+dataStr);
//4.1先把成绩的数据,按照空格进行分隔
const arrOld=dataStr.split(' ')
// console.log(arrOld);
// 4.2循环分割后的数组,对每一项数据,进行字符串的替换操作
const arrNew=[]
arrOld.forEach(item=>{
arrNew.push(item.replace('=',':'))
})
// 4.3把新数组中的每一项,进行合并,得到一个新的字符串
const newStr= arrNew.join('\r\n')
console.log(newStr);
//5.调用fs.writeFile()方法,把处理完的成绩,写入到新文件中
fs.writeFile('./files/成绩-ok.txt',newStr,function(err){
if(err){
return console.log('写入文件失败'+err.message);
}
console.log('成绩写入成功');
})
})
fs模块的路径动态拼接
在使用fs模块操作文件时,直接提供完整的路径,不要提供./
和../
开头的相对路径,防止动态路径拼接出错。
// __dirname表示当前文件所处的目录
fs.readFile(__dirname+'/files/1.txt','utf-8',function(err,dataStr){
if(err){
return console.log('读取文件失败'+err.message);
}
console.log('读取文件成功'+dataStr);
})
path路径模块
path
模块是 Node.js
官方提供的、用来处理路径的模块。它提供了一系列的方法和属性,用来满足用户对路径的处理 需求。
path.join()
方法:用来将多个路径拼接成一个完整的路径字符串。path.basename()
方法:用来从路径字符串中,将文件名解析出来。
使用path
模块之前,先导入:
const path = require('path')
路径拼接
path.join([...paths])
...paths<string>
路径片段的序列- 返回值:
<string>
示例:
const pathStr = path.join('/a', '/b/c', '../', './d', 'e');
console.log(pathStr);//输出内容:\a\b\d\e
const pathStr1 = path.join(__dirname,'./files/1.txt');
console.log(pathStr1);//输出:当前文件所处目录/files/1.txt
注意:但凡涉及到路径拼接的操作,都要使用path.join()
方法进行处理,不要直接使用+
进行字符串拼接。
获取路径中的文件名
path.basename(path,ext)
此方法可以获取到路径中的最后一部分。
path
:必选参数,表示一个路径的字符串ext
:可选参数,表示文件扩展名- 返回:表示路径中的最后一部分
示例:
//文件存放的路径
const fpath = 'a/b/c/index.html';
//文件名+扩展名
const fullName = path.basename(fpath);
console.log(fullName); //输出 index.html
//文件名
const nameWithoutExit = path.basename(fpath, '.html');
console.log(nameWithoutExit); //输出 index
获取路径中的文件扩展名
path.extname(path)
- path:必选参数,路径的字符串
- 返回:返回得到文件的扩展名字符串
示例:
//文件存放的路径
const fpath = 'a/b/c/index.html';
const kzm = path.extname(fpath);
console.log(kzm); //输出 html
http模块
在网络节点中,负责消费资源的电脑,叫做客户端;负责对外提供网络资源的电脑,叫做服务器。
http
模块是 Node.js
官方提供的、用来创建 web 服务器的模块。通过 http
模块提供的 http.createServer()
方法,就 能方便的把一台普通的电脑,变成一台 Web 服务器,从而对外提供 Web 资源服务。
创建最基本的web服务器
1.导入htpp模块
const http = require('http')
2.创建web服务器实例
const server = http.createServer()
3.为服务器实例绑定request
事件
绑定request
事件,即可监听客户端发送过来的网络请求。
server.on('request',function(req,res){
console.log('Someone visit our web server');
})
4.启动服务器
调用服务器实例的.listen()
方法,即可启动当前的web服务器实例.
server.listen(80,function(){
console.log('server running at http://127.0.0.1:80');
})
req请求对象
只要服务器接收到了客户端的请求,就会调用通过server.on()
为服务器绑定的request
事件处理函数。
如果想在事件处理函数中,访问与客户端相关的数据或属性,可以使用如下方式:
server.on('request',(req)=>{
//req是请求对象,它包含了与客户端相关的数据和属性
//req.url是客户端请求的URL地址
//req.method是客户端的method请求类型
const str = 'Your request url is $(req.url),and request method is ${req.method}'
console.log(str)
})
解决中午乱码问题
当调用 res.end()
方法,向客户端发送中文内容的时候,会出现乱码问题,此时,需要手动设置内容的编码格式:
server.on('request', (req,res)=> {
//定义一个字符串,包含中文内容
const str=`你请求的URL地址是 ${req.url},请求的method类似为 ${req.method}`
//调用res.setHeader()方法,设置响应头 Content-Type的值为text/html;charset=utf-8
res.setHeader('Content-Type','text/html;charset=utf-8')
res.end(str)
})
根据不同的url响应不同的html内容
const http=require('http')
const server=http.createServer()
server.on('request',(req,res)=>{
//1.获取请求的url地址
const url=req.url
//2.设置默认的响应内容为404
let content='404 Not found'
//3.判断用户请求的是否为/或者/index.html页面 首页
if(url==='/'||url==='/index.html'){
content='<h1>首页</h1>'
//4.判断用户请求是否为/about.html页面
}else if(url==='/about.html'){
content='<h1>关于</h1>'
}
//5.设置响应头,防止中文乱码
res.setHeader('Content-Type','text/html;charset=utf-8')
//6.使用res.end()把内容响应给客户端
res.end(content)
})
server.listen(80,()=>{
console.log('server running at http://127.0.0.1');
})
Node.js中的模块化
Node.js中根据模块来源不同,将模块分为3个大类:
- 内置模块:内置模块是由
Node.js
官方提供的,例如fs
、path
、http
等 - 自定义模块:用户创建的每个
.js
文件,都是自定义模块 - 第三方模块:由第三方开发出来的模块,并非官方提供的内置模块,也不是用户创建的自定义模块,使用前需要先下载。
加载模块
使用强大的require()
方法,可以加载需要的内置模块、用户自定义模块、第三方模块进行使用。
//1.加载内置的fs模块
const fs = require('fs')
//2,加载用户的自定义模块
const custom = require('./custom.js')
//3.加载第三方模块
const moment = require('moment')
注意:使用require()
方法加载其他模块时,会执行被加载模块中的代码。
模块作用域
和函数作用域类似,在自定义模块中定义的变量、方法等成员,只能在当前模块内被访问,这种模块级别的访问限制,叫做模块作用域。
//01模块
//在模块作用域中定义常量username
const username = '张三';
//在模块作用域中定义函数sayHello
function sayHello() {
console.log('大家好,我是' + username);
}
//02.js
const custom = require('./01');
//输出空对象,在02.js模块中,无法访问到01模块中的私有成员
console.log(custom);
好处:
防止了全局变量污染问题。
向外共享模块作用域中的成员
1.module
对象
在每个.js自定义模块中都有一个module
对象,它里面存储了和当前模块有关的信息:
2.module.exports
对象
在自定义模块中,可以使用 module.exports
对象,将模块内的成员共享出去,供外界使用。 外界用 require()
方法导入自定义模块时,得到的就是 module.exports
所指向的对象。
3.共享成员时的注意点
使用 require()
方法导入模块时,导入的结果,永远以 module.exports
指向的对象为准
4.exports
对象
由于 module.exports
单词写起来比较复杂,为了简化向外共享成员的代码,Node 提供了 exports
对象。
默认情况 下,exports
和 module.exports
指向同一个对象。
最终共享的结果,还是以 module.exports
指向的对象为准。
时刻谨记,require()
模块时,得到的永远是 module.exports
指向的对象:
注意:为了防止混乱,不要在同一个模块中同时使用exports
和module.exports
模块化规范
Node.js
遵循了 CommonJS
模块化规范,CommonJS
规定了模块的特性和各模块之间如何相互依赖。
CommonJS
规定:
- 每个模块内部,
module
变量代表当前模块。 module
变量是一个对象,它的exports
属性(即module.exports
)是对外的接口。- 加载某个模块,其实是加载该模块的
module.exports
属性。require()
方法用于加载模块。
npm与包
从 https://www.npmjs.com/ 网站上搜索自己所需要的包
从 https://registry.npmjs.org/ 服务器上下载自己需要的包
1.在项目中安装包的命令
npm intsall 包的完整名称
2.卸载包的命令
npm uninstall 包的完整名称
devDependencies节点
devDependencies
节点:包只在项目开发阶段会用到,在项目上线之后不会用到dependencies
节点:包在开发和项目上线之后都需要用到
//安装指定的包,并记录到devDependencies节点中
npm i 包名 -D
npm install 包名 --save-dev
解决下包慢
# 查看当前的下包镜像源
npm config get registry
# 将下包镜像源切换为淘宝镜像源
npm config set registry=https://registry.npm.taobao.org/
nrm
安装nrm
小工具,利用nrm
提供的终端命令,可以快速查看和切换下包的镜像源。
# 安装
npm i nrg -g
# 查看
nrm ls
# 切换
nrm use taobao
包的分类
1.项目包
被安装到项目的 node_modules
目录中的包,都是项目包。
项目包又分两类:
- 开发依赖包:被记录在
devDependencies
节点,只在开发期间用到 - 核心依赖包:被记录到
dependencies
节点,在开发期间和项目上线都会用到
npm i 包名 -D #开发依赖包
npm i 包名 -S #核心依赖包
2.全局包
在执行 npm install
命令时,如果提供了 -g
参数,则会把包安装为全局包。
全局包会被安装到 C:\Users\用户目录\AppData\Roaming\npm\node_modules
目录下。
npm i 包名 -g #全局安装指定的包
npm uninstall 包名 -g #卸载全局安装的包
注意:
- 只要工具性质的包,才有全局安装的必要性,因为它们提供了好用的终端命令。
- 判断某个包是否需要全局安装后才能使用,可以参考相关官方文档提供的使用说明。
模块的加载机制
优先从缓存中加载
模块在第一次加载后会被缓存。 这也意味着多次调用 require()
不会导致模块的代码被执行多次。
注意:不论是内置模块、用户自定义模块、还是第三方模块,它们都会优先从缓存中加载,从而提高模块的加载效率。
内置模块的加载机制
内置模块是由 Node.js
官方提供的模块,内置模块的加载优先级最高。
例如,require('fs')
始终返回内置的 fs
模块,即使在 node_modules
目录下有名字相同的包也叫做 fs
。
自定义模块的加载机制
使用 require()
加载自定义模块时,必须指定以 ./ 或 ../
开头的路径标识符。
在加载自定义模块时,如果没有指定 ./
或 ../
这样的路径标识符,则 node
会把它当作内置模块或第三方模块进行加载。
同时,在使用 require()
导入自定义模块时,如果省略了文件的扩展名,则 Node.js
会按顺序分别尝试加载以下的文件:
- 按照确切的文件名进行加载
- 补全
.js
扩展名进行加载 - 补全
.json
扩展名进行加载 - 补全
.node
扩展名进行加载 - 加载失败,终端报错
第三方模块的加载机制
如果传递给 require()
的模块标识符不是一个内置模块,也没有以 ./
或 ../
开头,则 Node.js
会从当前模块的父 目录开始,尝试从 /node_modules
文件夹中加载第三方模块。
如果没有找到对应的第三方模块,则移动到再上一层父目录中,进行加载,直到文件系统的根目录。
例如,假设在 'C:\Users\itheima\project\foo.js
' 文件里调用了 require('tools')
,则 Node.js
会按以下顺序查找:
- C:\Users\itheima\project\node_modules\tools
- C:\Users\itheima\node_modules\tools
- C:\Users\node_modules\tools
- C:\node_modules\tools
目录作为模块
当把目录作为模块标识符,传递给 require()
进行加载的时候,有三种加载方式:
- 在被加载的目录下查找一个叫做
package.json
的文件,并寻找main
属性,作为require()
加载的入口 - 如果目录里没有
package.json
文件,或者main
入口不存在或无法解析,则Node.js
将会试图加载目录下的index.js
文件。 - 如果以上两步都失败了,则
Node.js
会在终端打印错误消息,报告模块的缺失:Error: Cannot find module 'xxx‘
Express
官方概念:Express
是基于Node.js
平台,快速、开放、极简的web开发框架。
通俗理解:Express
的作用的和Node.js
内置的http模块
类似,是专门用来创建web服务器的。
Express
的本质:就是一个npm上的一个第三方包,提供了快速创建web服务器的便捷方法。
官网:http://www.expressjs.com.cn
使用Express
,我们可以方便、快速的创建web网站的服务器或API接口的服务器。
基本使用
1.安装
在项目所处的目录下,运行如下终端命令,即可将express
安装到项目中使用。
npm install express --save
2.创建基本的web服务器
//1.导入express
const express = require('express');
// 2.创建web服务器
const app = express();
// 3. 调用app.listen()端口号,启动成功后的回调函数,启动服务器
app.listen(80, () => {
console.log('expresss server running at http://127.0.0.1');
});
3.监听GET请求
通过app.get()
方法,可以监听客户端的GET
请求,具体的语法格式如下:
app.get('请求URL',function(req,res){/*处理函数*/})
- 参数1:客户端请求的URL地址
参数2:请求对应的处理函数
- req:请求对象,包含了与请求相关的属性和方法
- res:响应对象,包含了与响应相关的属性和方法
4.监听POST请求
通过app.post()
方法,可以监听客户端的post
请求,具体的语法格式如下:
app.post('请求URL',function(req,res){/*处理函数*/})
- 参数1:客户端请求的URL地址
参数2:请求对应的处理函数
- req:请求对象,包含了请求相关的属性和方法
- res:响应对象,包含了响应相关的属性和方法
5.把内容相关给客户端
通过res.send()
方法,可以把处理好的内容,发送给客户端
app.get('/user',(req,res)=>{
//向客户端发送JSON对象
res.send({name:'zs',age:20,gender:'男'})
})
app.post('/user',(req,res)=>{
//向客户端发送文本内容
res.send('请求成功')
})
6.获取URL中携带的查询参数
通过req.query()
对象,可以访问到客户端通过查询字符串的形式,发送到服务器的参数:
app.get('/',(req,res)=>{
//req.query默认是一个空对象
//客户端使用?name=zs&age=20 这种查询字符串形式,发送到服务器的参数
//可以通过req.query对象访问到,例如:
//req.query.name req.query.age
console.log(req.query)
})
7.获取URL中的动态参数
通过req.params
对象,可以访问到URL中,通过:
匹配到的动态参数
//URL地址中,可以通过:参数名的形式,匹配动态参数值
app.get('/user/:id',(req,res)=>{
//req.params默认是一个空对象
//里面存放着通过:动态匹配到的参数值
console.log(req.params)
})
托管静态资源
express
提供了一个非常好用的函数,叫做express.static()
,通过它,我们可以非常方便地创建一个静态资源服务器:
app.use(express.static('public'))
如此,便可以访问public
目录中的所有文件了。
注意:Express
在指定的静态目录中查找文件,并对外提供资源的访问路径。 因此,存放静态文件的目录名不会出现在 URL 中。
http://localhost:3000/images/bg.jpg
http://localhost:3000/css/style.css
如果要托管多个静态资源目录,多次调用express.static()
函数。
app.use(express.static('public'))
app.use(express.static('files'))
访问静态资源文件时,express.static()
函数会根据目录的添加顺序查找所需的文件。
如果希望在托管的静态资源访问路径之前,挂载路径前缀,则使用如下方式:
app.use('/public',express.static('public'))
这样,就可以通过带有public的前缀来访问public目录中的文件:
http://localhost:3000/public/images/bg.jpg
http://localhost:3000/public/css/style.css
nodemon
nodemon:这个工具,能够监听项目文件的变动,当代码被修改后,nodemon
会自动帮我们重启项目,极大方便了开发和调试。
全局安装:
npm install -g nodeman
使用nodeman:
#使用nodeman启动项目,即可实现自动重启项目效果
nodeman app.js
Express路由
在 Express
中,路由指的是客户端的请求与服务器处理函数之间的映射关系。
Express
中的路由分 3 部分组成,分别是请求的类型、请求的 URL 地址、处理函数,格式如下:
app.METHOD(PATH,HANDLER)
示例:
//匹配GET请求,且请求URL为/user
app.get('/user', (req, res) => {
res.send({ name: 'zs', age: 20, gender: '男' })
})
//匹配POST请求,且请求URL为/user
app.post('/user', (req, res) => {
res.send('请求成功')
})
每当一个请求到达服务器之后,需要先经过路由的匹配,只有匹配成功之后,才会调用对应的处理函数。
在匹配时,会按照路由的顺序进行匹配,如果请求类型和请求的 URL 同时匹配成功,则 Express 会将这次请求,转 交给对应的 function
函数进行处理。
路由匹配注意点:
- 按照定义的先后顺序进行匹配
- 请求类型和请求的URL同时匹配成功,才会调用对应的处理函数
路由的使用(模块化)
在Express
中使用路由最简单的方式,就是把路由挂载到app上:
const express = require('express')
//创建web服务器,命名为app
const app = express()
//挂载路由
app.get('/',(req,res)=>{ res.send('Helllo World.')})
//启动web服务器
app.listen(80,()=>{ console.log('server running at http://127.0.0.1')})
1.创建路由模块
// 这是路由模块router.js
// 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
2.注册路由模块
// app文件app.js
// 1. 导入路由模块
const router = require('./router.js')
// 2. 注册路由模块
app.use(router)
3.为路由模块添加前缀
类似于托管静态资源时,为静态资源统一挂载访问前缀一样:
// 1. 导入路由模块
const router = require('./03.router')
// 2. 注册路由模块,并加统一的访问前缀 /api
app.use('/api', router)
Express中间件
Express
的中间件,本质上就是一个 function
处理函数,Express
中间件的格式如下:
注意:中间件函数的形参列表中,必须包含 next
参数。而路由处理函数中只包含 req
和 res
。
next 函数
是实现多个中间件连续调用的关键,它表示把流转关系转交给下一个中间件或路由。
1.定义中间件函数
//常量mw所指向的就是一个中间件函数
const mw = function(req,res,next){
console.log('这是一个最简单的中间件函数')
//注意,在中间件的业务处理完之后,必选调用next()函数
next()//表示把流转关系交给下一个中间件或路由
}
2.全局生效的中间件
客户端发起的任何请求,到达服务器之后,都会触发的中间件,叫做全局生效的中间件。
通过app.use(中间件函数)
,即可定义全局生效的中间件。
//常量mw所指向的就是一个中间件函数
const mw = function(req,res,next){
console.log('这是一个最简单的中间件函数')
next()
}
//全局生效的中间件
app.use(mw)
3.定义全局中间件的简化形式
app.use(function(req,res,next){
console.log('这是一个最简单的中间件')
next()
})
4.中间件的作用
多个中间件之间,共享同一份 req
和 res
。基于这样的特性,我们可以在上游的中间件中,统一为 req
或 res
对象添加自定义的属性或方法,供下游的中间件或路由进行使用。
5.定义多个全局中间件
可以使用 app.use()
连续定义多个全局中间件。客户端请求到达服务器之后,会按照中间件定义的先后顺序依次进行 调用,示例代码如下:
const express = require('express')
const app = express()
// 定义第一个全局中间件
app.use((req, res, next) => {
console.log('调用了第1个全局中间件')
next()
})
// 定义第二个全局中间件
app.use((req, res, next) => {
console.log('调用了第2个全局中间件')
next()
})
// 定义一个路由
app.get('/user', (req, res) => {
res.send('User page.')
})
app.listen(80, () => {
console.log('http://127.0.0.1')
})
6.局部生效的中间件
不使用app.use()
定义的中间件,就是局部生效的中间件。
// 导入 express 模块
const express = require('express')
// 创建 express 的服务器实例
const app = express()
// 1. 定义中间件函数
const mw1 = (req, res, next) => {
console.log('调用了局部生效的中间件')
next()
}
// 2. 创建路由
//mw1 这个中间件只在当前路由中生效,属于局部生效的中间件
app.get('/', mw1, (req, res) => {
res.send('Home page.')
})
//mw1 不会影响下面这个路由
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')
})
7.定义多个局部中间件
可以在路由中,通过如下俩种等价方式,使用多个局部中间件:
//写法1
app.get('/', mw1, mw2, (req, res) => {
res.send('Home page.')
})
//写法2
app.get('/', [mw1, mw2], (req, res) => {
res.send('Home page.')
})
8.中间件的使用注意事项
- 一定要在路由之前注册中间件
- 客户端发送过来的请求,可以连续调用多个中间件进行处理
- 执行中间件的业务代码之后,不要忘记调用
next()
函数 - 未来防止代码逻辑混乱,调用
next()
函数后不要再写额外的代码 - 连续调用多个中间件时,多个中间件之间,共享
req
和res对象
中间件的分类
1.应用级别的中间件
通过 app.use()
或 app.get()
或 app.post()
,绑定到 app 实例上的中间件,叫做应用级别的中间件。
2.路由级别中间件
绑定到 express.Router()
实例上的中间件,叫做路由级别的中间件。它的用法和应用级别中间件没有任何区别。只不 过,应用级别中间件是绑定到 app 实例上,路由级别中间件绑定到 router
实例上。
const app = express()
const router = express.Router()
//路由级别中间件
router.use(function(req,res,next){
console.log('Time',Data.now())
next()
})
app.use('/',router)
3.错误级别中间件
作用:专门用来捕获整个项目中发生的异常错误,从而防止项目异常崩溃的问题。
格式:错误级别中间件的 function 处理函数中,必须有 4 个形参,形参顺序从前到后,分别是 (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)//打印错误信息
res.send('Error:' + err.message)//向客户端响应错误相关内容
})
// 调用 app.listen 方法,指定端口号并启动web服务器
app.listen(80, function () {
console.log('Express server running at http://127.0.0.1')
})
注意:错误级别的中间件,必选注册在所有路由之后!
4.Express内置的中间件
express.static
:快速托管静态资源的内置中间件express.json
:解析JSON格式的请求体数据express.urlencoded
:解析URL-encoded格式的请求体数据
//配置解析json格式数据的内置中间件
app.use(express.json())
// 配置解析表单中的 url-encoded 格式的数据
app.use(express.urlencoded({ extended: false }))
5.第三方的中间件
非 Express 官方内置的,而是由第三方开发出来的中间件,叫做第三方中间件。
在项目中,可以按需下载并配置第三方中间件,从而提高项目的开发效率。
注意:Express 内置的 express.urlencoded
中间件,就是基于 body-parser
这个第三方中间件进一步封装出来的
具体使用步骤:
- 运行
npm install body-parser
安装中间件 - 使用
require
导入中间件 - 调用
app.use()
注册并使用中间件
// 1. 导入解析表单数据的中间件 body-parser
const parser = require('body-parser')
// 2. 使用 app.use() 注册中间件
app.use(parser.urlencoded({ extended: false }))
6.自定义中间件
// 导入 express 模块
const express = require('express')
// 创建 express 的服务器实例
const app = express()
// 导入 Node.js 内置的 querystring 模块
const qs = require('querystring')
// 这是解析表单数据的中间件
app.use((req, res, next) => {
// 定义中间件具体的业务逻辑
// 1. 定义一个 str 字符串,专门用来存储客户端发送过来的请求体数据
let str = ''
// 2. 监听 req 的 data 事件
req.on('data', (chunk) => {
str += chunk
})
// 3. 监听 req 的 end 事件
req.on('end', () => {
// 在 str 中存放的是完整的请求体数据
// console.log(str)
// TODO: 把字符串格式的请求体数据,解析成对象格式
const body = qs.parse(str)
// 将解析出来的请求体对象,挂载为req.body属性
req.body = body
next()
})
})
app.post('/user', (req, res) => {
res.send(req.body)
})
// 调用 app.listen 方法,指定端口号并启动web服务器
app.listen(80, function () {
console.log('Express server running at http://127.0.0.1')
})
将自定义中间件封装为模块
//custom-body-parser.js模块
// 导入 Node.js 内置的 querystring 模块
const qs = require('querystring')
const bodyParser = (req, res, next) => {
// 定义中间件具体的业务逻辑
// 1. 定义一个 str 字符串,专门用来存储客户端发送过来的请求体数据
let str = ''
// 2. 监听 req 的 data 事件
req.on('data', (chunk) => {
str += chunk
})
// 3. 监听 req 的 end 事件
req.on('end', () => {
// 在 str 中存放的是完整的请求体数据
// console.log(str)
// TODO: 把字符串格式的请求体数据,解析成对象格式
const body = qs.parse(str)
req.body = body
next()
})
}
//向外导出自定义的中间件模块
module.exports = bodyParser
//使用自定义中间件模块代码.js
// 导入 express 模块
const express = require('express')
// 创建 express 的服务器实例
const app = express()
// 1. 导入自己封装的中间件模块
const customBodyParser = require('./custom-body-parser')
// 2. 将自定义的中间件函数,注册为全局可用的中间件
app.use(customBodyParser)
app.post('/user', (req, res) => {
res.send(req.body)
})
// 调用 app.listen 方法,指定端口号并启动web服务器
app.listen(80, function () {
console.log('Express server running at http://127.0.0.1')
})
使用Express写接口
1.创建基本的服务器(app.js)
// 导入 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('./apiRouter')
// 把路由模块,注册到 app 上
app.use('/api', router)
// 启动服务器
app.listen(80, () => {
console.log('express server running at http://127.0.0.1')
})
2.编写接口模块(apiRouter.js)
const express = require('express')
const router = express.Router()
// 在这里挂载对应的路由,编写GET接口
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.CRORS跨域资源共享
接口的跨域问题
GET 和 POST接口,存在一个很严重的问题:不支持跨域请求。
解决接口跨域问题的方案主要有两种:
CORS
(主流的解决方案,推荐使用)JSONP
(有缺陷的解决方案:只支持 GET 请求)
使用 cors 中间件解决跨域问题
cors
是 Express
的一个第三方中间件。通过安装和配置 cors
中间件,可以很方便地解决跨域问题。
使用步骤:
- 安装中间件
npm install cors
- 导入中间件
const cors = require('cors')
- 配置中间件 路由之前调用
app.use(cors())
4.JSONP接口
概念:浏览器端通过script
标签的 src 属性,请求服务器上的数据,同时,服务器返回一个函数的调用。这种请求数据 的方式叫做 JSONP
。
特定:
JSONP
不属于真正的Ajax
请求,因为它没有使用XMLHttpRequest
这个对象JSONP
仅支持GET
请求。
如果项目中已经配置了 CORS
跨域资源共享,为了防止冲突,必须在配置 CORS
中间件之前声明 JSONP
的接口。
否则 JSONP 接口会被处理成开启了 CORS
的接口。示例代码如下:
//优先创建JSONP接口,不会被处理称cors接口
app.get('/api/jsonp',(req,res)=>{})
//再配置cors中间件,后续所有接口都会被处理称cors接口
app.use(cors())
//这是一个开启了cors的接口
app.get('/api/get',(req,res)=>{})
使用jQuery
发起JSONP
请求
$('#btnJSONP').on('click', function () {
$.ajax({
type: 'GET',
url: 'http://127.0.0.1/api/jsonp',
dataType: 'jsonp',//表示要发起JSONP请求
success: function (res) {
console.log(res)
},
})
})
mysql数据库相关操作
node.js是之前学的,做一下笔记相当于复习了,操作mysql数据库参考前文:
2 条评论
在外面报班培训的笔记还是自学的?写的不错啊
自学呢,这是B站看的黑马的教程写的。