基于Node.js平台,快速、开放、极简的web开发框架
1.下载
//下载4.17.1版本
npm install express@4.17.1
2.导入包并使用
const express = require('express')
const app = express()
app.listen(80,() => {
})
express中的路由是客户端请求和服务器处理函数之间的映射关系
浏览器访问:http://127.0.0.1/getData?name=‘123’&age=20
req.query
可以获取?name=‘123’&age=20访问时拼接的参数
//监听GET请求
//请求url /getData
app.get('/getData',function(req,res){
console.log(req.query) // { name: "'123'", age: '20' }
res.send({name:'胡',age:20, gender:'女'}) //页面显示{name:'胡',age:20, gender:'女'}
})
//监听POST请求
app.post('/getData',function(req,res){
res.send() // 把响应给客户端
})
使用:
动态匹配, req.params
获取动态参数
浏览器访问:http://127.0.0.1/getData/2
//动态匹配
app.get('/getData/:id',function(req,res){
console.log(req.params) // 2
res.send({name:'胡',age:20, gender:'女'}) //页面显示{name:'胡',age:20, gender:'女'}
})
浏览器访问:http://127.0.0.1/getData/2/hu
app.get('/getData/:id/:name',function(req,res){
console.log(req.params) // { id: '2', name: 'hu' }
res.send({name:'胡',age:20, gender:'女'}) //页面显示{name:'胡',age:20, gender:'女'}
})
路由匹配规则:
使用express.static()
存放静态资源的目录名不会出现在url中
托管多个静态资源目录时,根据目录的添加顺序查找所需的文件
app.use(express.static('public'))
app.use(express.static('./public2'))
// public 目录下的图片、文件资源都可以访问
// 访问public/images/bg.jpg http://127.0.0.1/images/bg.jpg
// 访问public/index.html http://127.0.0.1/index.html
在项目中我们通常需要定义的接口往往很多,所以需要把模块化路由。
.js
文件express.Router()
函数创建路由对象module.exports
向外共享路由//创建route.js文件
const express = require('express')
const router = express.Router()
router.get('/user/list',(req, res) => {
res.send('userlist')
})
router.post('/user/add',(req, res) => {
res.send('post')
})
module.exports = router
//启动文件中
const router = require('./route')
app.use(router)
中间件有输入输出。
express 可以连续调用多个中间件,从而对这次请求 请求预处理。
多个中间件之间,共享同一份req和res
我们可以在上游的中间件中,统一为req和res对象添加自定义的属性或方法供下游的中间件或路由使用。
const express = require('express')
const app = express()
//中间件
app.get('/',function(req,res,next){
req.time = Date.now
next()
})
//可以访问到req.time
app.get('/use',function(res,req){
console.log(req.time)
})
包含了next
参数回调函数就是中间件,未包含是路由处理函数
next
函数的作用:
方式一:
const mw = function (req,res, next) {
next()
}
app.use(mw)
方式二:
app.use(function (req,res, next) {
next()
})
不使用app.use()
就是局部中间件
// mw中件件参数
const mw = function (req,res, next) {
next()
}
app.get('/',mw,function(req,res){
// 中间件只在当前中作用
})
定义多个中间件 以下两种等价
app.get('/',mw1,mv2,function(req,res){
// 中间件只在当前中作用
})
app.get('/',[mw1,mv2],function(req,res){
// 中间件只在当前中作用
})
注意: 一定要在路由之前注册中间件,把中间件件放到路由后面 路由就无法使用
连续定义多个中间件,会安照定义的先后顺序依次调用。
官方把常见的中间件用法,分成了5大类分别是:
绑定到app实例上的中间件
app.use()
app.get()
app.post()
绑定到express.Router()
实例上的中间件
专门用来捕获整个项目中发生的异常错误,防止程序崩溃。必须有四个参数(err,req,res,next)
错误中间件必须注册在路由之后
三个常用的内置中间件:
express.static
托管静态资源文件express.json
json格式的请求体数据(有兼容性,仅在4.16.0+版本中可用)express.urlencoded
解析URL-encode格式的请求体数据(有兼容性,仅在4.16.0+版本中可用)//在路由之前配置
app.use(express.static('public'))
app.use(express.json())
app.use(express.urlencoded({extended:false}))
使用第三方中间件body-parser:res.body 获取JSON 格式表单数据和url-encoded格式的数据
//1.下载
npm install body-parser
//2.导入包
const parser = require('body-parser')
//3.app.use
app.use(parser.urlencoded({extended:false}))
使用插件,避免每次修改内容后,都需要收到启动服务器
//下载
npm install -g nodemon
//启动服务
dnoemon app.js //app.js 启动文件
协议、域名 、端口其中一个不同就会存在跨域
npm install cors
const cros = require('cors')
app.use(cors())
CORS跨域资源共享由一系列HTTP响应头组成,这些HTTP响应头决定。
浏览器是阻止 前端js代码跨域获取资源
CORS在浏览器中有兼容性,只支持XMLHttpRequest level2浏览器,才能正常访问开启CROS的服务器接口
Access-Control-Allow-Origin: | *
origin参数允许访问资源的外域URL// 只允许来自https://cn.vuejs.org/的请求
res.setHeader('Access-Control-Allow-Origin','https://cn.vuejs.org/')
//允许来自任何域的请求
res.setHeader('Access-Control-Allow-Origin','*')
如果客户端向服务器发送了额外的请求头信息,则需要通过Access-Control-Allow-Headers
对额外的请求头进行申明,否则这次请求失败
// 允许客户端额外向服务器发送请求头 Content-Type X-Custom-Header
res.setHeader('Access-Control-Allow-Headers','Content-Type', 'X-Custom-Header')
//多个请求头之间用英文逗号进行分隔
3.Access-Control-Allow-Methods
默认情况下COES仅支持客户端发起 GET、POST、HEAD
请求,PUT 、DELETE
需要 指明实际请求所允许使用的HTTP方法
//允许请求方式:GET POST HEADPUT DELETE
res.setHeader('Access-Control-Allow-Methods', 'GET', 'POST', 'HEADPUT', 'DELETE',)
//允许所有请求方式
res..setHeader('Access-Control-Allow-Methods','*')
普通请求 只发生一次请求 预检请求会发生两次
什么情况下会预检请求?
GET、POST、HEAD
之外的的请求类型自定义头部
字段application/json
格式的数据OPTION
请求进行预检,以获知服务器是否允许该实际请求 ,成功后才会发送真正的请求并携带真实数据浏览器通过标签的src属性,请求服务器上的数据同时服务器返回一个函数的调用
只支持GET
为了防止冲突 必须在配置CORS中间件之前申明JSONP的接口
字符串拼接出一个函数调用的字符串 在标签执行解析
app.get('/api/jsonp',(req,res) => {
const funcName = req.query.callback
const data = {}
const scriptStr = `${funcName}(){${JSON.stringify(data)}}`
res.send(scriptStr)
})
MySQL传统性数据库,MySQL是用来组织、存储和管理数据的仓库
下载mysql地址:https://dev.mysql.com/downloads/mysql/
下载后进行安装,会安装如下两个:
MySQL Server 专门用来提供数据存储和服务的软件
MySQL Workbench 可视化的MySQL管理工具
MySQL
数据库的第三方模块npm install mysql
MySQL
数据库const mysql = require('mysql')
const db = mysql.createPool({
host: '127.0.0.1', // 数据库的IP地址
user: 'root', // 登录数据库的账号
password: '123456', // 数据库的密码
database: 'my_database', // 指定操作哪个数据库
})
SQL
语句db.query('select 1', (err,results) => {
if (err) return console.log(err.message)
console.log(results)
})
操作数据前,先在数据库中建表,以下操作基于users表操作
const sqlStr = 'select * from users'
db.query(sqlStr , (err,results) => {
if (err) return console.log(err.message)
console.log(results)
})
const user = {username: 'hu1',passsowrd:'node123'}
const sqlStr = 'insert into users (username,passsowrd) values (?,?)'
db.query(sqlStr ,[user.username,user.passsowrd] ,(err,results) => {
if (err) return console.log(err.message)
if (results.affectedRows === 1) {
console.log('插入数据成功')
}
console.log(results)
})
便捷插入
const user = {username: 'hu2',passsowrd:'node456'}
const sqlStr = 'insert into users set ?'
// 数据对象的每一个属性和数据表字段一一对应
// 直接将数据对象当做占位符
db.query(sqlStr, user ,(err,results) => {
if (err) return console.log(err.message)
if (results.affectedRows === 1) {
console.log('插入数据成功')
}
console.log(results)
})
const user = {id:6,username: 'huq2',passsowrd:'node789'}
const sqlStr = 'update users set username=?,passsowrd=? where id=?'
db.query(sqlStr, [user.username,user.passsowrd,user.id] ,(err,results) => {
if (err) return console.log(err.message)
if (results.affectedRows === 1) {
console.log('更新数据成功')
}
console.log(results)
})
便捷更新数据
const user = {id:6,username: 'huq2',passsowrd:'node789'}
const sqlStr = 'update users set ? where id=?'
db.query(sqlStr, [user,user.id] ,(err,results) => {
if (err) return console.log(err.message)
if (results.affectedRows === 1) {
console.log('更新数据成功')
}
console.log(results)
})
id唯一标识
const sqlStr = 'delete from users where id=?'
db.query(sqlStr, 6 ,(err,results) => {
if (err) return console.log(err.message)
if (results.affectedRows === 1) {
console.log('删除数据成功')
}
console.log(results)
})
直接删除delete不能找回数据,所以在项目中最好使用标记删除,定义一个 status
删除状态,设置为1,就代表数据已删除
const sqlStr = 'update users set status=1 where id=?'
db.query(sqlStr, 6 ,(err,results) => {
if (err) return console.log(err.message)
if (results.affectedRows === 1) {
console.log('删除数据成功')
}
console.log(results)
})
第一种:基于服务器渲染的传统Web开发模式,服务发送给客户端的HTML页面,是在服务器通过字符串的拼接,动态生产的,不需要Ajax
额外请求页面数据
优点:
通过一定手段,完成对用户身份的确认
目的:确认当前申称为某种用户,确实是所声称的某种用户
前后端分离推荐使用Session
认证机制、JWT
认证机制
1.http协议的无状态性
是指客户端每次HTTP请求都是独立的,连续多个请求之间没有直接关系,服务器不会主动保留每次HTTP请求
2.如何突破HTTP无状态机制
使用Cookie。
Cookie
是存储在用户浏览器的一段不超过4kb
的字符串,它由一个名称、一个值和其他几个用于控制Cookie有效性、安全性、使用范围的可选属性组成
不同域名下的Cookie各自独立,每当客户端发起请求时
会自定把当前域名下所有未过期的cookie一同发送到服务器。
Cookie特征:自动发送、域名独立、过期时限、4kb限制
客户端第一次请求的时候,服务器通过响应头的形式,向客户端发送一个身份认证Cookie,客户端会自动将Cookie保存到浏览器。随后,当客户端浏览器每次请求服务器的时候,浏览器会将身份认证相关的cookie,通过请求头的形式发送给服务器,服务器级可验明客户端的身份
Cookie不具有安全性
由于cookie是存储在浏览器中的,而且浏览器也提供了读写Cookie的API,因此Cookie很容易被伪造
不建议服务器将重要的隐私数据,通过cookie形式发送给浏览器
提高身份认证的安全性,使用Session
认证机制
npm install express-session
app.use(session({
secret:'HUQIN', //任意字符串
resave: false, //固定写法
saveUninitialized:true //固定写法
}))
app.get('/login',(req,res) => {
// 配置session后可以使用req.session
if(req.query.password!=='123') {
}
req.session.user = req.query
req.session.islogin = true
res.send('登录成功')
})
app.get('/userData',(req,res) => {
if(!req.session.islogin){
return res.send('获取数据失败')
}
res.send('成功')
})
Session认证的局限性:Session认证机制
需要配合Cookie
才能实现,由于Cookie默认不支持跨域访问
,所以,当涉及到前端跨域请求后端接口的时候,需要做很多额外配置才能实现Session认证。
JWT认证机制最流行跨域认证解决方案
通常由三部分组成:
Header(头部)
Payload(有效载荷) 真正的用户信息
Signature(签名)
其它两个是安全性相关部分,只是为了保证Token的安全性使用.
进行分隔
Header.Payload.Siginature
客户端通常会把JWT存储在localSorage
或 sessionStorage
中,每次请求都有带上JW字符串 从而进行身份认证
推荐把jwt放在请求头中的Authorization字段后
Authorization:Bearer
使用JWT需要下载两个模
npm install jsonwebtoken express-jwt
jsonwebtoken 生产jwt字符串
express-jwt 将jwt字符串解析还原成JSON对象
const jwt = require('express-jwt')
const expressJWT = require('express-jwt')
const secreKey = 'xiaohuzi' //加密的key 可以是任意字符串
// 只有配置成功 就可以使用req.user
app.use(expressJWT({secret: secreKey}).unless({path:[/^\/api\//]}))// 使用正则配置api开头不需要访问权限
app.get('/login',(req,res) => {
//userInfo 模拟获取到的值
const userInfo = {username:'xiaohuzi'}
const tokenStr = jwt.sigin({username:userInfo.username},secreKey,{expiresIn:'30s'})
res.send({
status:200,
token:tokenStr
})
res.send('登录成功')
})