Node.js:Express 服务 & 路由
- 创建服务
- 处理请求
- req对象
- 静态资源托管
- 托管多个资源
- 挂载路径前缀
- 路由
- 模块化
Express
是Node.js
上的一个第三方框架,可以快速开发一个web
框架。本质是一个包,可以通过npm
直接下载。
创建服务
Express
创建一个服务器的流程非常简单:
- 导入
Express
包 - 实例化
Express
为一个服务 - 监听端口,开始服务
如下:
const express = require('express')
const app = express()
app.listen(80, () => {
console.log("创建web服务成功")
})
处理请求
当Express
被实例化为app
服务后,就可以通过这个服务来进行请求处理:
app.get('url', (req, res) => {
// ...
})
app.post('url', (req, res) => {
// ...
})
以上两个方法,分别处理get
和post
请求,触发回调函数,req
是请求信息,res
是响应信息,这和http
模块类似。
如果在处理请求时,需要向客户端发送消息,调用res.send
方法:
app.get('/login', (req, res) => {
res.send("hello Express")
})
运行服务后,访问127.0.0.1
就可以得到响应结结果:
req对象
在使用get
请求时,常会通过?
携带一些参数信息,这些参数可以通过req.query
获取到。
app.get('/login', (req, res) => {
res.send(req.query)
})
启动服务后,用户携带的参数存储在req.query
中,直接将这个对象发回给浏览器:
访问服务器时,携带了数据?name=zhangsan&age=20
,最后被服务器发送回来,以json
的格式展示在浏览器中。
处理通过?
携带的键值对形式参数,url
还可以携带动态参数/:id
,此处以:
表示后面是一个动态参数,这个路径可以匹配任意一个字符串。
如果需要得到这个动态参数,就需要req.params
对象,其存储了动态参数。
app.get('/user/:id', (req, res) => {
res.send(req.params)
})
在指定路径时,以:id
结尾,表示这个路径可以匹配一个动态参数,参数名为id
。动态参数存储在req.params
中,直接把这个对象返回给浏览器。
访问/user/:115599
:
req.params
拿到了动态参数id = 115599
。
匹配多个动态参数:
app.get('/user/:id/:name', (req, res) => {
res.send(req.params)
})
此处匹配两个动态参数id
和name
。
静态资源托管
当网站中有多个html
、css
、js
文件,此时如果一个个完成get
方法会很麻烦。为此express
提供了一个express.static
方法,它可以快速创建一个静态资源服务器,指定一个目录作为根目录,该目录下的所有文件都可以直接进行访问。
语法:
app.use(express.static(目录路径))
首先通过express.static
指定一个目录,此时该目录下的所有资源都可以被访问。再将返回值作为app.use
的参数传入,此时就完成了静态资源托管。
示例:
const express = require('express')
const path = require('path')
const app = express()
app.use(express.static(path.join(__dirname, "/root")))
app.listen(80, () => {
console.log("success!")
})
此处引入了一个path
模块,防止路径错误。express.static
指定同级目录下的/root
目录作为服务的根目录,该目录下有index.html
和style.css
两个文件。
在浏览器中访问这两个文件:
两个文件都可以直接访问,虽然没有写app.get("/root/index.html")
这样的方法,但是通过静态资源托管,就直接可以进行访问了。
托管多个资源
如果需要同时托管多个目录下的静态资源,可以多次执行app.use(express.static(目录路径))
。
app.use(express.static("./public"))
app.use(express.static("./files"))
这样public
和files
两个目录下的文件,都被托管了。浏览器请求文件时,按照托管的顺序查询,假设两个目录内部都有index.html
,由于public
先托管,浏览器会得到public/index.html
。
挂载路径前缀
在静态资源托管指定目录后,目录内部的文件可以直接被访问,无需指明该文件在哪一个目录下。
app.use(express.static("./public"))
比如以以上形式托管静态资源,最终访问服务器就以127.0.0.1/index.html
即可,public
目录不会出现在路径中,如果访问127.0.0.1/public/index.html
反而会出错,找不到路径。
如果希望用户访问静态资源时,要加上这个路径前缀,那么可以在app.use
时添加一个路径前缀。
app.use(`/public`, express.static("./public"))
此时就可以通过127.0.0.1/public/index.html
进行访问了。当然也不一定说前面这个路径前缀一定要和托管的目录名称相同,可以自己指定:
app.use(`/abc`, express.static("./public"))
这样就可以通过127.0.0.1/abc/index.html
访问资源。
之前说过,在多个目录被托管时,如果有同名文件,就会发生覆盖,只能访问到先托管那个目录下的文件。如果加上路径前缀,就可以解决这个问题:
app.use("/public", express.static("./public"))
app.use("/files", express.static("./files"))
想要访问两个不同的index.html
,只需要加上不同的前缀即可:
127.0.0.1/public/index.html
127.0.0.1/files/index.html
路由
Express
要为每一种类型的请求,都绑定一个函数来处理。当Express
收到一个请求,就会进行匹配,找到对应的函数,然后执行,这个过程就是路由
。
路由分为三部分:请求类型
、url
、处理函数
。
上图中有三个路由,其中第一个路由请求的地址是/
与后两者不同。虽然后两个路由请求的都是/index.html
,但是它们请求的方法不同。
当一个请求到来,Express
会进行路由匹配,找到请求类型
、url
都相同的函数,然后执行它。
当一个路由绑定了多个函数,只有第一个生效。
app.get('/', (req, res) => {
res.send("func1")
})
app.get('/', (req, res) => {
res.send("func2")
})
以上代码,为get /
绑定了两个函数,此时由于func1
先定义,只有func1
会生效。
模块化
先前的所有路由,都是直接挂载到app
这个对象上的,其实Express
并不建议这么做,而是建议将路由单独做成一个模块。
模块化路由流程:
- 创建一个新模块(
.js
文件) - 调用
express.Router
方法创建路由对象 - 往路由对象上挂载路由
- 使用
module.exports
向外共享路由 - 使用
app.use
注册路由模块
示例:
以下操作均在router.js
模块中。
创建路由对象:
const express = require('express')
const router = express.Router()
路由对象是express
的一个对象,专门用于挂载路由。
挂载路由:
router.get('/', function(req, res){
res.send("get / success")
})
router.post('/', function(req, res){
res.send("post / success")
})
router.get('/index.html', function(req, res){
res.send("get /index.html success")
})
此时直接把路由挂载到router
对象上,而不是app
对象上。
使用module.exports
向外共享路由:
module.exports = router
此时一个路由模块就制作完成了,要注意不能写为exports = router
,因为这样会导致exports
和module.exports
指向不同对象,最后没有成功共享router
。
在main.js
中导入模块:
const express = require('express')
const router = require('./router.js')
const app = express()
注册路由:
app.use(router)
直接将路由对象通过use
注册到app
中。
路由和静态资源托管一样,都是通过app.use
进行绑定的,那么路由自然也可以增加路径访问前缀:
app.use('/test', router)
这样访问路由内部的所有路径时,都要先加上前缀/test
。