nodejs常用指令
esc ---清空输入的指令
cls ---清空已运行的命令
卸载包:npm uninstall 包名
npm i 包名 -D ---安装指定包名,并记录到devDependencies节点中----npm install 包名 --save-dev ---完整写法
npm config get registry ---查看当前的下包镜像源
npm config set registry=https://registry.npm.taobao.org/ ---将下包镜像源切为淘宝的
npm config get registry ---检查是否下载成功
安装nrm小工具,通过nrm的终端命,可以快速查看和切换下包镜像源
npm i nr, -g ---将nrm安装为全局可用(-g全局安装包)
nrm ls ---查看所有可用镜像源 ---需要使用时直接执行此命令即可(无论什么镜像源下)
nrm use taobao ---将镜像源切换为淘宝
npm i express@4.17.1 ---安装express并指定版本4.17.1
nodemon ---监听项目文件的变动,代码修改后nodemon回到帮我们重启项目,方便开发和调试
npm install -g nodemon ---全局安装
node app.js ---代码被修改后需要手动重启项目
nodemon app.js
npm install cors ---安装cors跨域资源共享
npm install mysql ---安装sql服务
安装JWT相关包
npm install jsonwebtoken express-jwt ---jsonwebtoken(生成JWT字符串),express-jwt(将JWt字符串解析还原成JSON对象)
const jwt = require('jsonwebtoken')
const expressJWT = require('express-jwt')
密码加密---bcryptjs
npm i bcryptjs@2.4.3
优化表单数据验证
npm install @hapi/joi@17.1.0 安装包为表单携带的每个数据项,定义验证规则
npm i @escook/express-joi ---安装中间件实现自动对表单数据进行验证功能
在验证模块中导入:const joi = require('@hapi/joi') / ('joi')---最新版
nodejs目录
bin:项目的启动文件,也可以放其他脚本。
node_modules:用来存放项目的依赖库。
public:用来存放静态文件(css,js,img)。
routes:路由控制器。
views:视图目录(相当于MVC中的V)。
app.js:项目入口及程序启动文件。
package.json:包描述文件及开发者信息。
-----------------------------------------------其他,自己创建使用------------------------------
models:数据模型(相当于MVC中的M)。
controllers:控制器,对请求的操作(相当于MVC中的C)。
tools:工具库。
config:配置目录。
test:测试目录。
README.md:项目说明文件。
package.json与package-lock.json文件的作用
package.json文件记录你项目中所需要的所有模块。当你执行npm install的时候,node会先从package.json文件中读取所有dependencies信息,然后根据dependencies中的信息与node_modules中的模块进行对比,没有的直接下载,已有的检查更新(最新版本的nodejs不会更新,因为有package-lock.json文件,下面再说)。另外,package.json文件只记录你通过npm install方式安装的模块信息,而这些模块所依赖的其他子模块的信息不会记录。
package-lock.json文件锁定所有模块的版本号,包括主模块和所有依赖子模块。当你执行npm install的时候,node从package.json文件读取模块名称,从package-lock.json文件中获取版本号,然后进行下载或者更新。
因此,正因为有了package-lock.json文件锁定版本号,所以当你执行npm install的时候,node不会自动更新package.json文件中的模块,必须用npm install packagename(自动更新小版本号)或者npm install packagename@x.x.x(指定版本号)来进行安装才会更新,package-lock.json文件中的版本号也会随着更新。
附:当package.json与package-lock.json都不存在,执行"npm install"时,node会重新生成package-lock.json文件,然后把node_modules中的模块信息全部记入package-lock.json文件,但不会生成package.json文件,此时,你可以通过"npm init --yes"来生成package.json文件
nodejs笔记
node.js运行环境---chrome v8引擎和内置API
node.js无法调用DOM,BOM
版本:LTS---稳定版,Current---测试版
查看node.js->打开终端(win+r=>cmd)->node -v
终端node.js运行js
cd js所在文件目录->node 文件名.js
shift +右键->powershell窗口
上箭头(pgup)定位到上一步运行的node
node 文件名局部字段+tab---自动补全
esc ---清空输入的指令
cls ---清空已运行的命令
ip地址用来定位计算机
端口号用来定会为具体的应该程序
所有联网通信软件都会站用一个端口号
端口范围是0-65536之间
同一台计算机中,同一个端口号只能被一个应用程序访问
fs---文件操作模块
http---网络服务后见模块
os---操作系统信息模块
path---路径处理模块
所有模块都需要手动`require`来手动加载后才能使用 ===var fs = require('fs')
不同的Content-Type不同,可参照:http://tool.oschina.net/commons
fs文件系统模块
const fs = require('fs') ---调用fs模块
文件读取
fs.readFile(path(*文件路径),options(编码格式-utf8),callback(*读取完成后回调函数拿到的结果))---读取文件中的内容*必选
//fs.readFile('./path/hello.js','utf8',function(err,dataStr){ })
读取成功 err为null ---err.message
读取失败 err值为错误对象,dataStr值为underfined
文件写入
fs.writeFile('./path/hello.txt','写入内容',function(err){})---可以创建文件,但不会创建文件夹,重复使用会新覆盖旧
写入成功 err为 null
写入失败 err为 错误对象
split(' ')---将数据按照空格分割
push() ---向数组末尾添加新项目,并返回新长度
replace('=',':')---将数据中的=换成:
join('\r\n')---将数据合并,数据间插入\r\n
动态拼接错误问题
原因:提供了./ 或 ../开头的相对路径,nodejs会自动拼接
解决:path直接写完整存放路径---\\
缺点:移植行差,不利于维护
最优解:fs.readFile(__dirname+'./path/hello.js')
__dirname---表示当前文件所处目录
path路径模块
const path = reuquire('path')
path.join()---将多个多个路径片段拼接成一个完整字符串
//const pathStr = path.join('/a','/b/c','../','./d','e') //\a\b\d\e---一个../抵消一层路径
//const pathStr2 = path.join(__dirname,'/path/hello.txt')---可用于readFile的path
path.basename()---获取文件路径最后一部分---从路径字符串中将文件名解析出来
path.extname(path)---获取路径中的文件扩展名
拆分html,css,js案例
const fs = require('fs')
const path = require('path')
const regStyleCSS = /<style>[\s\S]*<\/style>/ ----\s空白字符,\S非空白字符,*任意次
const regStyleScript = /<script>[\s\S]*<\/script>/
fs.readFile(path.join(__dirname,'../path/index.html'),'utf8',function(err,dataStr){
if(err) return console.log('读取HTML文件失败'+err.message)
resolveCSS(dataStr)
resolveJS(dataStr)
resolveHTML(dataStr)
})
//CSS
function resolveCSS(htmlStr){
const r1 = regStyleCSS.exec(htmlStr---变量名)----exec使用正则提取需要的内容
const newCSS = r1[0].replace('<style>','').replace('</style>','')---只要纯css样式
fs.writeFile(path.join(__dirname,'./path/index.css'),newCSS,function(err){
if(err) return console.log('写入CSS样式失败'+err.message)
console.log('写入样式文件成功')
})
}
//JS
function resolveJS(htmlStr){
const r1 = regStyleScript.exec(htmlStr---变量名)----exec使用正则提取需要的内容
const newJS = r1[0].replace('<script>','').replace('</script>','')---只要纯css样式
fs.writeFile(path.join(__dirname,'./path/index.js'),newJS,function(err){
if(err) return console.log('写入JS样式失败'+err.message)
console.log('写入样式文件成功')
})
}
//HTML
function resolveHTML(htmlStr){
const newHTML = htmlStr.replace(regStyleCSS,'<link rel="stylesheet" href="./index.css"/>').replace
(regStyleScript,'<script src="./index.js"></script>')
fs.writeFile(path.join(__dirname,'./path/index.html'),newHTML,function(err){
if(err) return console.log('写入HTML文件失败'+err.message)
console.log('写入HTML页面成功')
})
}
http模块---创建web服务器
ping 域名地址 ---查看网站ip
1.导入http模块:const http = require('http')
2.创建web服务器实例:const server = http.createServer()
3.为服务器实力榜的request事件,监听客户端的请求
server.on('request', (req,res)=>{ ---通过on()方法为服务器绑定一个request事件
console.log('Somone bisit our web server.') ---客户端请求服务器触发request事件
//req是请求对象,包含客户端的相关数据和属性
const url = req.url ---url地址
const method = req.method ---客户端请求的method类型(get/post/...)
const str = `Your request url is ${url}. and request method is ${method}`
//解决中文乱码
const strZ = `您请求的 url 地址是 ${req.url}. 请求的 method 类型是 ${req.method}`
res.setHeader('Content-Type','text/html:charset=utf-8')
res.end(str) ---向客户端发送内容并终止本次访问
})
4.启动服务器 ----server.listen(端口号,cb回调) ---端口号可自由设置
server.listen(80,()=>{
console.log('http server running ar htto://127.0.0.1')
})
4.5.根据不同的url响应不同的html内容---动态响应
a.获取请求的url地址
b.设置默认的响应内容为404 Not found
c.判断用户请求的是否是/或/index.html首页
d.判断用户请求是否为/about.html关于页面
e.设置Content-Type 响应头,防止中文乱码
f.使用res.end()把响应内容发送给客户端
server.on('request',function(req,res){
const url = req.url
let content = '<h1>404 Not found</h1>'
if(url === '/' || url === '/index.html'){
content = '<h1>首页</h1>'
}else if(url === '/about.html'){
content = '<h1>关于页面</h1>'
}
res.setHeader('Content-Type','text/html:charset=utf-8')
res.end(str)
})
web服务器实例
const fs = require('fs')
const path = require('path')
const http = require('http')
const server = http.createServer()
server.on('request', (req,res)=>{
const url = req.url
//const fpath = path.join(__dirname,url) ---把请求的url地址映射为具体的文件存放路径
let fpath = ''
if(url === '/'){
fpath = path.join(__dirname,'./path/index.html')
}else{
fpath = path.join(__dirname,'./path',url)
}
fs.readFile(fpath,'utf',(err,dataStr)=>{
if(err) return res.end('404 Not found')
res.end(dataStr)
})
})
server.listen(80,()=>{
console.log('http server running ar htto://127.0.0.1')
})
模块化
内置模块---fs,path,http(优先级最高,当自定义模块和内置模块名重复时,只会加载内置模块)
自定义模块
第三方模块 ---加载时需要加./或../ ( ---无扩展名下加载补全顺序(确切文件名->js->json->node->加载失败)
没有./或../情况下:require('tools')
C:\Users\itheima\project\node_modules\tools
C:\Users\itheima\node_modules\tools
C:\Users\node_modules\tools
C:\node_modules\tools
报错
require加载方式
1.在加载目录中寻找package.json文件,并找到main属性,作为require加载入口
2.如果目录没有package.json文件或者main入口不存在或无法解析,则Node.js会试图加载目录下的index.js文件
3.如果以上两步都失败,则Node.js在终端打印模块缺失:Error:Cannot find module 'xxx'
加载模块---requier()加载其他模块时,会执行被加载模块中的代码--无输出内容则输出{}
模块作用域好处---防止全局污染
module对象----储存了和当前模块有关的信息---在模块中输出console.log(module)
向外共享模块作用域的成员
module.exports.username = 'name'
module.exports.sayHello = function(){
console.log('hello')
}
module.exports = {
name:'name',
sayHello(){ ... }
}
----外界用require导入模块后则会指向module.exports,以json形式输出
简化---module.exports与exports对象指向相同---他们冲突时,最终都是module.exports所指向的(对象)
输出exports对象---module.exports = exports
两个均不是对象数据时不冲突,都会输出
CommonJS规范
a.每个模块内部,module变量代表当前模块
b.module变量是一个对象,他的exports属性(module.exports)是对外的接口
c.加载某个模块,施加在该模块的module.exports属性,require(方法用于加载模块)
第三方模块----包---npm
node.js的内置模块仅提供了底层API,包是基于内置模块封装出来的,提高开发效率
从https://www.npmjs.com/ 网站上搜索需要的包 ---全球最大包共享平台
在httos://registry.npmjs.org/ 服务器上下载需要的包
getFullYear(),getMonth(),getDate(),getHours(),getMinutes(),getSeconds()
安装包
npm install 完整包名 / npm i 完整包名
moment ---时间格式包名
const moment = require('moment')
const dt = moment().format('YYYY-MM-DD HH:mm:ss')
初次装包后会出现node_modules文件(存放所有已安装到项目中的包)和package-lock.json文件(记录node_modules目录下的每一个包下载信息)
npm install 会自动安装最新版本的包,npm i moment@2.22.2---通过@安装指定版本包
包的语义化版本规范 2.22.0 ---1位数大版本,2位数功能版本,3位数Bug修复版本
包管理配置文件,在项目根目录中,必须提供一个package.json的包管理配置文件
npm init -y ---新建package,json文件----项目目录中不能包含中文
package.json中的dependencies记载了安装的包(核心依赖包)
当把node_modules删除后,需要把所有的包下载到项目中才能运行起来
一次性安装所有包:npm install (或npm i)
卸载包:npm uninstall 包名
devDependencies节点----只在项目开发中会用到的包(开发依赖包)
npm i 包名 -D ---安装指定包名,并记录到devDependencies节点中----npm install 包名 --save-dev ---完整写法
包下载慢问题---从外网httos://registry.npmjs.org/ 服务器下载
淘宝NPM镜像(文件储存形式,一个磁盘上的数据在另一个磁盘上存在一个完全相同的副本即为镜像)服务器
npm config get registry ---查看当前的下包镜像源
npm config set registry=https://registry.npm.taobao.org/ ---将下包镜像源切为淘宝的
npm config get registry ---检查是否下载成功
安装nrm小工具,通过nrm的终端命,可以快速查看和切换下包镜像源
npm i nr, -g ---将nrm安装为全局可用(-g全局安装包)
nrm ls ---查看所有可用镜像源 ---需要使用时直接执行此命令即可(无论什么镜像源下)
nrm use taobao ---将镜像源切换为淘宝
全局包会被安装到:C:\Users\用户名\AppData\Roaming\npm\node_modules目录下
---工具性质的包,提供了终端命令--在npmjs.com/package/搜索可查看是否推荐全局安装
i5ting_toc---可以把md文档转为html页面的小工具
npm install -g i5ting_toc
i5tinng_toc -f 要转换的md文档路径 -o 或进入md文档所在目录:i5ting_toc -f 名.md -o
包的规范
以单独目录存在,
顶级目录下必须包含package.json这个包管理配置文件
package.json中必须包含name(名字),version(版本号),main(包的入口)三个属性
开发自己的包
a.新建itheima-tools文件夹,作为包的根目录
b.包下新建三个文件
i. package.json(包管理配置文件)
{
"name":"itheima-tools", ----需检查是否与已存在包名冲突
"version":"1.0.0", ----版本号
"main":"index.js", ---指定包的入口,require使用包时所指向的文件
"description":"提供格式时间,HTMLEscape的功能", ---用户在npmjs搜索本包时所能看到的提示信息
"keywords":["itheima","dateFormat","escape"], ---关键字
"license":"ISC" ---遵循开源属性
}
ii. index.js(包的入口文件)
function dateFormat(dateStr){
const dt = new Date(dateStr)
const y = dt.getFullYear()
const m = padZero(dt.getMonth()+1)
const d = padZero(dt.getDate())
const hh = padZero(dt.getHours())
const mm = padZero(dt.getMinutes())
const ss = padZero(dt.getSeconds())
return `{y}-{m}-${d} {hh}:{mm}:${ss}`
}
function padZero(n){
return n > 9 ? n:'0'+n
}
//转义html的方法---还原相反
function htmlEscape(htmlStr){
return htmlStr.replace(/<|>|"|&/g,(match)=>{ ---match匹配成功的字符,g表示全局配置
swith(match):
case '<':
return '$lt;'
case '>':
return '$gt;'
case '"':
return '$quot;'
case '&':
return '∓'
})
}
module.exports = {
dateFormat,
htmlEscape
}
iii. README.md(包的说明文档)
安装方式,导入方式,格式化时间,转义html特殊字符,,还原html特殊字符,开源协议
##安装
npm install itheima-tools
##导入
const itheims = require('itheima-tools')
。。。
模块化
在包文件ithema-tools文件下,新建包名src,在src下新建按功能划分的js文件,将原来index.js中的方法按照功能性划分并放入src下的对应js中
index.js
const date = require(./src/名1)
const date = require(./src/名2)
module.exports = {
...date, ---...展开运算符
...escape
}
发布包
1.注册npm账号
https://www.npmjs.com ->点击sign up ->进入注册页面
2.终端登录npm账号
npm login ----注意:运行npm前,必须把下包的服务器切换为npm官方服务器,否则会导致发布包失败 nrm ls ---查看当前服务器,nrm use 服务器 ---切换成指定服务器
依次输入用户名,密码(不会显示),邮箱
3.把终端切换到包的根目录下,运行npm publish 命令即可发布到npm上(包名不能和npm已有包同名)
4.删除已发布的包
npm unpublish 包名 --force ---只能删除72小时以内发布的包,且删除后24小时内不允许重复发布
express---与http模块(开发效率低)类似,用于搭建web服务器,是web框架
express可创建的服务器:
Web网站服务器:对外提供Web资源的服务器
API接口服务器:对外提供API接口的服务器
npm i express@4.17.1 ---安装express并指定版本4.17.1
const express = require('express') ---导入express
const app = express() ---创建web服务器
app.listen(80,()=>{ ---启动web服务器
console.log('express server running at http://127.0.0.1')
})
node app.js ---运行程序
监听get请求
app.get('请求URL',function(req,res) --req请求对象,res响应对象
res.send({ name: '', age:""}) --把内容响应给客户端
res.send(req.query) ---req.query获取客户端发送的参数,默认情况下,req.query是一个空对象{}
})
app.post('/user',(req,res)=>{
res.send('请求成功')
})
app.get('/user/:ids/:name',(req,res)=>{ ----http://127.0.0.1/user/12/hh
res.send(req.params) ---动态匹配参数值,默认值空对象{}
})
托管静态资源
app.use(express.static('./public')) ---express.static()---可以很方便的创建静态资源服务器
注意:express在指定的静态目录中查找文件,并对外提供资源访问路径,因此存放静态文件的目录名不能出现在URL中即上面的public--http://localhost:80/1.png
托管多个静态资源---连续调用express.static(),谁在前面,谁优先级越高
挂在路径前缀---在托管静态资源访问路径之前,关在路径前缀
app.use('./public',express.status('public')) ----http://localhost:80/public/1.png
nodemon ---监听项目文件的变动,代码修改后nodemon回到帮我们重启项目,方便开发和调试
npm install -g nodemon ---全局安装
node app.js ---代码被修改后需要手动重启项目
nodemon app.js
Express路由
路由指的是客户端的请求与服务端处理函数之间的映射管理
Express路由组成:请求类型,url地址,处理函数 ----格式:app.WETOD(PATH, HANDLER)
路由匹配过程:
每一个请求到达服务器之后,需要先经过路由的匹配,按照先后顺序匹配,前面成功,后面就不会进行
最简单的使用:app.get()/post() ---挂载到app上
模块化路由
抽离为单独模块的步骤:
a.创建路由模块对应的.js文件
b.调用express.Router()函数创建路由对象
c.向路由对象挂载具体的路由
d.使用module.exports向外共享路由对象
e.使用app.use()函数注册路由模块
var express = require('express')
var router = express.Router() ---创建路由对象
router.get('/user/list', function(req,res){
res.send('get user list')
})
router.post('/user/add', function(req,res){
res.send('add new user')
})
module.exports = router ---向外导出路由对象
const userRouter = require('./router/user.js') ---导入路由
app.use(userRouter) ---app.user()(注册全局中间件)---注册路由模块
app.use('/api',useRouter) ---为路由模块添加前缀
express中间件---本质就是一个function处理函数
多个中间件,共享同一人份req,res
当一个请求到达express的服务器后,可以连续调用多个中间件,从而对此次请求进行预处理
next函数,表示表达流转关系转交给下一个中间件或路由
const mv = function(req,res,next){
//当前中间件的业务处理完毕后,必须调用next()函数
next()
}
注册全局中间件
1.app.use(userRouter)
2.app.user((req,res,next)=>{
res.send('add new user')
const time = new Date()
req.starTime = time
nex()
})
局部生效中间件
const mm1 = (req,res,next) =>{
console.log('中间件')
next()
}
aoo.get('/', mm1 , (req,res)=>{ ---(.., mm1 , mm2, ...) ---可连续使用多个局部中间件
res.send('get into the page')
})
注意:中间件须在路由之前注册(除了错误级),要调用next()函数
中间件分类:
应用级:app.use()/app.get()/app.post()绑定到app实例上的中间件
路由级:express.Router()实例上榜的
错误级:(err,req,res,next)
app.get('/',function(req,res){
throw new Errow('服务器内部错误')
})
app.use(function(err,req,res,next){ --错误级中间件
console.log(req.body) ---req.body接受客户端发送的请求体数据,默认情况下,不配置解析表单数据的中间件,req.body默认等于underfined
res.send('Error:'+err.message)
})
Express内置中间件:
express.static---托管静态资源内置中间件(无兼容性)
express.json---解析JSON格式的请求数据(有兼容性-4.16.0+)---app.use(express.json())
express.urlencoded---解析URL-encoded格式的请求数据(有兼容性-4.16.0+)
---app.use(express.urlcoded({ extended:false})))---解析:application/x-www-for-urllencoded 格式数据的内置中间件
第三方中间件:
a. npm install body-parser ---安装中间件
b. const parser = require('body-parser')---导入中间件
c. app.use(parser.urlencode({ extended:false }))---注册并使用中间件----express.urlencoded-是基于parser封装的
自定义中间件:
1.定义中间件
app.use((req,res,next)=>{
//中间件业务逻辑
})
2.监听req的data事件
//数据很大时,客户端会将将数据分割分批发送到服务器,所以需要拼接
let str = '' ---存储客户端发送的数据体
req.on('data',(chunk)=>{
str += chunk
})
3.监听req的end事件
req.on('end',()=>{
console.log(str) ---str中存放的是完整的请求体数据
})
4.使用querystring模块解析请求体数据
const qs =require('querystring')
const body = qs.parse(str) ----parse(函数查询字符串并解析成对象格式)
5.解析出来的数据对象挂载为req.body
req.on('end/,()=>{
const body = qs.parse(str)
req.body=body
next()
})
6.服务器响应
app.posy('/user',(req,res)=>{
res.end(req.body)
})
7.将自定义中间件封装为模块
const qs = require('querystring')
function bodyParser(req,res,next){ ... }
module.express = bodyParser ---向外导出解析请求数据的中间函数
------------------分割先---------------
const myBodyParser = require('custom-body-parser') ---导入自定义模块
app.use(myBodyParser)
创建API路由模块
const express = require('express')
const apiRouter = express.Router()
module.exports = apiRouter
const apiRouter = require('./apiRouter.js')
app.use('/api',apiRouter)
编写GET接口
apiRouter.get('/get',(req,res)=>{
const query = req.query ---获取客户端发送到服务器的数据
res.send({
status:0, ---状态0成功,1失败
msg:'GET 请求成功',
data:qurey ---响应给客户端的具体数据
})
})
编写POST接口
apiRouter.post('/post',(req,res)=>{
const body = req.body
res.send({
status:0,
msg:'POST 请求成功'
data:body
})
})
在线jquery文件<script src="https://cdn.staticfile.org/jquery/3.4.1/jquery.min.js"></script>
接口跨域问题
a.CORS(跨域资源共享)
b.JSONP---支支持GET使用
解决:cors中间件
a. npm install cors ---安装
b. const cors = require('cors') ---导入
c. app.use(cors()) ---使用
指定Access-Control-Allow-Origin字段的值为通配符
res.setHeader('Access-Control-Allow-Origin','*')
CORS支持客户端向服务端发送的请求头:
Accept, Accept-Language, Content-Language, DRR, Downlink, Save-Data, Viewpot-Width, Width, Content-Type
值仅限于text/plain, multipart/foem-data, application/x-www-form-urlencoded三者之一
对额外的请求头声明:
res.setHeader('Access-Control-Allow-Headers','Content-Type,x-Custom-Header')
默认情况下,CORS仅支持客户端发起的GET, POST, HEAD请求(PUT, DELETE等方式需要Access-Control-Allow-Methods指明)
res.setHeader('Access-Control-Allow-Methods','POST, GET, DELETE, HEAD') 或 res.setHeader('Access-Control-Allow-Methods','*') *支持所有
预检请求---浏览器与服务器正是通信之前,浏览器会先发送OPTION进行预检,以获知服务器是否允许该实际请求。服务器成功响应预检请求后,才会发生真正请求并携带证实数据
只要符合一下任何一个条件都需要进行预检请求:
1.请求方式为GET, POST, HEAD之外的请求Method类型
2.请求中包含自定义头部字段
3.向服务器发送application/json格式的数据
JSONP---浏览器通过<srcpt>标签的src属性请求服务器上的数据,同时服务器返回一个函数调用的请求方式
1.JSONP不属于真正的Ajax请求,因为它没有XMLHttpRequest这个对象
2.JSONP仅支持GET请求
创建JSONP接口---若已配置CORS跨域资源共享,为防止冲突,必须在配置CORS中间件之前声明JSONP接口,否则JSONP接口会被处理成开启了CORS的接口
app.get('api/jsonp',(req,res)=>{ })
实现步骤:
a.获取客户端发送的回调函数的名字
b.得到要通过JSONP形式发送给客户端的数据
c.根据前两步得到的数据,拼接出一个函数调用的字符串
d.把拼接的字符串响应给客户端的<script>标签进行解析执行
app.get('api/jsonp',(req,res)=>{
const funcName = req.query.callback----a
const data = { name:'zs', age:22} ---b
const scriptStr = `{function}({JSON.stringify(data)})` ---c
res.send(scriptStr) ---d
})
mysql模块
npm install mysql ---安装
const mysql = require('mysql') ---导入
const db = mysql.createPool({ ---建立与mysql数据库的连接
host: '127.0.0.1', ----链接IP
user: 'root', ---用户名
password: 'admin123', ---链接密码
database: 'my_db_01' ---数据库名
})
//检测mysql模块是否能够正常工作
db.query('SELECT 1',(err,results)=>{
if(err) return console.log(err.message)
console.log(results)
})
插入数据
const user = { username:'Spider-Man' , password: '123456'}
const sqlStr = 'INSERT INTO user (username, password) VALUES (?, ?)'
db.query(sqlStr, [user.username, user.password], (err, results)=>{
if(err) return console.log(err.message) ---失败
if(results.affctedRows === 1){ console.log('数据插入成功')} ----成功
})
简易插入
const sqlStr = 'INSERT INTO users SET ?'
db.query(sqlStr, user, (err,results)=>{ ... })
web开发模式
1.基于服务端渲染的传统web开发模式
服务端向客户端发送HTML页面,是在服务器通过字符串的拼接,动态生成的,所有客户端无需使用Ajax请求页面的数据
2.基于前后端分离的新型web开发模式
Session认证机制----不能跨域访问
http协议的无状态性,指的是客户端的每次http请求都是独立的,连续多个请求之间没有直接的关系,服务器不会主动保留每次HTTP请求的状态
cookie:存储在用户浏览器中的一段不超过4kb的字符串,不同域名下的Cookie各自独立,当客户端发送请求时,会自动把当前域名下的所有未过期Cookie一同发送到服务器
cookie特点:自动发送,域名独立,过期时限,4kb限制
安装express-session中间件:npm install express-session
var session = require('express-session') ---导入
app.use(sessio({ ---配置中间件
secret: 'keyboard cat', ---secret属性值可以为任意字符串
resave: false, ---固定写法
saveUninitialized:true ---固定写法
}))
向seesion中存数据
app.post('/api/login',(req,res)=>{
req.session.user = req.body ---将用户信息存入session中
req.session.islogin = true ---将用户登录状态存入session中
res.send({ status:0, msg:'登录成功'})
})
从session中取数据
app.get('/api/username', (req,res)=>{
if(!req.session.islogin){
return res.send({ status:1, msg:'fial'})
}
res.send({
status:0,
msg: 'success',
username:req.session.user.username ,
})
})
清空session
app.post('/api/logout', (req,res)=>{
req.session.destroy()
res.send({
status:0,
msg:'退出登录'
})
})
JWT认证机制(跨域认证)
组成:Header(头部),Payload(有效荷载--用户信息经过加密之后生成的字符串),Signature(签名)---Header与Signature是为了保证Token的安全性
Header.Payload.Signature
客户端收到服务端返回的JWT之后,通常存储在localStorage/sessionStorage中--客户端每次与服务端通信后,会将JWT放在HTTP请求头的Authorization字段中
请求路径:http://ajax.fontend.itheina.net
以/api开头的请求路径不需要访问权限
以/my开头的请求路径,需要在请求头中获取Authorization身份字段认证
安装JWT相关包
npm install jsonwebtoken express-jwt ---jsonwebtoken(生成JWT字符串),express-jwt(将JWt字符串解析还原成JSON对象)
const jwt = require('jsonwebtoken')
const expressJWT = require('express-jwt')
定义secret密匙
安装cors中间件:npm i cors@2.8.5
1.当生成jwt字符串时,需要使用secret密匙对用户信息进行加密
const secretKey = 'itheima no1 ^_^'
2.当把JWT字符串解析还原成JSON对象的时候,需要使用secret密匙进行解析
app.use(expressJWT({ secret:secretKey}),unless({ path:[/^\/api\//] }))
登录成功后生成JWT字符串
调用jsonwebtoken包提供的sign()方法,将用户的信息加密成JWT字符串,响应给客户端
const tokenStr = jwt.sign({username: userinfo.username},secreKey,{expiresIn:'30s'})---第一个参数:用户信息对象; 第二个参数:加密的密匙; 第三个参数:配置对象,当前token的有效期
res.send({
status:200,
message:''登录成功,
token:tokenStr,
})
获取权限的API接口
app.get('.admin/getinfo',function(reqres)=>{
console.log(req.user) //报错可换成req.auth
res.send({
status:200,
message:'获取用户信息成功',
data:req.user //报错可换成req.auth
})
})
捕获解析JWT失败后产生的错误
app.use((err, req, res, next)=>{
if(err.name === 'UnauthorizedError'){
return res.send({ status:401, message: '无效token' })
}
res.send({ status:500 message:'未知错误' })
})
密码加密---bcryptjs
npm i bcryptjs@2.4.3
const bcryt = require('bcryptjs')
userinfo.password = bcrypt.hashSync(userinfo.password,10)
优化表单数据验证
npm install @hapi/joi@17.1.0 安装包为表单携带的每个数据项,定义验证规则
npm i @escook/express-joi ---安装中间件实现自动对表单数据进行验证功能
在验证模块中导入:const joi = require('@hapi/joi') / ('joi')---最新版