express

一、express介绍

利用nodejs中http创建的服务器,在处理请求的时候,只能放在一个回调函数中,且判断也相对较为复杂。为了方便基于nodejs服务器的开发,有人对nodejs服务器的创建、请求处理等操作,进行了封装,比较常见的就是express。

可以说,express就是基于nodejs封装的一个框架,专业用于提供方便易操作的web服务器。

Express 的本质:就是一个 npm 上的第三方包,提供了快速创建 Web 服务器的便捷方法。

使用Express开发框架可以非常方便、快速的创建Web网站的服务器或API接口的服务器。

  • 初体验

//下载安装
npm init -y
npm i express -S
//使用步骤:
//1.导入模块
const express=require('express')
//2.创建服务器
const app=express()
//3.监听端口号
app.listen(11018,()=>{console.log('server is running at http://localhost:11018')})
//4.处理请求
app.get(路由,回调)
二、路由

含义:请求路径跟响应内容之间的对应关系。

  1. 路由方法
  • 语法:app.路由方法(路径, 处理函数)

  • 路由方法有:

  • get:请求方式是get的,且请求路径为参数1,可以处理,只能处理一次

  • post:请求方式是post的,且请求路径为参数1,可以处理,只能处理一次

  • get请求比较方便,通过浏览器即可完成,但是post请求和后续的put请求以及delete请求,无法使用浏览器发送了,所以我们需要专门用于发送请求的软件(postman)或插件(postwomen)来调试请求

  • put:请求方式是put,且请求路径为参数1,可以处理,只能处理一次

  • delete:请求方式是delete,且请求路径为参数1,可以处理,只能处理一次

  • all:任意请求方法都可以处理,且请求路径为参数1的,都可以处理,只能处理一次

  • 处理get请求

  • 处理post请求

  • 处理put请求

  • 处理delete请求

  • use:任意请求方法都可以处理,且请求路径为参数1的,都可以处理,可以处理多次;可以省略参数1,表示任意路径的请求都会被处理;

  • 处理路径开头的请求:

//use:处理任意方式的请求 - 处理的路径可以是开头即可
// 请求路径只要是以  /a开头的就可以处理 
app.use('/a',(req,res)=>{
    res.end('aaaa')
})
  • 处理多次:处理请求的函数中,可以添加第三个参数next,调用next,表示这次处理结束后,会继续向下执行,再次匹配到后,会再次处理;

  • 处理任意路径的请求:

app.use('/a',(req,res,next)=>{
    console.log('总会被处理')
    //交给下一次清理
    next()
})

app.get('/',(req,res)=>{
    console.log('路径为/的get请求')
    res.end("a")
})
app.post('/abc',(req,res)=>{
    console.log('路径为/abc的post请求')
    res.end('b')
})
  1. 路径匹配
  • 完全匹配:请求路径必须跟定义好的路径完全相等,才会处理

//匹配根路径的请求
app.get('/',(req,res)=>{
    res.send('root')
})

//匹配/about路径的请求
app.get('/about',(req,res)=>{
    res.send('about')
})

//匹配/random.text路径的请求
app.get('/random.text',(req,res)=>{
    res.send('random')
})
  • 不完全匹配:请求路径能符合定义好的路径的规则即可处理

//匹配acd和abcd
app.get('/ab?cd',(req,res)=>{
    res.send('ab?cd')
})

//匹配abcd、abbcd、abbbcd等
app.get('/ab+cd',(req,res)=>{
    res.send('ab+cd')
})

//匹配abcd、abxcd、abEABDOOMcd、ab123cd等
app.get('/ab*cd',(req,res)=>{
    res.send('ab*cd')
})
  • 正则匹配:请求的路径能符合定义好的正则规则就可以处理

// 匹配任何路径中含有 a 的路径:
app.get(/a/, function (req, res) {
    res.send('/a/');
});

// 匹配 butterfly、dragonfly,不匹配 butterflyman、dragonflyman等
app.get(/.*fly$/, function (req, res) {
    res.send('/.*fly$/');
});
  • restful风格匹配

在实际项目开发中,为了能让路径中的参数写起来更简便,且更具有迷惑性,通常会采用动态路由方式进行传参。

动态路由传参,其实就是将参数当做路径一部分,不再使用?隔开。

也就是说,传递参数的路径,其实有两种写法,比如:

传统方式:http://localhost:3000/index.html?id=6&name=zhangsan

动态路由:http://localhost:3000/index.html/6/zhangsan

