基于 nodejs 平台,快速、开放、极简的 Web 开发框架。
官方有脚手架程序express-generator,可以快速搭建express的程序。
如果需要安装,建议全局安装:
npm install express-generator -g
使用express-generator搭建:
express -h
-h, --help 输出使用方法
--version 输出版本号
-e, --ejs 添加对 ejs 模板引擎的支持
--hbs 添加对 handlebars 模板引擎的支持
--pug 添加对 pug 模板引擎的支持
-H, --hogan 添加对 hogan.js 模板引擎的支持
--no-view 创建不带视图引擎的项目
-v, --view 添加对视图引擎(view) 的支持 (ejs|hbs|hjs|jade|pug|twig|vash) (默认是 jade 模板引擎)
-c, --css 添加样式表引擎 的支持 (less|stylus|compass|sass) (默认是普通的 css 文件)
--git 添加 .gitignore
-f, --force 强制在非空目录下创建
搭建没有模板的项目:
express <项目名称> --no-view
自动创建目录和基本文件,但是不包含依赖包。
安装依赖包:
npm install
执行:
npm run start
node ./bin/www
nodemon
是一个适用于开发阶段的辅助工具,它能够自动检测文件(js、json)变化,自动重启项目。
安装
npm install nodemon -g
使用,只要用nodemon替代node就可以。
例如,启动express项目:
使用,只要用nodemon替代node就可以。
例如,启动express项目:
pm2
是重量级的辅助工具,除了具有nodemon的特性之外,还具有自动多线程、负载均衡、日志等等.......
安装:
npm install pm2 -g
启动项目:
pm2 start ./bin/www
查看所有管理中的项目列表:
pm2 list
操作项目:
启动一个项目
pm2 start
启动所有项目
pm2 start all
停止一个项目
pm2 stop
停止所有项目
pm2 stop all
重启一个项目
pm2 restart
重启所有项目
pm2 restart all
在pm2管理器中删除某个项目
pm2 delete
清除所有项目
pm2 delete all
启用监控:
pm2 start ./bin/www --watch
打开实时监控画面:
pm2 monit
创建项目目录,并执行初始化命令:
npm init -y
建立主文件(index.js);
安装express到项目中:
npm install express
在index.js中引入express;
const express = require('express');
创建一个express实例:
const app = express();
创建第一个接口(api):
app.get('/', function (req, res, next) {
res.send('你好世界!!!');
})
当用户访问网站根目录时,返回字符串。
最终启动服务器:
const port = 80;
app.listen(port, function () {
console.log('服务器启动,监听在' + port + '端口')
})
指定静态路径(网站根):
app.use(
express.static('./public')
)
要注意顺序问题,一般情况下,静态路径的指定要在路由之前:
app.use(
express.static('./public')
)
app.get('/index.html', function (req, res, next) {
res.send('你好!!!');
})
中间键是指,匹配到路径后要做事情的函数:
app.use('/admin',function(req.res,next){
next();
})
app.use(路径,回调函数);
路径省略或为'*',表示匹配任何路径。
app.use(function(req.res,next){
next();
})
app.use('*',function(req.res,next){
next();
})
回调函数中next表示继续向下匹配。
中间件用app调用,全局中间键,还可以在路由中使用,路由中间键
建立路由对象
const router = express.Router();
让express实例使用路由:
app.use('/users', router);
当用户请求地址是已/users开头的,则被router对象接收。
为router设置处理api:
router.get('/abc',function(req,res){
res.send('135');
})
表示当用户访问 http://127.0.0.1/users/abc ,向用户返回135。
通常,我们使用模块化的方式将不同的路由分别建立:
//在routers文件夹下建立indexRouter.js文件
const express = require('express');
const router = express.Router();
router.get('/abc', function (req, res) {
res.send('135')
})
module.exports = router;
我们在主文件中,引入并使用:
const indexRouter = require('./routers/indexRouter');
app.use('/users', indexRouter);
实际访问地址是:http://127.0.0.1/users/abc
正则匹配:
'ab?cd' ?匹配前面的字符{0,1} ?前出现的字符0-1次
'ab+cd' + 匹配前面的字符{1,} + 前面的字符最少一次
'ab*cd' * 不表示量词,ab和cd中间至少有一个字符,最多不限,字符可以是任意字符。
‘ab(cd)?e’ 小括号表示整体计算,小括号不能出现在开头。
可以直接使用正则表达式来匹配:
router.get(/abc$/,function(req,res){});
动态路由匹配:
router.get('/class/:classid/news/:newsid', function (req, res, next) {
res.send(`classid:${req.params.classid} , newsid:${req.params.newsid}`)
});
res.send() 通用方法,会根据参数类型自动设置返回头。
res.download() 参数是url,向客户端返回需要下载的文件。
res.end() 用来告诉客户端,当前访问已经结束。
res.json() 向客户端返回json类型的数据。
res.jsonp()向客户端以jsonp形式发送数据
res.redirect() 重定向
res.sendFile() 发送文件到浏览器(浏览器会解析)
res.sendStatus() 发送状态码
postman是一个专用于测试服务器api接口的程序。
Postmanhttps://www.postman.com/
动态路由传参:
//访问地址是http://127.0.0.1/api/1/2/3
router.get('/api/:a/:b/:c',function(req,res){
const {a,b,c} = req.params;
})
请求头传参:
url ?a=1&b=2
//请求的地址是http://127.0.0.1/api?a=1&b=2&c=3
router.get('/api',function(req,res){
const {a,b,c} = req.query;
})
请求体传参:
//请求的地址是http://127.0.0.1/api/123?x=1&y=2
router.get('/api/:p',function(req,res){
const {p} = req.params
const {x,y} = req.query;
const {a,b,c} = req.body;
})
crypto
crypto用来做加密的操作,可以使用md5、sha1等等很多方式进行加密
安装
npm install crypto
项目用,安装到项目(不安装到全局)。
使用:
const crypto = require('crypto');
//每个实例只能输出一次;
const jiami = crypto.createHash('sha1');
jiami.update('123123');
//输出16进制的密文
jiami.digest('hex')
multer
处理formData数据(multipart/form-data),用于上传文件。
安装
npm install multer
使用时,先注册:
const multer = require('multer');
生成实例并配置:
Multer 接受一个 options 对象,其中最基本的是 dest 属性,这将告诉 Multer 将上传文件保存在哪。如果你省略 options 对象,这些文件将保存在内存中,永远不会写入磁盘。
const upload = multer({
dest: 'upload/'
})
router.post('/upload', upload.single('file'), function (req, res) {
res.json(req.file)
})
multer不会自动生成目录,需要我们手动创建。
如果你想在上传时进行更多的控制,你可以使用 storage 选项替代 dest。Multer 具有 DiskStorage 和 MemoryStorage 两个存储引擎; 另外还可以从第三方获得更多可用的引擎。
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, '/tmp/my-uploads')
},
filename: function (req, file, cb) {
cb(null, file.fieldname + '-' + Date.now())
}
})
const upload = multer({ storage: storage })
destination:表示文件上传的目录,它可以是一个字符串(目录会自动创建)或函数(目录不会自动创建)。
filename:表示修改后的文件名,需要完整的文件名。
可以限制文件尺寸:
const upload = multer({
storage: storage,
limits: { fileSize: maxSize }
})
maxSize为字节单位。
如果上传的为多文件:
app.post('/upload', upload.array('files', 12), function (req, res, next) {
// req.files 是 `files` 文件数组的信息
// req.body 将具有文本域数据,如果存在的话
})
如果上传的表单元素不止一个,可以使用:
const cpUpload = upload.fields([{ name: 'avatar', maxCount: 1 }, { name: 'gallery', maxCount: 8 }])
app.post('/upload', cpUpload, function (req, res, next) {
// req.files 是一个对象 (String -> Array) 键是文件名,值是文件数组
//
// 例如:
// req.files['avatar'][0] -> File
// req.files['gallery'] -> Array
//
// req.body 将具有文本域数据,如果存在的话
})
如果你需要处理一个只有文本域的表单,你应当使用 .none()
:
app.post('/profile', upload.none(), function (req, res, next) {
// req.body 包含文本域
})
jsonwebtoken和express-jwt
登录状态保持,早期使用的方法是session:
当用户第一次访问网站时,网站在内存中生成一个session空间,并返回session-id(内存地址),内存地址写入客户端的cookie中。
弊端:如果客户端关闭cookie功能则无法保持登录状态。多个服务器之间无法共享session。
jsonwebtoken用于生成JWT字符串
express-jwt
用于将JWT字符串解析还原成JSON对象
安装:
npm install jsonwebtoken express-jwt
导入:
const jwt = require('jsonwebtoken')
const expressJWT = require('express-jwt')
定义秘钥:
const secretKey = 'hello';
在登录成功之后,调用 jwt.sign() 方法生成 JWT 字符串。并发送给客户端
const tokenStr = jwt.sign({ username: userinfo.username }, secretKey, { expiresIn: '30s' })
客户端收到后存储,在请求需要登录后才能访问的url中加入token:
xhr.setRequestHeader('Authorization','Bearer '+token);
服务器通过express-jwt组件来验证,可以全局验证(注册到全局中间键),也可以单独对一个或几个api做验证。
api做验证时:
const { expressjwt } = require('express-jwt');
router.get(
'/abc',
expressjwt({
secret: 'key',
algorithms: ['HS256']
}
),
function (req, res, next) {
res.json({
username: req.auth.username
})
});
1、路由中间键:
const { expressjwt } = require('express-jwt');
app.use(expressjwt({
secret: 'key',//密钥
algorithms: ['HS256']//jwt的加密算法
}).unless({ path: ['/users/login', /^\/images/] }))
secret:解密秘钥(与加密秘钥相同)。
express-jwt会自动将解密后的内容放入req.auth 。
如果解密失败,则会返回一个403错误。
登录:
判断是否登录成功:jsonwebtoken.sign()生成token。返回给客户端。客户端保存token。
通常,当token验证不通过时,客户端需要得到一个明确的指示,而不是一个简单的错误提示。
我们使用自定义中间键来拦截token错误,固定向客户端返回401错误。
app.use(function (err, req, res, next) {
if (err.name === "UnauthorizedError") {
res.status(401).send("invalid token...");
} else {
next(err);
}
});
app.js的全局中间键后。
通常与全局中间键写在一起
app.use(expressjwt({
secret: 'key',//密钥
algorithms: ['HS256']//jwt的加密算法
})
//排除,token验证时,排除'/users/login', '/users/reg'路由地址
.unless({ path: ['/users/login', '/users/reg'] }));
//拦截token错误,固定向客户端返回401错误,错误的内容可以自己定制。
app.use(function (err, req, res, next) {
if (err.name === "UnauthorizedError") {
res.status(401).send("invalid token...");
} else {
next(err);
}
});
用户登录成功后,需要保持登录状态。需要在服务端及客户端都记录一些数据来表示处于已登录状态。
http协议是短链接,访问结束后,访问状态消失,下次访问服务器时,服务器会将客户端当做全新的来看待。
曾经采用的解决方案:
cookie(session)方案
session(会话),application(应用)
会话状态,当我们访问服务器时,服务器会为我们本次访问单独建立一个作用域,访问结束(关掉与服务器的所有连接)时,作用域消失。例如:当我们要访问某个网站时,第一次打开网站的任意一个网页(请求)时,产生本次访问的会话状态,当前访问不结束,再次访问时,与上次的会话状态共享。直到所有的访问全部结束,会话状态结束。
登录成功时,服务器产生一个session,每个session都有一个独立的sessionID。服务器将这个sessionID和登录状态一起存储在客户端的cookie中。当客户端再次访问服务器时,服务器会尝试读取客户端的sessionID,看看在服务器中,这个sessionID对应的session是否还存在,如果不存在了,则表示登录超时,如果还存在,进一步读取登录状态,来判断是否还处于登陆中。
这种判断过程,对于单服务器比较完美,还要看客户端是否禁用了cookie。
改进的方案:token方案
1、当用户登录成功后,服务器生成一个加密的字符串,这个字符串包含三部分内容:第一部分是存储加密头信息、第二部分存储过期时间、第三部分是存储我们需要额外存储的信息。独一无二的。服务器对应的也会存储秘钥用于解密。
2、服务器将这个token发送给客户端,客户端存储这个token。
3、当客户端需要再次访问服务器时,在参数中将这个token发送给服务器。
4、服务器判断当前请求有没有附加token信息,如果没有,那就表示没有登录,如果有,则用秘钥解密token,得到过期时间,看当前时间是否处于过期时间,处于过期时间内则表示还处于登录状态,超出过期时间则表示登录超时。
在需要登录后才能访问的api中做登录验证。
客户端在发送请求(get、post)时,需要携带token(请求头)。
服务器接收到请求时,用express-jwt去获取客户端携带的token,解密(还原)。
是否可以还原,如果可以还原再判断过期时间。都通过则将加密的数据保存在req.auth中,next();
如果任何一步出错了,则不再继续,返回401错误。
验证时: