express 是一个快速、极简的web开发框架,并且是一个同步框架。
koa 是异步框架。
安装express:
cnpm install --save-dev express
创建应用程序 Hello Word 代码如下:
//引入
const express = require('express');
//实例化
const app = new express();
let port = 8000;
let hostname = 'localhost';
//get请求
app.get("/", (req, res) => {
res.send('hello world!')
})
//监听端口
app.listen(port, hostname, () => {
console.log(`服务器运行在:http://${hostname}:${port}/`);
})
路由是指应用程序的端点(URI)如何响应客户端请求。
您还可以使用app.all()
处理所有HTTP
方法,并使用app.use()
将中间件指定为回调函数。
应用程序“侦听”与指定的路由 和 方法匹配的 请求,并且当它检测到匹配项时,将调用指定的回调函数。路由方法可以具有多个回调函数作为参数(中间件)。对于多个回调函数,重要的是提供next
回调函数的参数,然后next()
在函数体内调用,以将控制权移交给下一个回调。
get
浏览器使用 表单也可以
post
put
delete
表单使用
举例:
app.get('/get', (req, res) => {
res.send('get')
})
app.post('/post', (req, res) => {
res.send('post')
})
app.put('/put', (req, res) => {
res.send('put')
})
app.delete('/delete', (req, res) => {
res.send('delete')
})
路由中间件,也叫路由得全局守卫。所有类型的http
请求都会执行 all
。
中间件会向下执行,所以回调函数有一个next
方法。
//路径写 * 针对所有路由
app.all("*", (req, res, next) => {
console.log(req.url);
next();
});
//针对指定路由
app.all("/regest", (req, res, next) => {
console.log("注册路由");
next();
});
路由路径可以使用字符串,或者正则表达式;
特殊字符需要转义,比如$
需要写为 ([\$])
,或者对整个路径使用正则。
//路径 /data/$book
//只转义$
app.get("/data/([\$])book", (req, res) => {
res.send("book2");
});
//对整个路径使用正则
app.get(/data\/\$book/, (req, res) => {
res.send("book");
});
正则路径使用 ? + *
() 代表子表达式
//?匹配前边的子表达式0次或1次
app.get("/ab?c", (req, res) => {
res.send(req.url);
});
//+匹配前边的子表达式1次或多次
app.get("/a(ts)+f", (req, res) => {
res.send(req.url);
});
//*代表任意字符串 也可代表空
app.get("/a*b", (req, res) => {
res.send(req.url)
});
路由传值可以使用三种方式:
get传值、动态传值、post传值(要使用第三方中间件)。
(1)Get 路由传值
路径后边以?
来写 ,参数在req.query
上。
//路由的get传值 直接在路径后边 ?id=222&name=sss
app.get("/list", (req, res) => {
console.log(req.query);
res.send("get传值");
})
(2)动态路由传值
使用动态路由传值时,定义了就必须传值,否则匹配不到路径。
:形参
/ 传的值
动态路由传值在req.params
上。
//路由动态传值
app.get("/info/:id/:name", (req, res) => {
console.log(req.params);
res.send("动态传值")
});
app.get("/info/:id/list/:name", (req, res) => {
console.log(req.params);
res.send("动态传值2");
});
动态路由传值时可以使用连字符 - .
//动态传值里边的连字符 - .
app.get("/info/:id-:name-:class", (req, res) => {
console.log(req.params);
res.send('动态传值3');
});
app.get('/info/:id.:name', (req, res) => {
console.log(req.params);
res.send('动态传值4');
});
可以使用正则验证:
验证表达式直接写在动态参数后边
//路由动态传值 正则验证 验证表达式直接写在动态参数后边
app.get('/info/:id(\\d+)', (req, res) => {
console.log(req.params);
res.send("动态传值5")
})
Because the regular expression is usually part of a literal string, be sure to escape any \ characters with an additional backslash, for example
\\d+
.
什么是路由的线路处理?
线路—— 一个路由对应多个回调函数处理,使用next
方法来控制线路。
路由处理程序可以采用函数,函数数组或二者组合的形式
//采用函数
app.get("/example/a", (req, res, next) => {
console.log("fun1");
//next();
next('route');
}, (req, res) => {
console.log("fun2")
res.send("示例a")
});
//上边的路由处理程序中调用next('route') 会直接执行下边的处理程序
app.get("/example/a", (req, res) => {
res.send("直接进行相同路堆栈的下一组")
});
注意: 在路由处理函数程序中使用 next()
和 next('route')
的区别:
Next()
按照顺序走
Next(‘route’)
越过其余的路由回调 直接进行相同路由堆栈的下一组
//路由处理程序采函数数组和 函数表达式结合
let b1 = (req, res, next) => {
console.log("fun1");
next();
};
let b2 = (req, res, next) => {
console.log("fun2");
next();
};
app.get('/example/b', [b1, b2], (req, res) => {
console.log("fun3");
res.send('示例2')
});
路由的链式线路写法——连点:
app.get('/list1', (req, res, next) => {
console.log('111');
next();
}).get('/list1', (req, res) => {
res.send('路由线路式写法');
})
通过该方法,可以给同一路径 写多个请求方式。
app.route("/student")
.get((req, res) => {
res.send("1");
})
.post((req, res) => {
res.send("2");
})
什么是路由的模块化封装?
使用express.Router()
类创建模块化的,可安装的路由处理程序。
一个Router
实例是一个完整的中间件和路由系统;因此,它通常被称为“迷你应用程序”。
下边来封装一个简单的路由模块:
首先,新建routes.js
文件,在该文件中,将路由器创建为模块,在其中加载中间件功能、定义一些路由,并将该模块暴露出去。
(也可以将路由模块直接写在应用程序中,但推荐新建文件来写路由模块)
代码如下:
//引入express模块的Router类
const router = require('express').Router();
//路由中间件
router.all("*", (req, res, next) => {
console.log("中间件");
next();
});
//定义一些路由
router.get("/", (req, res) => {
res.send("home");
});
router.get("/login", (req, res) => {
res.send("login");
});
router.get("/stu", (req, res) => {
res.send("stu");
});
//将该路由模块暴露出去
module.exports = router;
接下来,就可以将封装好的路由器模块安装在主应用程序的路径上使用了:
在main.js
文件里,代码如下:
const express = require('express');
const app = express();
//引入封装好的路由模块
const routes = require('./routes')
let port = 8000;
let hostname = "localhost";
//app.use() 在指定路径上使用路由模块
//路由模块上的路径均为一级路径
app.use("/", routes, (req, res) => {
res.send("router");
});
//路由模块上的路径均为二级路径
app.use("/list",routes);
app.listen(port, hostname, () => {
console.log(`http://${hostname}:${port}/`);
})
注意:
app.use()
在指定路径上使用封装好的路由模块 。
参数:
指定的路径(一级路径, 二级路径等),
要使用的路由模块,
回调函数,在router.use()
路由级中间件里边使用next(‘router’)
时触发
Express是一个路由和中间件Web框架,其自身功能很少。Express应用程序本质上是一系列中间件函数调用。
中间件其实就是一个处理函数。
中间件功能可以执行以下任务:
执行任何代码。
更改请求和响应对象。
结束请求-响应周期。
调用堆栈中的下一个中间件函数。
Express应用程序可以使用以下类型的中间件:
应用层中间件
路由器级中间件
错误处理中间件
内置中间件
第三方中间件
将处理程序绑定到应用程序上的中间件。使用app.use()
关联。
//使用应用级中间件
//没有指定路径的中间件,每次应用收到请求时,都会执行该功能。
app.use((req, res, next) => {
console.log("应用级中间件1");
next();
})
//指定了路径的应用级中间件 针对该路径上任何类型的HTTP请求执行
app.use("/login",(req,res,next)=>{
console.log("应用级中间件2");
next();
})
注意:
要从路由器中间件堆栈中跳过其余中间件功能,请调用next('route')
将控制权传递给下一条路由。 注意:next('route')
仅在使用app.METHOD()
或router.METHOD()
函数加载的中间件函数中有效。
路由级中间件与应用程序级中间件的工作方式相同,只不过它绑定到实例express.Router()
。
使用router.use()
和router.METHOD()
函数加载路由器级中间件。
要跳过路由器的其余中间件功能,请调用next('router')
将控制权转回路由器实例。
//没有指定路径
router.use((req, res, next) => {
console.log("路由级中间件1");
if (req.query.id) {
next();
}
else {
next('router');
}
});
//制定了路径的路由级中间件
router.use('/', (req, res, next) => {
console.log("路由级中间件2");
next();
})
错误处理:
错误处理是指Express如何捕获和处理同步和异步发生的错误。Express带有默认的错误处理程序,因此无需自己编写即可开始使用。
捕获错误:
确保Express能够捕获运行路由处理程序和中间件时发生的所有错误,这一点很重要。
路由处理程序和中间件内部的同步代码中发生的错误不需要任何额外的工作。如果同步代码引发错误,则Express将捕获并处理该错误。
app.get('/', function (req, res) {
throw new Error('BROKEN') // Express will catch this on its own.
})
对于由路由处理程序和中间件调用的异步函数返回的错误,必须通过next()
将它们传递给下一个处理函数 ,使Express 能够捕获并处理它们。如下 :
app.get('/', function (req, res, next) {
fs.readFile('/file-does-not-exist', function (err, data) {
if (err) {
next(err) // Pass errors to Express.
} else {
res.send(data)
}
})
})
如果将任何内容传递给该next()函数(字符串除外’route’),Express都会将当前请求视为错误,并且将跳过所有剩余的非错误处理路由和中间件函数。
错误处理中间件:
next(error)
将错误传递给下一个处理函数之后,才会执行。可以在错误处理中间件里边做错误日志打印等。
在错误中间件中捕获错误,服务不会终止。
错误处理中间件始终带有四个参数。您必须提供四个参数以将其标识为错误处理中间件函数。即使您不需要使用该next对象,也必须指定它以维护签名。否则,该next对象将被解释为常规中间件,并且将无法处理错误。
错误处理中间件的用法示例:
const express = require('express');
const app = express();
const router = express.Router();
//在路由级中间件中返回一个错误
router.use((req, res, next) => {
if (false) {
next();
}
else{
let error=new Error("***异常!");
next(error);
}
});
//给路由模块定义一个路由
router.get("/", (req, res) => {
res.send('首页');
});
//使用路由模块
app.use(router);
//错误处理中间件 捕获上边返回的错误 可以在里边做错误日志打印等
app.use((err, req, res, next) => {
console.log(err.stack);
res.status(500).send("错误");
});
app.listen(8000, "localhost", () => {
console.log("http://localhost:8000/");
})
常用的内置中间件:
服务端的文件是不能通过静态路径来访问的,要使用静态路径访问,可以将他们放到静态资源目录里边。
express.static(root,[options])
提供静态文件,例如HTML文件,图像等。
root
参数指定要从其提供静态资产的根目录。该功能通过req.url与提供的root目录结合来确定要提供的文件。当找不到文件时,它不会发送404响应,而是调用next() 移至下一个中间件,从而可以进行堆栈和回退。
举例:
在当前项目下新建一个名为static
的文件夹,并将它设为静态资源目录:
app.use(express.static( "static"));
配置静态资源目录的虚拟目录:
app.use(express.static(__dirname + "/static"));
//配置虚拟目录
app.use('/assets', express.static(__dirname + "/static"));
配置
assets
为static
的虚拟目录之后,就可以从assets
中访问static
的文件了
express.json
它使用 body解析器 解析带有JSON负载的传入请求 。
It parses incoming requests with JSON payloads and is based on body-parser.
//设置bodyparser解析成json格式
app.use(bodyparser.json());
express.urlencoded
使用URL编码的有效内容解析传入的请求。
It parses incoming requests with urlencoded payloads and is based on body-parser
.
//设置bodyparser解析的编码格式
app.use(bodyparser.urlencoded({ extended: false }));
使用方法:
安装——引入——使用。
具体使用方法可以在npm
官网查看。
下边是几个常用的第三方中间件:
安装:
cnpm install --save-dev body-parser
引入:
const bodyparser=require('body-parser');
使用:
//设置
app.use(bodyparser.urlencoded({ extended: false }));
app.use(bodyparser.json());
//表单post提交
router.post('/login',(req,res)=>{
console.log(req.body);
})
注意:
如果没有
body-parser
中间件 ,那么表单post
提交时没有值的。
安装:
cnpm install --save-dev cookie-parser
引入:
const cookies=require('cookie-parser');
使用:
app.use(cookies());
注意:
使用签名缓存时,
app.use(cookies("sadds"))
要带参数,否则会报错:(cookieParser(“secret”) required for signed cookies)
写入缓存 使用示例:
//将post传值写入cookies缓存
router.post('/login',(req,res)=>{
console.log(req.body);
if(req.body.id){
//写入缓存
res.cookie("id",req.body.id.toString(),{
maxAge:1000*60*60*24*2,
signed:false
})
};
res.send("登录");
})
读取缓存 使用示例:
//读取未签名的缓存
console.log(req.cookies.id);
//读取签名缓存
console.log(req.signedCookies.id);
要使用 formidable
插件,必须在 form
标签上设置enctype
属性为 multipart/form-data
。比如:
<form action="/upload" method="POST" enctype="multipart/form-data">
<input type="file" name="file">
<button>上传</button>
</form>
安装:
cnpm install --save-dev formidable
引入:
const formidable = require('formidable');
使用:
在当前项目手动创建一个目录(以 upload 为例),用于存放上传的文件,开始使用该中间件上传:
router.post('/upload', (req, res) => {
//实例化formidable中间件
const form = formidable({ multiple: true });
//设置
form.encoding = 'utf-8';
form.uploadDir = __dirname + "/upload"; //指定文件上传的目录
form.keepExtensions = true; //保留后缀,不保留后缀文件格式乱
//使用
form.parse(req,(err,fields,files)=>{
console.log(fields); //form表单里的其它内容
console.log(files.file); //所上传的文件信息
res.send("上传");
})
});
如果要修改文件名称,将上边“使用”的代码改为:
form.parse(req, (err, fields, files) => {
//修改文件名称
let oldname = files.file.path;
let arr = oldname.split("\\");
fs.rename(__dirname + `/upload/${arr[arr.length - 1]}`, __dirname + `/upload/${files.file.name}`, (err) => {
if (err)
throw err;
})
res.send("上传");
})
安装:
cnpm install --save-dev ejs
引入:
const ejs = require("ejs");
修改ejs后缀为html (可以不修改):
app.engine(".html", ejs.__express);
设置模板引擎:
app.set("view engine", "html");
接下来就可以使用了:
const router = express.Router();
router.get('/', (req, res) => {
//使用ejs
res.render('index');
})
app.use(router);
上边
"index"
是views
目录下的 ejs 模板。