目录
一、express是什么?
二、安装 express
三、安装Mysql
四、安装 nodemon 实现项目热更新
五、这里先了解下express的post get delete接口
post接口说明:
get接口说明 :
DELETE 接口
六、注册功能
1、流程分析
校验表单数据是否合法
检测用户名是否占用
密码加密处理
插入新用户
2、完整注册接口
regUser(req, res) 注册函数
七、封装错误处理函数(即:注册功能使用的res.cc)
八、登录功能
1、流程分析
1.判断前端提交的后端的数据是否合法。
2.查询登录的用户是否存在。
3.判断当前用户的密码是否正确。
2、生成token字符
1.安装jsonwebtoken用于生成token
2.导入jsonwebtoken
3.全局配置文件(里面有token的密钥)
3.使用jwt.sign对用户的信息进行加密,生成 token 字符串
4.登录成功后将生成的token返回给客户端
3、登录接口的完整代码
1.login()登录函数代码
九、最后附上users路由模块和登录注册函数.js的完整代码
users.js
login.js登录注册函数
接口文档:
十、解析token中间件
1.安装解析中间件
2.在App.js 中引入
3.注册全局中间件并解析token
4.注册全局错误中间件 当token失效时 返回信息
十一、CORS跨域中间件
1. 使用 cors 中间件解决跨域问题
完整代码:(jsonp就不做过多解释了)
1、CORS 响应头部 - Access-Control-Allow-Origin
2、CORS 响应头部 - Access-Control-Allow-Headers
3、CORS 响应头部 - Access-Control-Allow-Methods
4、 CORS请求的分类
5、 简单请求
6、预检请求
一、express是什么?
- Express 是一个简洁而灵活的 node.js Web应用框架, 提供了一系列强大特性帮助你创建各种 Web 应用,和丰富的 HTTP 工具。
- 使用 Express 可以快速地搭建一个完整功能的网站。
- Express 框架核心特性:
-
可以设置中间件来响应 HTTP 请求。
-
定义了路由表用于执行不同的 HTTP 请求动作。
-
可以通过向模板传递参数来动态渲染 HTML 页面。
二、安装 express
搭建Express项目有两种方式:
- 方式一:从零搭建自己的express应用结构
- 方式二:安装express-generator脚手架 一键生成express项目
在这里我们使用方式二快速构建一个express项目:
express-generator 是 Express 应用程序生成器工具,我们可以使用它来快速创建应用程序框架。
- 在项目文件夹下打开node终端
-
安装express-generator 脚手架
npm install -g express-generator
- 创建项目
express expressFrame (expressFrame 是项目名)
执行完后项目目录下的结构
注:bin/www 是启动入口文件,在里面可以设置端口号等
3. 安装依赖
npm install
4. 启动项目
npm start
此时在浏览器打开 http://localhost:3000/
出现以上页面,那么恭喜你express服务器已创建完成
三、安装Mysql
npm i mysql
1、新建db 数据库文件夹,文件夹下新建index.js 用来配置数据库信息,index.js内容如下
// 导入 mysql 模块
const mysql = require('mysql')
// 建立与 MySQL 数据库的连接
const db = mysql.createPool({
host: '127.0.0.1', // 数据库的IP地址
port: 3306, //数据库端口号
user: 'web2245321733', // 登录数据库的账号
password: 'web2245321733', // 登录数据库的密码
database: 'web2245321733' // 指定要操作哪个数据库
})
// 检测数据库是否连接成功
db.query("select 1", (err, results) => {
if (err) return console.log(err);
console.log(results, '数据库链接成功');
});
module.exports = db
此时重启项目,终端看到打印出数据库链接成功,
四、安装 nodemon 实现项目热更新
在刚才的添加数据库当中,我们发现每次修改代码都需要重启项目,非常麻烦
1、安装 nodemon 来监控 node.js 源代码的任何变化和自动重启你的服务器
npm install -g nodemon
2、在package.json中修改启动命令
然后重启项目,就可以了。
五、这里先了解下express的post get delete接口
post接口说明:
// 定义 POST 接口
router.post('/post', (req, res) => {
// 通过 req.body 获取请求体中包含的 url-encoded 格式的数据
const body = req.body
// 调用 res.send() 方法,向客户端响应结果
res.send({
status: 0, // 0 表示处理成功,1 表示处理失败
msg: 'POST 请求成功!', // 状态的描述
data: body, // 需要响应给客户端的数据
})
})
get接口说明 :
// 在这里挂载对应的路由
router.get('/get', (req, res) => {
// 通过 req.query 获取客户端通过查询字符串,发送到服务器的数据
const query = req.query
// 调用 res.send() 方法,向客户端响应处理的结果
res.send({
status: 0, // 0 表示处理成功,1 表示处理失败
msg: 'GET 请求成功!', // 状态的描述
data: query, // 需要响应给客户端的数据
})
})
DELETE 接口
// 定义 DELETE 接口
router.delete('/delete', (req, res) => {
res.send({
status: 0,
msg: 'DELETE请求成功',
})
})
参数说明:
- router.post 用于创建post接口 它有两个参数 参数1:路由匹配规则 参数2:请求的回调函数
- 回调函数的req参数:客户端请求信息,包括 请求头 请求参数等,req.bodey或req.query获取请求参数
- 回调函数的res参数:用于提交服务端的响应给客户端
- 调用 res.send() 方法,向客户端响应处理的结果
六、注册功能
1、流程分析
注册的一般流程 1.校验表单数据是否合法 2.检测用户名是否占用 3.密码加密处理 4.插入新用户
校验表单数据是否合法
// 通过 req.body 获取请求体中包含的 url-encoded 格式的数据
const userInfo = req.body
//【步骤一】对客户端的数据进行校验
if (userInfo.username == '' || userInfo.password == '') {
return res.send({
status: 1,
msg: '用户名和密码不能为空'
})
}
检测用户名是否占用
// 定义sql语句,查询用户名是否被占用
let sql = 'select * from ev_users where username=?'
db.query(sql, [userInfo.username], (error, result) => {
if (error) {
return res.cc(error)
}
if (result.length > 0) {
return res.cc('用户名已被占用!')
}
})
密码加密处理
安装bcryptjs 加密包 用于密码加密
npm i bcryptjs
引入加密包
// 导入 bcryptjs 加密包
const bcrypt = require('bcryptjs')
调用 bcrypt.hashSync() 对密码加密
// 调用 bcrypt.hashSync() 对密码加密
userInfo.password = bcrypt.hashSync(userInfo.password, 10)
说明:bcrypt.hashSync() 的参数1:要加密的密码 参数2: 加密等级 填10即可
插入新用户
前面我们已经连接过数据库了,这里我们直接引入数据库操作模块
// 导入数据库操作模块
const db = require('../../db/index')
定义插入新用户的 SQL 语句
// 定义插入新用户的 SQL 语句
let sql1 = 'insert into ev_users set ?'
调用 db.query() 执行 sql 语句 插入新用户 并给客户端返回结果
// 调用 db.query() 执行 sql 语句
db.query(sql1, {
username: userInfo.username,
password: userInfo.password
}, (error, result) => {
if (error) return res.cc(error)
// 判断影响行数是否为 1
if (result.affectedRows !== 1) return res.cc('注册用户失败!')
return res.cc('注册用户成功', 0, {
username: userInfo.username
})
})
2、完整注册接口
- 打开routes文件夹下的users.js 路由模块,添加以下内容
/**
* POST 用户注册
* @param username 用户名
* @param password 用户密码
*/
router.post('/add', (req, res, next) => {
// 通过 req.body 获取请求体中包含的 url-encoded 格式的数据
console.log(req.body)
const userInfo = req.body
//【步骤一】对客户端的数据进行校验
if (userInfo.username == '' || userInfo.password == '') {
return res.send({
status: 1,
msg: '用户名和密码不能为空'
})
}
// 【步骤二】执行定义好的注册函数
regUser(req, res)
});
regUser(req, res) 注册函数
在这里我单独新建了个login.js文件用于写注册和登录的处理的函数放
引入
// 导入写好的注册/登录函数
const {
regUser,
login
} = require('../public/javascripts/login')
regUser(req, res) 注册函数内容如下:
// 导入数据库操作模块
const db = require('../../db/index')
// 导入 bcryptjs 加密包
const bcrypt = require('bcryptjs')
/**
* POST 用户注册
* @param username 用户名
* @param password 用户密码
*/
exports.regUser = (req, res) => {
// 获取客户端提交到服务器的用户信息
const userInfo = req.body
// 定义sql语句,查询用户名是否被占用
let sql = 'select * from ev_users where username=?'
db.query(sql, [userInfo.username], (error, result) => {
if (error) {
return res.cc(error)
}
if (result.length > 0) {
return res.cc('用户名已被占用!')
}
// 调用 bcrypt.hashSync() 对密码加密
userInfo.password = bcrypt.hashSync(userInfo.password, 10)
// 定义插入新用户的 SQL 语句
let sql1 = 'insert into ev_users set ?'
// 调用 db.query() 执行 sql 语句
db.query(sql1, {
username: userInfo.username,
password: userInfo.password
}, (error, result) => {
if (error) return res.cc(error)
// 判断影响行数是否为 1
if (result.affectedRows !== 1) return res.cc('注册用户失败!')
return res.cc('注册用户成功', 0, {
username: userInfo.username
})
})
})
}
/**
* POST 登录的回调函数
* @param username 用户名
* @param password 用户密码
*/
exports.login = (req, res) => {
}
七、封装错误处理函数(即:注册功能使用的res.cc)
在注册功能里我对res.send向客户端响应函数做了处理,
在app.js中,放在路由前面
//封装错误处理函数
app.use((req, res, next) => {
res.cc = function(err, status = 1, data = {}) {
res.send({
status,
data,
message: err instanceof Error ? err.message : err
})
}
next()
})
八、登录功能
1、流程分析
登录的一般流程 1.判断前端提交的后端的数据是否合法。 2.查询登录的用户是否存在。 3.判断当前用户的密码是否正确。
1.判断前端提交的后端的数据是否合法。
// 通过 req.body 获取请求体中包含的 url-encoded 格式的数据
console.log(req.body)
const userInfo = req.body
//【步骤一】对客户端的数据进行校验
if (userInfo.username == '' || userInfo.password == '') {
return res.send({
status: 1,
msg: '用户名和密码不能为空'
})
}
2.查询登录的用户是否存在。
// 定义 SQL 语句
const sql = 'select * from ev_users where username=?'
// 执行 SQL 语句,根据用户名查询用户的信息
db.query(sql, userInfo.username, (err, result) => {
// 执行 SQL 语句失败
if (err) return res.cc(err)
// 执行 SQL 语句成功,但是获取的数据条数不为1 也是失败的
if (result.length !== 1) return res.cc('登录失败!')
// 经过上方俩条判断条件,则证明执行 SQL 成功
})
3.判断当前用户的密码是否正确。
使用 加密包的bcrypt.compareSync方法对比用户提交的密码和数据库中的密码是否一致,如果一直即:登录成功
// TODO :判断密码是否正确
const comRes = bcrypt.compareSync(userInfo.password, result[0].password)
if (!comRes) return res.cc('登陆失败')
2、生成token字符
密码正确的话,登录成功,根据用户信息生成唯一的token返回给客户端
1.安装jsonwebtoken用于生成token
npm i jsonwebtoken
2.导入jsonwebtoken
// 导入生成Token的包
const jwt = require('jsonwebtoken')
3.全局配置文件(里面有token的密钥)
config.js放在根目录
// 全局配置文件 config.js
module.exports = {
// 加密和解密 token 的密钥
jwtSecretKey: 'itheima No1. ^_^',
// token 有效期
expiresIn: '10h'
}
导入
// 导入全局配置文件(里面有token的密钥)
const config = require('../../config')
3.使用jwt.sign对用户的信息进行加密,生成 token 字符串
jwt.sign 有三个参数依次是 生成token的数据,加密的形式,token有效期
加密前先处理用户信息,将用户的敏感信息置空(如:密码等)
// 在服务器端生成 Token 字符串
const user = {
...result[0], // 解构用户信息
password: '', //密码等敏感信息置空
}
// 对用户的信息进行加密,生成 token 字符串
const tokenStr = jwt.sign(user, config.jwtSecretKey, {
expiresIn: config.expiresIn //tonken 有效期
})
4.登录成功后将生成的token返回给客户端
// 调用 res.send 将Token响应给客户端
res.send({
status: 0,
data: {
user: user,
token: 'Bearer ' + tokenStr,
},
message: '登录成功!!!',
})
3、登录接口的完整代码
/**
* POST 用户登录
* @param username 用户名
* @param password 用户密码
*/
router.post('/login', (req, res, next) => {
// 通过 req.body 获取请求体中包含的 url-encoded 格式的数据
// console.log(req.body)
const userInfo = req.body
//对客户端的数据进行校验
if (userInfo.username == '' || userInfo.password == '') {
return res.send({
status: 1,
msg: '用户名和密码不能为空'
})
}
// 执行定义好的登录函数
login(req, res)
});
1.login()登录函数代码
/**
* POST 登录的回调函数
* @param username 用户名
* @param password 用户密码
*/
exports.login = (req, res) => {
console.log('user', req.user);
// 接收表单的数据
const userInfo = req.body
// 定义 SQL 语句
const sql = 'select * from ev_users where username=?'
// 执行 SQL 语句,根据用户名查询用户的信息
db.query(sql, userInfo.username, (err, result) => {
// 执行 SQL 语句失败
if (err) return res.cc(err)
// 执行 SQL 语句成功,但是获取的数据条数不为1 也是失败的
if (result.length !== 1) return res.cc('登录失败!')
// 经过上方俩条判断条件,则证明执行 SQL 成功
// TODO :判断密码是否正确
const comRes = bcrypt.compareSync(userInfo.password, result[0].password)
if (!comRes) return res.cc('登陆失败')
// 在服务器端生成 Token 字符串
const user = {
...result[0], // 解构用户信息
password: '', //密码等敏感信息置空
}
// 对用户的信息进行加密,生成 token 字符串
const tokenStr = jwt.sign(user, config.jwtSecretKey, {
expiresIn: config.expiresIn //tonken 有效期
})
// 调用 res.send 将Token响应给客户端
res.send({
status: 0,
data: {
user: user,
token: 'Bearer ' + tokenStr,
},
message: '登录成功!!!',
})
})
}
九、最后附上users路由模块和登录注册函数.js的完整代码
users.js
var express = require('express');
var router = express.Router();
// 导入写好的注册/登录函数
const {
regUser,
login
} = require('../public/javascripts/login')
/* GET users listing. */
router.get('/', function(req, res, next) {
// 获取客户端提交到服务器的用户信息
const userInfo = req.body
if(req.user){
return res.cc('获取成功', 0, req.user)
}
// 获取到中间件的时间
res.send('GET 请求成功');
});
/**
* POST 用户注册
* @param username 用户名
* @param password 用户密码
*/
router.post('/add', (req, res, next) => {
// 通过 req.body 获取请求体中包含的 url-encoded 格式的数据
console.log(req.body)
const userInfo = req.body
//【步骤一】对客户端的数据进行校验
if (userInfo.username == '' || userInfo.password == '') {
return res.send({
status: 1,
msg: '用户名和密码不能为空'
})
}
// 【步骤二】执行定义好的注册函数
regUser(req, res)
});
/**
* POST 用户登录
* @param username 用户名
* @param password 用户密码
*/
router.post('/login', (req, res, next) => {
// 通过 req.body 获取请求体中包含的 url-encoded 格式的数据
// console.log(req.body)
const userInfo = req.body
//对客户端的数据进行校验
if (userInfo.username == '' || userInfo.password == '') {
return res.send({
status: 1,
msg: '用户名和密码不能为空'
})
}
// 执行定义好的登录函数
login(req, res)
});
/* 模板 */
// // 在这里挂载对应的路由
// 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;
login.js登录注册函数
// 导入数据库操作模块
const db = require('../../db/index')
// 导入 bcryptjs 加密包
const bcrypt = require('bcryptjs')
// 导入生成Token的包
const jwt = require('jsonwebtoken')
// 导入全局配置文件(里面有token的密钥)
const config = require('../../config')
/**
* POST 用户注册
* @param username 用户名
* @param password 用户密码
*/
exports.regUser = (req, res) => {
// 获取客户端提交到服务器的用户信息
const userInfo = req.body
// 定义sql语句,查询用户名是否被占用
let sql = 'select * from ev_users where username=?'
db.query(sql, [userInfo.username], (error, result) => {
if (error) {
return res.cc(error)
}
if (result.length > 0) {
return res.cc('用户名已被占用!')
}
// 调用 bcrypt.hashSync() 对密码加密
userInfo.password = bcrypt.hashSync(userInfo.password, 10)
// 定义插入新用户的 SQL 语句
let sql1 = 'insert into ev_users set ?'
// 调用 db.query() 执行 sql 语句
db.query(sql1, {
username: userInfo.username,
password: userInfo.password
}, (error, result) => {
if (error) return res.cc(error)
// 判断影响行数是否为 1
if (result.affectedRows !== 1) return res.cc('注册用户失败!')
return res.cc('注册用户成功', 0, {
username: userInfo.username
})
})
})
}
/**
* POST 登录的回调函数
* @param username 用户名
* @param password 用户密码
*/
exports.login = (req, res) => {
console.log('user', req.user);
// 接收表单的数据
const userInfo = req.body
// 定义 SQL 语句
const sql = 'select * from ev_users where username=?'
// 执行 SQL 语句,根据用户名查询用户的信息
db.query(sql, userInfo.username, (err, result) => {
// 执行 SQL 语句失败
if (err) return res.cc(err)
// 执行 SQL 语句成功,但是获取的数据条数不为1 也是失败的
if (result.length !== 1) return res.cc('登录失败!')
// 经过上方俩条判断条件,则证明执行 SQL 成功
// TODO :判断密码是否正确
const comRes = bcrypt.compareSync(userInfo.password, result[0].password)
if (!comRes) return res.cc('登陆失败')
// 在服务器端生成 Token 字符串
const user = {
...result[0], // 解构用户信息
password: '', //密码等敏感信息置空
}
// 对用户的信息进行加密,生成 token 字符串
const tokenStr = jwt.sign(user, config.jwtSecretKey, {
expiresIn: config.expiresIn //tonken 有效期
})
// 调用 res.send 将Token响应给客户端
res.send({
status: 0,
data: {
user: user,
token: 'Bearer ' + tokenStr,
},
message: '登录成功!!!',
})
})
}
到此我们的登录和注册接口已经实现,
接口文档:
注册:post http://127.0.0.1/users/add
请求参数:username:用户名
password:密码
登录:post http://127.0.0.1/users/login
请求参数:username:用户名
password:密码
十、解析token中间件
在刚刚我没完成了token的生成,现在我们做一个中间件用来解析token,来对用户进行身份认证
1.安装解析中间件
npm i express-jwt
2.在App.js 中引入
//token解析中间件 一定要在路由之前配置解析 Token 的中间件
const expressJWT = require('express-jwt')
//映入解密
const config = require('./config')
3.注册全局中间件并解析token
// 注册全局中间件 链式调用 unless 方法,接收一个配置对象,path 字段设置一个正则表达式,表示不需要 token 身份认证的路由前缀。
app.use(expressJWT({
// 加密时设置的密钥
secret: config.jwtSecretKey,
// 设置算法
algorithms: ['HS256'],
// 无token请求不进行解析,并且抛出异常
// credentialsRequired: false
}).unless({
path: [
'/users/add',
'/users/login',
{
url: /^\/public\/.*/,
methods: ['GET', 'POST']
}
]
// path: ['/users/login','/users']
}))
4.注册全局错误中间件 当token失效时 返回信息
// 错误中间件 当token失效时 返回信息
app.use((err, req, res, next) => {
if (err.name === 'UnauthorizedError') {
res.status(401).send({
status: 1,
data: {},
message: '身份认证失败!'
});
}
});
十一、CORS跨域中间件
注册登录接口写好了,但是使用的时候会有一个很严重的问题:不支持跨域请求。
解决接口跨域问题的方案主要有两种:
① CORS(主流的解决方案,推荐使用)
② JSONP(有缺陷的解决方案:只支持 GET 请求)
1. 使用 cors 中间件解决跨域问题
cors 是 Express 的一个第三方中间件。通过安装和配置 cors 中间件,可以很方便地解决跨域问题。
使用步骤分为如下 3 步:
① 运行 npm install cors 安装中间件
npm install cors
② 使用 const cors = require(‘cors’) 导入中间件
// 一定要在路由之前,配置 cors 这个中间件,从而解决接口跨域的问题
const cors = require('cors')
③ 在路由之前调用 app.use(cors()) 配置中间件
app.use(cors())
完整代码:(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)
})
// 一定要在路由之前,配置 cors 这个中间件,从而解决接口跨域的问题
const cors = require('cors')
app.use(cors())
注意事项:
①CORS 主要在服务器端进行配置。客户端浏览器无须做任何额外的配置,即可请求开启了CORS的接口。
②CORS在浏览器中有兼容。只有支持XMLHttpRequest Level2的浏览器,才能正常访问开启了CORS的服务端接口(例如:IE10+、Chrome4+、FireFox3.5+)。
1、CORS 响应头部 - Access-Control-Allow-Origin
如果指定了 Access-Control-Allow-Origin 字段的值为通配符 *,表示允许来自任何域的请求,示例代码如下:
res.setHeader('Access-Control-Allow-Origin','*')
2、CORS 响应头部 - 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 三者之一)
如果客户端向服务器发送了额外的请求头信息,则需要在服务器端,通过 Access-Control-Allow-Headers 对额外的请求头进行声明,否则这次请求会失败!
// 允许客户端额外向服务器发送 Content-Type 请求头和 X-Custom-Header 请求头
// 注意: 多个请求头之间使用英文的逗号进行分割
res.setHeader('Access-Control-Allow-Headers ''Content-Type X-Custom-Header')
3、CORS 响应头部 - Access-Control-Allow-Methods
默认情况下,CORS 仅支持客户端发起 GET、POST、HEAD 请求。
如果客户端希望通过 PUT、DELETE 等方式请求服务器的资源,则需要在服务器端,通过 Access-Control-Alow-Methods来指明实际请求所允许使用的 HTTP 方法。示例代码如下:
// 只允许 POST、GET、DELETE、HEAD 请求方法
res,setHeader('Access-Control-A1low-Methods','POST,GET,DELETE,HEAD')
// 允许所有的 HTTP 请求方法
res,setHeader('Access-Control-A1low-Methods','*')
4、 CORS请求的分类
客户端在请求 CORS 接口时,根据 请求方式和请求头的不同,可以将 CORS 的请求分为两大类,分别是:
① 简单请求
② 预检请求
5、 简单请求
同时满足以下两大条件的请求,就属于简单请求:
① 请求方式:GET、POST、HEAD 三者之一
② HTTP 头部信息不超过以下几种字段:无自定义头部字段、Accept、Accept-Language、Content-Language、DPR、Downlink、Save-Data、Viewport-Width、Width 、Content-Type(只有三个值application/x-www-form-urlencoded、multipart/form-data、text/plain)
6、预检请求
只要符合以下任何一个条件的请求,都需要进行预检请求:
① 请求方式为 GET、POST、HEAD 之外的请求 Method 类型
② 请求头中包含自定义头部字段
③ 向服务器发送了 application/json 格式的数据
在浏览器与服务器正式通信之前,浏览器会先发送 OPTION 请求进行预检,以获知服务器是否允许该实际请求,所以这一次的 OPTION 请求称为“预检请求”。服务器成功响应预检请求后,才会发送真正的请求,并且携带真实数据。
7.、简单请求和预检请求的区别
简单请求的特点:客户端与服务器之间只会发生一次请求。
预检请求的特点:客户端与服务器之间会发生两次请求,OPTION 预检请求成功之后,才会发起真正的请求。
2、最后附上app.js完整代码
// 导入 express
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');
// 引入路由模块
var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');
// 创建服务器实例
var app = express();
app.use(logger('dev'));
// 处理 application/json
app.use(express.json());
// 配置解析表单数据的中间件 处理 x-www-form-urlencoded
app.use(express.urlencoded({
extended: false
}));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
//token解析中间件 一定要在路由之前配置解析 Token 的中间件
const expressJWT = require('express-jwt')
//映入解密
const config = require('./config')
// 注册全局中间件 链式调用 unless 方法,接收一个配置对象,path 字段设置一个正则表达式,表示不需要 token 身份认证的路由前缀。
app.use(expressJWT({
// 加密时设置的密钥
secret: config.jwtSecretKey,
// 设置算法
algorithms: ['HS256'],
// 无token请求不进行解析,并且抛出异常
// credentialsRequired: false
}).unless({
path: [
'/users/add',
'/users/login',
{
url: /^\/public\/.*/,
methods: ['GET', 'POST']
}
]
// path: ['/users/login','/users']
}))
// 【必须在配置 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())
//定义第一个全局中间件
app.use((req, res, next) => { //res用于返回客户端 req客户端的请求参数 next() 提交给下一个中间件
// 只允许 请求方法
res.setHeader('Access-Control-Allow-Methods', 'POST, GET, DELETE,HEAD')
// 响应头 允许所有的 HTTP 请求方法
res.setHeader('Access-Control-Allow-Methods', '*')
// 如果指定了 Access-Control-Allow-Origin 字段的值为通配符 *,表示允许来自任何域的请求
res.setHeader('Access-Control-Allow-Origin', '*')
// res.setHeader("Access-Control-Allow-Headers", "content-type,Authorization");
next();
})
//封装错误处理函数
app.use((req, res, next) => {
res.cc = function(err, status = 1, data = {}) {
res.send({
status,
data,
message: err instanceof Error ? err.message : err
})
}
next()
})
// 错误中间件 当token失效时 返回信息
app.use((err, req, res, next) => {
if (err.name === 'UnauthorizedError') {
res.status(401).send({
status: 1,
data: {},
message: '身份认证失败!'
});
}
});
// 挂载路由
app.use('/', indexRouter);
/* 用户路由 */
app.use('/users', usersRouter);
module.exports = app;
跨域这部分和jsonp参考了以下文章 文章来源:https://www.toymoban.com/news/detail-672639.html
【nodejs-03】黑马nodejs学习笔记03-express中间件与跨域_简单长庚的博客-CSDN博客文章来源地址https://www.toymoban.com/news/detail-672639.html
到了这里,关于基于NodeJs+Express+MySQL 实现实现登录注册接口+token生成与解析验证+跨域-CORS的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!