Express 简介
Express 是一个简洁而灵活的 Node.js Web应用框架, 提供一系列强大特性帮助你创建各种Web应用
Express内部还是使用的http模块实现服务器创建和监听, 对http模块进行了二次封装
严格上来说 express就是NodeJS的一个模块 直接下载安装
中文官网 express中文官网
使用
node下载express模块:
命令 :
初始化服务 : npm init -y
下载express: npm i express
使用的时候 引入 需要实例化一个express应用 使用应用对象创建各种服务
express初体验 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 const express = require ("express" );const app =express ();app.get ("/" ,(req,res,next )=> { res.end ("hello GET express" ); }) app.post ("/" ,(req,res,next )=> { res.end ("hello POST express" ); }) app.listen (3000 ,()=> { console .log ("server is running port is 3000" ); })
express中间件 中间件基本概念
Express是一个路由和中间件的Web框架,Express应用程序本质上是一系列中间件函数的调用;那么,中间件是什么呢?
中间件的本质是传递给express的一个回调函数;
这个回调函数接受三个参数:
请求对象(request对象);
响应对象(response对象);
next函数(在express中定义的用于执行下一个中间件的函数);
中间件中可以执行哪些任务呢?
执行任何代码;
更改请求(request)和响应(response)对象;
结束请求-响应周期(返回数据);
调用栈中的下一个中间件;
如果当前中间件功能没有结束请求-响应周期,则必须调用next()将控制权传递给下一个中间件功能,否则,请求 将被挂起。
app.use创建最普通中间件(对method和路由均没有限制)
使用app.use
注册一个最普通的中间件,因为没有对method
做限制,所以GET请求
与POST请求
都可以访问;因为没有对路由
做限制,所以所有路由
都可以访问;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 const express = require ("express" );const app = express ();app.use ((req,res,next )=> { console .log ("注册了第01个普通中间件" ); res.end ("over" ); }) app.listen (3000 , () => { console .log ("server is running port is 3000" ); })
app.use创建多个最普通中间件
app.use注册多个最普通的中间件,当发送请求的时候,虽然这几个中间件都可以匹配成功,但是只会执行第01个中间件;后面的即使匹配成功了,也不会执行;
即:中间件永远查找匹配成功的第一个中间件,要想别的匹配成功的中间件也执行,需要在上一个匹配成功的中间件执行next();
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 const express = require ("express" );const app = express ();app.use ((req, res, next ) => { console .log ("注册了第01个普通中间件" ); res.end ("over" ); }) app.use ((req, res, next ) => { console .log ("注册了第02个普通中间件" ); }) app.use ((req, res, next ) => { console .log ("注册了第03个普通中间件" ); }) app.listen (3000 , () => { console .log ("server is running port is 3000" ); })
那么如何执行多个中间件呢?
答:使用next()
调用栈中的下一个可以匹配成功
的中间件;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 const express = require ("express" );const app = express ();app.use ((req, res, next ) => { console .log ("注册了第01个普通中间件" ); res.end ("over" ); next (); }) app.use ((req, res, next ) => { console .log ("注册了第02个普通中间件" ); next (); }) app.use ((req, res, next ) => { console .log ("注册了第03个普通中间件" ); }) app.listen (3000 , () => { console .log ("server is running port is 3000" ); })
路由中间件
路径匹配中间件:对路由做限制,对method不做限制
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 const express = require ("express" );const app = express ();app.use ("/home" ,(req,res,next )=> { console .log ("home middleware 01" ); res.end ("home middleware" ); }) app.listen (3000 , () => { console .log ("server is running port is 3000" ); })
多个路由中间件同上,需要调用next()执行下一个匹配成功的中间件;
路由和方法都匹配的中间件
不使用app.use
即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 const express = require ("express" );const app = express ();app.get ("/home" ,(req,res,next )=> { console .log ("home path and method middleware 01" ); res.end ("home middleware" ); }) app.post ("/login" ,(req,res,next )=> { console .log ("login path and method middleware 01" ); res.end ("login middleware" ); }) app.listen (3000 , () => { console .log ("server is running port is 3000" ); })
连续注册中间件(koa不支持)
多个参数,每个参数都是回调函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 const express = require ("express" );const app = express ();app.get ("/home" , (req, res, next ) => { console .log ("home path and method middleware 01" ); res.end ("home middleware" ); }, (req, res, next ) => { console .log ("home path and method middleware 02" ); }, (req, res, next ) => { console .log ("home path and method middleware 03" ); }, (req, res, next ) => { console .log ("home path and method middleware 04" ); }) app.listen (3000 , () => { console .log ("server is running port is 3000" ); })
要想执行多个匹配成功的中间件,也还是调用next();
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 const express = require ("express" );const app = express ();app.get ("/home" , (req, res, next ) => { console .log ("home path and method middleware 01" ); res.end ("home middleware" ); next (); }, (req, res, next ) => { console .log ("home path and method middleware 02" ); next (); }, (req, res, next ) => { console .log ("home path and method middleware 03" ); next (); }, (req, res, next ) => { console .log ("home path and method middleware 04" ); }) app.listen (3000 , () => { console .log ("server is running port is 3000" ); })
POST请求body参数解析 中间件应用-json-urlencoded解析
注册express内置的body-parser
;使用req.body
接收参数;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 const express = require ("express" );const app = express ();app.use (express.json ()); app.use (express.urlencoded ({extended :true })); app.post ("/login" ,(req,res,next )=> { const info = req.body ; console .log (info); res.end ("使用req.body获取body参数" ); }) app.listen (3000 , () => { console .log ("server is running port is 3000" ); })
关于express.urlencoded({extended:true})中的extended:
true: 那么对urlencoded进行解析时,他使用的是第三方库:qs
false:那么对urlencoded进行解析时,他使用的是node内置模块:querystring
form-data解析:一般用于上传图片文件;
express框架没有内置的解析中间件,但是有官方的库multer
,需要下载安装一下:npm i multer
初步使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 const express = require ("express" );const app = express ();const multer = require ("multer" );const upload = multer ();app.use (upload.any ()); app.post ("/upload" ,(req,res,next )=> { console .log (req.body ); res.end ("用户上传成功" ); }) app.listen (3000 , () => { console .log ("server is running port is 3000" ); })
但是上方写法存在个问题:https://github.com/expressjs/multer/blob/master/doc/README-zh-cn.md 写着”永远不要将 multer 作为全局中间件使用,因为恶意用户可以上传文件到一个你没有预料到的路由,应该只在你需要处理上传文件的路由上使用;
如果你需要处理一个只有文本域的表单,你应当使用 .none()
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 const express = require ("express" );const multer = require ("multer" );const app = express ();const upload = multer ()app.post ("/upload" , upload.none (), (req, res, next ) => { console .log (req.body ); res.end ("用户上传成功~" ); }) app.listen (3000 , () => { console .log ("server is running port is 3000" ); })
文件上传
上传图片文件,并且将上传的图片文件放在服务器中某位置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 const express = require ("express" );const multer = require ("multer" );const app = express ();const upload = multer ({ dest : "./uploadImg/" }); app.post ("/upload" , upload.single ("avatar" ), (req, res, next ) => { console .log (req.file ); console .log (req.body ); res.end ("用户上传成功~" ); }) app.listen (3000 , () => { console .log ("server is running port is 3000" ); })
此时我们图片没有后缀,而且不能直接打开,我们给图片添加后缀和重命名
磁盘存储引擎 (DiskStorage):磁盘存储引擎可以让你控制文件的存储。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 const express = require ("express" );const multer = require ("multer" );const path = require ("path" );const app = express ();const storage = multer.diskStorage ({ destination : (req, file, cb ) => { cb (null , "./uploadImg/" ) }, filename : (req, file, cb ) => { cb (null , Date .now () + path.extname (file.originalname )); } }) const upload = multer ({ storage }); app.post ("/upload" , upload.single ("avatar" ), (req, res, next ) => { console .log (req.file ); console .log (req.body ); res.end ("用户上传成功~" ); }) app.listen (3000 , () => { console .log ("server is running port is 3000" ); })
这里使用diskStorage处理文件上传的地址和文件名,其中 destination可以为字符串也可以为函数,但是两者使用上有区别:
1 2 3 4 5 6 7 8 9 10 11 12 const storage = multer.diskStorage ({ destination :"./uploadImg/" , filename :(req,file,cb )=> { cb (null ,Date .now ()+path.extname (file.originalname )) } })
字符串,将根据字符串,创建文件夹;
回调函数中,传入的字符串表示文件放入的地址不会创建该文件夹(若没有的话);
上面是一次上传一张图片的情形,其实还有一次上传多张图片的情形;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 const express = require ('express' )const multer = require ('multer' )const upload = multer ({ dest : 'uploads/' })const app = express ()app.post ('/profile' , upload.single ('avatar' ), function (req, res, next ) { }) app.post ('/photos/upload' , upload.array ('photos' , 12 ), function (req, res, next ) { }) const cpUpload = upload.fields ([{ name : 'avatar' , maxCount : 1 }, { name : 'gallery' , maxCount : 8 }])app.post ('/cool-profile' , cpUpload, function (req, res, next ) { })
.single(fieldname):接受一个以 fieldname 命名的文件。这个文件的信息保存在 req.file。
.array(fieldname[, maxCount]):接受一个以 fieldname 命名的文件数组。可以配置 maxCount 来限制上传的最大数量。这些文件的信息保存在 req.files。
.fields(fields):接受指定 fields 的混合文件。这些文件的信息保存在 req.files;fields 应该是一个对象数组,应该具有 name 和可选的 maxCount 属性。
array和fields区别是:array上传的多张文件名的key都是一样的,而fields因为参数是数组对象,所以可以多种key,每个key又能限制count;
Example:
1 2 3 4 [ { name : 'avatar' , maxCount : 1 }, { name : 'gallery' , maxCount : 8 } ]
upload.any()不作为全局,作为局部中间件;也可以接收一次上传多张的情形,而且还不用指定key;更加自由;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 const express = require ("express" );const multer = require ("multer" );const path = require ("path" );const app = express ();const storage = multer.diskStorage ({ destination :"./uploadImg/" , filename : (req, file, cb ) => { cb (null , Date .now () + path.extname (file.originalname )); } }) const upload = multer ({ storage }); app.post ("/upload" , upload.any (), (req, res, next ) => { console .log (req.files ); console .log (req.body ); res.end ("用户上传成功~" ); }) app.listen (3000 , () => { console .log ("server is running port is 3000" ); })
GET请求参数解析 params解析(路由参数)
/products/:id/:name的这种的(http://127.0.0.1:3000/products/fsl/forward),用req.params解析
1 2 3 4 5 6 7 8 9 10 11 12 13 const express= require ("express" );const app = express ();app.get ("/products/:id/:name" ,(req,res,next )=> { console .log (req.params ); res.end ("商品的详情数据~" ) }) app.listen (3000 ,()=> { console .log ("server is running port is 3000~" ); })
query解析
/login这种的(http://127.0.0.1:3000/login?name=fsl&age=18),用req.query解析
1 2 3 4 5 6 7 8 9 10 11 12 13 const express= require ("express" );const app = express ();app.get ("/login" ,(req,res,next )=> { console .log (req.query ); res.end ("登陆成功~" ) }) app.listen (3000 ,()=> { console .log ("server is running port is 3000~" ); })
响应数据
res.end(xxx):只能返回 字符串、buffer数据等,不能返回 JSON,数组;
res.json(xxx):可以传入很多的类型:object、array、string、boolean、number、null等,它们会被转换成 json格式返回;
res.status(200):用于设置状态码
express路由
我们可以使用 express.Router()
来创建一个路由处理程序:一个Router实例拥有完整的中间件和路由系统;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 const express = require ("express" );const router = express.Router (); router.get ("/users" , (req, res, next ) => { res.json (["why" , "kobe" , "fsllala" ]); }) router.get ("/users/:id" , (req, res, next ) => { res.json (`${req.params.id} 用户的信息` ); }) router.post ("/users" , (req, res, next ) => { res.json ("create user success~" ); }) module .exports = router;
1 2 3 4 5 6 7 8 9 const express= require ("express" );const app = express ();const userRouter = require ("./router/user.js" )app.use (userRouter); app.listen (3000 ,()=> { console .log ("server is running port is 3000~" ); })
注册路由的时候,可以有两个参数 app.use(“/公共路由”,”路由”);上面的写法就可以写成下面这样:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 const express = require ("express" );const router = express.Router (); router.get ("/" , (req, res, next ) => { res.json (["why" , "kobe" , "fsllala" ]); }) router.get ("/:id" , (req, res, next ) => { res.json (`${req.params.id} 用户的信息` ); }) router.post ("/" , (req, res, next ) => { res.json ("create user success~" ); }) module .exports = router;
1 2 3 4 5 6 7 8 9 const express= require ("express" );const app = express ();const userRouter = require ("./router/user.js" )app.use ("/users" ,userRouter); app.listen (3000 ,()=> { console .log ("server is running port is 3000~" ); })
服务端错误处理
处理方式一:不易维护(后期修改code码/message信息);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 const express = require ("express" );const app = express ();app.use (express.json ()); app.use (express.urlencoded ({ extended : true })); app.post ("/login" , (req, res, next ) => { const { username, password } = req.body ; if (!username || !password) { res.json ({ code : -1001 , message : "请输入用户名或密码!" , }); } else if (username !== "fsllala" || password !== "123456" ) { res.json ({ code : -1002 , message : "用户名或密码错误!" , }); } else { res.json ({ code : 0 , message : "登录成功!" , token :"fasfasf123123aff" }); } }); app.listen (3000 , () => { console .log ("server is running~" ); });
处理方式二:通过next()传递给下一个匹配的中间件,其中next可以传递 错误信息 ;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 const express = require ("express" );const app = express ();app.use (express.json ()); app.use (express.urlencoded ({ extended : true })); app.post ("/login" , (req, res, next ) => { const { username, password } = req.body ; if (!username || !password) { next (-1001 ); } else if (username !== "fsllala" || password !== "123456" ) { next (-1002 ); } else { res.json ({ code : 0 , message : "登录成功!" , token :"fasfasf123123aff" }); } }); app.use ((err, req, res, next ) => { const code = err; let message = "未知错误" ; switch (err) { case -1001 : message = "请输入用户名或密码!" ; break ; case -1002 : message = "用户名或密码错误!" ; break ; } res.json ({ code, message }); }); app.listen (3000 , () => { console .log ("server is running~" ); });
参考文献 multer官方文档
Node中间件multer库学习