NodeJs+Express+MySQL 实现实现登录注册接口+token生成和验证+跨域-CORS
目录
五、这里先了解下express的post get delete接口
3.使用jwt.sign对用户的信息进行加密,生成 token 字符串
九、最后附上users路由模块和登录注册函数.js的完整代码
1、CORS 响应头部 - Access-Control-Allow-Origin
2、CORS 响应头部 - Access-Control-Allow-Headers
3、CORS 响应头部 - Access-Control-Allow-Methods
一、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参考了以下文章
这篇好文章是转载于:学新通技术网
- 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
- 本站站名: 学新通技术网
- 本文地址: /boutique/detail/tanhggggeg
-
photoshop保存的图片太大微信发不了怎么办
PHP中文网 06-15 -
《学习通》视频自动暂停处理方法
HelloWorld317 07-05 -
Android 11 保存文件到外部存储,并分享文件
Luke 10-12 -
word里面弄一个表格后上面的标题会跑到下面怎么办
PHP中文网 06-20 -
photoshop扩展功能面板显示灰色怎么办
PHP中文网 06-14 -
微信公众号没有声音提示怎么办
PHP中文网 03-31 -
excel下划线不显示怎么办
PHP中文网 06-23 -
excel打印预览压线压字怎么办
PHP中文网 06-22 -
TikTok加速器哪个好免费的TK加速器推荐
TK小达人 10-01 -
怎样阻止微信小程序自动打开
PHP中文网 06-13