app.get('/about/:id',(req,res)=>{
    res.send("about")
})

//表示id可选项的话,使用?
app.get('/about/:id?',(req,res)=>{
    res.send("about")
})
  • 获取get请求参数

传统方式的路径参数获取,使用req.query

比如:请求地址 - http://localhost:3000/index.html?id=6&name=zhangsan

app.get('/index.html', (req, res) => {
    console.log(req.query);
    res.end('params')
})

动态路由传参的获取,使用req.params

app.get('/index.html/:id/:name', (req, res) => {
    console.log(req.params);
    res.end('params')
})
  1. 路由处理

处理某个请求路径的时候,可以使用一个函数,也可以使用多个函数,也可以使用多个函数组成的数组,也可以将数组和函数组合起来使用。

  • 单函数处理

app.get('/home', (req, res) => {
    res.end('home');
});
  • 多函数处理

app.get('/home', (req, res, next) => {
    console.log('处理完之后会交给下一个函数处理');
    next();
}, (req, res) => {
    res.end('home');
});
// 多个函数处理同一个请求的时候,多个函数共用同一个req
  • 函数数组处理

var cb0 = (req, res, next) => {
    console.log('CB0')
    next()
}
var cb1 = (req, res, next) => {
    console.log('CB1')
    next()
}
var cb2 = (req, res) => {
    console.log('CB2')
    res.end('home')
}
app.get('/home', [cb0, cb1, cb2])
  • 函数和数组混合处理

var cb0 = (req, res, next) => {
    console.log('CB0')
    next()
}
var cb1 = (req, res, next) => {
    console.log('CB1')
    next()
}
app.get('/home', [cb0, cb1], (req, res) => {
    res.end('home')
})
  • 路由对象处理路由

路由处理的时候,可以使用路由对象来代替函数处理,只是路由对象自己也要像app一样写具体的处理函数

const express = require('express')
const app = express()
// 创建路由变量
const router = express.Router() 
app.get(路由,router)
router.get(路由, (req, res) => {})
app.listen(端口号)
  • 路由对象配合use处理路由

use配合路由对象处理路由的时候,会将自己匹配到的路径截取掉,给router就剩下截取后剩下的路径

// 创建路由变量
const router = express.Router() 
// use方法跟router是黄金搭档
// 请求路径 : /abc/def   
// use交给router处理的时候,会将开头部分的路径截取掉
app.use('/abc', router) 
// router经过use处理后,自己处理的路径就剩下了  完整路径 - use的路径
router.get('/def', (req, res) => {
    res.end('home')
})
  1. 路由模块化

利用use方法和router的配合,我们可以将某一部分的请求单独封装到一个文件中,例如,所有有关用户的请求,都会是/user开头的请求路径,将所有有关用户请求的路由处理,单独封装在一个文件中。

//app.js
//1.导入模块
const express = require('express')
//2.创建服务器
const app = express()
//3.监听端口号
app.listen(11018, () => { console.log('server is running at http://localhost:11018') })
//4.处理请求
const userRouter = require('./user')
app.use('/user', userRouter)
const goodsRouter = require('./goods')
app.use('/goods', goodsRouter)

//user.js
const express = require('express')
const router = express.Router()
router.get('/add', (req, res) => {
    res.end('/user/add')
})
router.get('/edit', (req, res) => {
    res.end('/user/edit')
})
//导出
module.exports = router

//goods.js
const express = require('express')
const router = express.Router()
router.get('/add', (req, res) => {
    res.end('/goods/add')
})
router.get('/edit', (req, res) => {
    res.end('/goods/edit')
})
//导出
module.exports = router
三、响应方法
// req是请求对象
    // 请求方法 - req.method
    console.log(req.method);
    // 请求路径 - req.url
    console.log(req.url);
    // 请求的客户端ip - req.ip
    console.log(req.ip);
    // 请求头 - req.headers
    console.log(req.headers);
// 响应对象
    // 设置响应头
    //避免中文乱码的响应头
    res.setHeader('Content-Type', 'text/html;charset=utf8')
    res.end('这是我的服务器')   
    // 用一行代码代表上述两行代码 - res.send()
    res.send('这是我的服务器')
// json方法专门用于响应json数据
    res.json({
         name: '张三',
         age: 12
    })
// 跳转路由,重定向请求
res.redirect('/index.html')
// 读取页面并响应
// 内置变量  __dirname   代表当前文件所在文件夹的绝对路径
console.log(__dirname);
res.sendFile(__dirname + '/index.html')
res.sendFile(path.join(__dirname, 'index.html'))
// 设置响应状态码,并将其以字符串形式作为响应体的一部分发送
res.sendStatus(404)
四、中间件
  1. 概念

中间件是匹配路径后,在最终处理函数响应之前,先交给其他函数去处理一次,然后再交给最终的处理函数去响应,中间处理的函数就叫做中间件

  1. 中间件的分类
  • 根据中间件的开发者不同,可以将中间件分为3类:

  • 内置中间件 - express系统自带

  • 自定义中间件 - 自己开发的中间件

  • 第三方中间件 - 有人开发好了,我们下载下来使用

  • 根据中间件的使用情况,可以将中间件分为2类:

  • 应用级别中间件 - 由app调用的

  • 全局应用级别中间件 - 当前路由后面的所有请求都会经过这个中间件

  • 局部应用级别中间件 - 只在当前路由中有效

  • 路由级别中间件 - 由router调用的

  • 全局路由级别中间件

  • 局部路由级别中间件

  1. 中间件语法
//全局中间件的使用语法:
//应用级别使用
app.use(中间件函数)
//路由级别使用
router.use(中间件函数)

//局部中间件的使用:
//应用级别
app.路由方法(路径, 中间件,(req,res)=>{})
//路由级别
router.路由方法(路径, 中间件, (req,res)=>{})
  1. 内置中间件

express提供了好用的内置中间件。

  • 静态资源服务器中间件:express.static( ) - 必须是全局中间件

app.use('路径',express.static('托管目录地址'))
  • 获取post请求传递的请求主体:express.urlencoded() - 可全局可局部

app.use(express.urlencoded({extended: false}))

此后的post请求都可以通过req.body获取到post请求传递的请求主体了。

这个中间件有版本要求:express >= 4.16.0

  1. 自定义中间件
  • 自定义中间件,本质上是一个函数,相当有我们使用多个函数处理请求的时候,中间经历的函数。

let getPostData = (req, res, next) => {
    // 自己需要定义的逻辑流程
    // 中间件最后一定要执行此函数,否则程序无法向下执行下去
    let str = ''
    req.on('data', chunk => {
        str += chunk.toString()
    })
    req.on('end', () => {
        req.postData = qs.parse(str)
        next()
    })
}
//使用:
// 全局应用级别中间件
app.use(myMiddleware) 
// 局部应用级别中间件
app.get(路径, myMiddleware, (req, res) => {}) 

app.use(路径,路由)
// 全局路由级别中间件
router.use(myMiddleware) 
// 局部路由级别中间件
router.post(路径, myMiddleware, (req, res) => {}) 
  1. 第三方中间件

很多功能都可以通过别人写好的插件来实现,例如:body-parser;

body-parser这个中间件的功能是用来获取post请求传递的请求主体的。

//下载
npm i body-parser
//导入
const bodyParser=require('body-parser')
//使用中间件
app.use(bodyParser.urlencoded({extended:false}))
app.post('/',(req,res)=>{
    res.send(req.body)
})
  1. 异常处理中间件

当我们在某次请求处理的时候,发生了错误,会给客户端抛出报错,这个报错会让用户有不好的体验感,也容易将自己服务器所在的文件目录结构暴露。为了能解决以上两个缺点,我们可以在发生错误的时候,给用户以比较友好的自定义内容响应。这就是异常处理中间件。

app.get('/',(req,res) => {
    throw new Error('服务器内部发生了致命的错误!')
    res.send('Welcome to my homepage')
})

app.use((err,req,res,next) => {
    // err.message代表具体的错误信息
    console.log('发生了错误:' + err.message) 
    res.send('Error!' + err.message)
})

//手动抛出错误语法
throw new Error('自定义错误信息') 
  1. 404中间件

当用户发起了一个服务器并没有处理的请求,此时会在页面中输出404,会给用户造成不好的体验感,为了能避免这个问题发生,我们可以在当访问到没有处理的请求的时候,给用户响应自定义内容。

// 该中间件也需要写在最后(与异常中间件的顺序无所谓,只要确保其在所有的路由方法之后就可)
app.use((req,res,next) => {
    // 输出404错误
    res.status(404).send('<h1>404</h1>')
    // 先指定404状态码,然后再输出错误信息
})

注意:404中间件,必须在所有路由之后,至于与异常中间件的顺序,谁先谁后无所谓。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值