搞定Express框架

写在前面


大家好,我是一溪风月⛽,一名前端工程师,在Node基础系列的上篇文章中,我们学习Web服务器的基本知识,我们学习和使用了http服务来搭建了一个Web服务器,但是在实际的开发中我们并不会这样做,我们一般都是基于框架去开发Web服务,Express就是其中比较受欢迎的一个,这篇文章我们将学习一个Express的基本用法,让你能够基于这个框架来搭建和开发自己的服务接口,好了,让我们开始吧!

一.认识Web框架


在前面的文章中我们已经了解了Web服务器的基础知识,那么为什么还要学习框架哪?原因有这些

  • 原生http在进行很多处理的时候,会较为复杂。
  • 有URL判断,Method判断,参数处理,逻辑代码处理等,这些都需要我们自己来处理。
  • 并且所有的放在一起,会非常的混乱。

目前在Node中比较流行的轻量级框架有express框架和koa框架,express整个框架的核心就是中间件,了解了中间件一切都会变得非常的简单。

二.Express安装


Express框架的安装和使用有两种方式:

  • 方式一:通过express提供的脚手架,直接创建一个应用的骨架。
js 复制代码
// 安装脚手架
npm install -g express-generator
// 创建项目
express express-demo
// 安装依赖
npm install
// 启动项目
node app.js
  • 方式二:从零自己来搭建自己express的应用结构。
js 复制代码
// 初始化npm搭建项目
npm init -y 
// 安装express
npm install express
// 在文件中导入express进行使用
const express = require("express");
const app = express();

三.Express的基本使用


我们创建第一个express项目之后,我们会发现,我们可以很方便的将我们的请求进行分离,无论是不同的URL,还是get,post等请求方式,这样的方式非常方便我们进行维护和扩展,比如路径中有这些参数/user/:userId在request对象中我们就可以通过这种方式获取req.params.userId来获取。

js 复制代码
const express = require("express")

// 创建express服务器
const app = express()

// home的get请求
app.get("/home", (req, res) => {
  res.end("返回首页轮播图数据!")
})
// login的post请求
app.post("/login", (req, res) => {
  res.end("登录成功,欢迎回来!")
})

// 启动express服务器
app.listen(8000, () => {
  console.log("服务启动🚀在了8000端口!")
})

// nodemon server.js

返回数据的时候我们可以非常方便的使用json还有其他很多方式,我们在文章的后面会详细讲解。

四.认识中间件


Express是一个路由和中间件的Web框架,它本身的功能非常少,Express应用程序的本质是一系列中间件函数的调用,那么什么是中间件哪?

中间件的本质就是传递给Express的一个回调函数,这个回调函数接收三个参数

  • 请求对象(request对象)
  • 响应对象(response对象)
  • next函数(在express中定义的用于执行下一个中间件的函数)

中间件可以执行哪些任务哪?

  • 执行任何代码
  • 更改请求(request)和响应对象(response)对象
  • 结束请求-响应周期(返回数据)
  • 调用栈中的下一个中间件

如果当前中间件功能没有结束请求-响应周期,则必须调用next()将控制权传递给下一个中间件功能,否则,请求将被挂起。

那么如何将中间件应用到我们的应用程序中哪?Express主要提供了两种方式

  • app/router.use
  • app/router.methods

可以是app也可以是router我们会在文章的后边进行讲解,methods指的是app.getapp.post等等,我们首先来使用下最普通的中间件use的方式来写几个案例。

案例一: 最普通的中间件

js 复制代码
// 通过use注册的中间件是最普通的中间件,无论通过什么方式都会匹配上。
app.use((req, res, next) => {
  console.log("middleware!!!")
})
// middleware!!!

💡Tips:当express接收到了客户端发送的请求的时候,所有中间件都会开始匹配,当其中的一个中间件符合中间件的是时候那么就匹配这个中间件,当有多个中间件符合的时候会首先匹配第一个,后续中间件是否执行取决于这个首先匹配到的中间件是否执行了next。

案例二:path匹配中间件

js 复制代码
app.use("/home", (req, res, next) => {
  res.json({
    "address": "南京东路"
  })
})
// localhost:8000/home 

案例三:path和methods匹配中间件

js 复制代码
app.get("/home", (req, res, next) => {
  res.json({
    "address": "南京东路"
  })
})
// get localhost:8000/home 

案例四:匹配多个中间件

js 复制代码
app.get("/home", (req, res, next) => {
  console.log("001")
  next()
}, (req, res, next) => {
  console.log("002")
}, (req, res, next) => {
  console.log("003")
}, (req, res, next) => {
  console.log("004")
})
// 001
// 002

五.应用中间件body解析


在客户端发送POST请求的时候,会将数据放在body中,客户端可以通过JSON的方式进行传递,也可以通过form表单的方式进行传递。

在开发中我们会使用express内置中间件或者使用body-parse来进行解析。

js 复制代码
const express = require("express")
const app = express()

// 使用中间件解析json
app.use(express.json())

app.post("/login", (req, res, next) => {
  console.log(req.body)
})

app.listen(8000, () => {
  console.log("服务启动在8000端口~")
})

// { username: 'zzz', password: '12345' }

在开发中除了会有解析json的需求之外还会有解析application/x-www-form-urlencoded我们使用如下方式:

js 复制代码
const express = require("express")
const app = express()

// 使用中间件解析urlencoded
app.use(express.urlencoded({ extended: true }))

app.post("/login", (req, res, next) => {
  console.log(req.body)
})

app.listen(8000, () => {
  console.log("服务启动在8000端口~")
})

六.应用中间件-第三方中间件


在服务端的开发中我们如果想要去排查问题,就需要查看服务端的日志,如果我们想要将请求的日志记录下来,那么我们可以使用Express官方开发的第三方库:morgan

js 复制代码
npm install morgan

然后我们可以使用以下morgan,代码如下:

js 复制代码
const express = require("express")
const morgan = require("morgan")
const fs = require("fs")
// 应用第三方中间件
const app = express()

const writeStream = fs.createWriteStream("./logs/access.log")
app.use(morgan('combined', { stream: writeStream }))

app.post('/login', (req, res) => {
  res.end("登录成功,欢迎回来!")
})

app.listen(9000, () => {
  console.log("服务启动在9000端口!")
})

然后我们在代码中新建一个名为logs文件夹,然后新建一个access.log通过apifox进行请求,然后我们会发现在我们新建的文件中增加了一条信息。

除了使用morgan进行日志的记录,我们还可以使用multer来进行文件的上传。

js 复制代码
npm install multer

然后我们编写一下文件上传单个文件的逻辑

js 复制代码
const express = require("express")
const multer = require("multer")
const app = express()

const upload = multer({
  dest: './uploads'
})

//编写中间件
app.post('/avater', upload.single('avatar'), (req, res, next) => {
  console.log(req.file)
  res.end("文件上传成功!")
})

app.listen(9000, () => {
  console.log("服务运行在9000端口")
})

然后我们通过apifox进行请求,我们就会发现文件被上传了,但是在window中我们是打不开的,因为我们并没有指定后缀名,但是其实这个文件没有任何问题的。

为了解决这种问题,我们可以自定义文件名,multer其实已经支持了这个功能

js 复制代码
const upload = multer({
  storage: multer.diskStorage({
    destination (req, file, callback) {
      callback(null, './uploads')
    },

    filename (req, file, callback) {
      callback(null, Date.now() + '_' + file.originalname)
    }
  })
})

除了上传单个文件之外,我们使用multer还可以进行多文件的上传

js 复制代码
app.post("/photos", upload.array('photos'), (req, res, next) => {
  console.log(req.files)
  res.end("上传多张照片成功!")
})

七.form-data的解析


multer除了可以上传文件之外,还可以帮助我们解析form-data格式的数据,因为在客户端有可能使用form-data的格式给我们传递普通的数据,如果要解析普通数据,我们可以使用any。

js 复制代码
const express = require("express")
const multer = require("multer")
const app = express()

const upload = multer()

app.post("/login", upload.any(), (req, res, next) => {
  console.log(req.body)
  res.end("登录成功,欢迎回来")
})

app.listen(9000, () => {
  console.log("服务运行在9000端口")
})

八.客户端发送请求的方式


客户端传递到服务器参数的方法常见的有五种:

  • 方式一:通过get请求中的URL的params
  • 方式二:通过get请求中的URL的query
  • 方式三:通过post请求中的body的json格式(中间件中已经使用了)
  • 方式四:通过post请求中的body的x-www-form-urlencoded格式(中间件使用过)
  • 方式五:通过post请求中的form-data格式(中间件中使用过)

其中有三个我们已经使用过了,我们来尝试着使用下其他两个:

query参数的解析

js 复制代码
app.get("/home/list", (req, res) => {
  const queryInfo = req.query
  console.log(queryInfo)
  res.end("data list")
})

params参数的解析

js 复制代码
app.get("/users/:id", (req, res) => {
  const id = req.params.id
  res.end(`获取到的数据id为${id}`)
})

九.响应数据


end方法 :类似于http中的response.end方法,用法是一致的。

js 复制代码
app.get("/login", (req, res) => {
  res.end("登录成功,欢迎回来!")
})

json 方法:json方法可以传入很多类型:object,array,string,boolean,number,null等,它们会被转换为json格式返回,这个方式也是我们使用比较多的方式。

js 复制代码
app.get("/login", (req, res) => {
  res.json(666)
})

status方法:用于设置状态码,这是一个函数不是一个属性值。

js 复制代码
app.get("/login", (req, res) => {
  res.status(500)
  res.json("创建用户失败!")
})

更多的响应方式可以参考官方文档:www.expressjs.com.cn/4x/api.html...

十.Express路由


如果我们将所有的代码逻辑都写在app中,那么app会变的越来越复杂,一方面完整的Web服务包含非常多的处理逻辑,另一方面有些处理逻辑其实是一个整体,我们应该将它们放在一起,比如user相关的处理,获取某个用户,创建一个新用户等等,我们可以使用express.Router来创建一个路由处理程序,一个router拥有完整的中间件和路由系统,因此它也被称为迷你应用程序。

js 复制代码
const express = require("express")
const userRouter = express.Router()

userRouter.get('/users', (req, res, next) => { })
userRouter.get('/users/:id', (req, res, next) => { })
userRouter.post('/users', (req, res, next) => { })
userRouter.delete('/users/:id', (req, res, next) => { })
userRouter.patch('/users/:id', (req, res, next) => { })
// 导出路由
module.exports = userRouter 

导出之后我们仅仅需要在app.js中导入使用app.use来使用一下即可。

十一.静态资源服务器


部署静态资源我们可以选择很多方式,Node也可以作为静态资源服务器,并且express给我们提供了方便部署静态资源的方法。

js 复制代码
const express = require("express")
const app = express()

app.use(express.static('./uploads'))

app.listen(9100, () => {
  console.log("静态资源服务器启动成功~")
})

十二.服务端错误处理


我们在请求接口的时候如果是一个错误的请求,服务端需要响应给客户端一些错误的信息,所以我们需要一个服务端给客户端进行错误处理的方案。

方案一:返回http错误码

js 复制代码
res.status(401)
res.json("未授权访问的信息")

方案二:http状态码 200 信息中会包含错误code/message

js 复制代码
res.json({
    code:'-1001',
    message:"未授权访问的信息,请检查token",
})

方案一是早期使用比较多的方案,目前大多数公司使用的是第二种方案,在Express中我们还可以编写一个错误处理中间件,当某个接口中出现错误直接通过next(-1001)这种方式来调用。

js 复制代码
app.use((err,req,res,next)=>{
    const code = err;
    let message = "未知错误!"
    switch(code){
        case -1001:
        message = "没有输入用户名和密码!"
        break;
        case -1002:
        message = "输入用户名和密码错误!"
    }
    
    res.json({ code, message })
})

十三.总结


这篇文章到这里就结束了🌸,这篇文章我们学习了Node中的Express框架,我们分别介绍了Express核心概念中间件,包括Express内置的中间件和第三方中间件,我们使用了multer分别实现了单文件上传和多文件上传,并且在最后封装了Express全局的错误处理,相信在你学习了Express之后你会对服务端有更加深刻的理解,同时也是技术栈上一块坚实的拼图~

相关推荐
雯0609~2 分钟前
js:循环查询数组对象中的某一项的值是否为空
开发语言·前端·javascript
bingbingyihao7 分钟前
个人博客系统
前端·javascript·vue.js
尘寰ya8 分钟前
前端面试-HTML5与CSS3
前端·面试·css3·html5
最新信息10 分钟前
PHP与HTML配合搭建网站指南
前端
前端开发张小七24 分钟前
每日一练:3统计数组中相等且可以被整除的数对
前端·python
天天扭码38 分钟前
一杯咖啡的时间吃透一道算法题——2.两数相加(使用链表)
前端·javascript·算法
Hello.Reader43 分钟前
在 Web 中调试 Rust-Generated WebAssembly
前端·rust·wasm
NetX行者1 小时前
详解正则表达式中的?:、?= 、 ?! 、?<=、?<!
开发语言·前端·javascript·正则表达式
流云一号1 小时前
Python实现贪吃蛇三
开发语言·前端·python
liangshanbo12151 小时前
深入讲解 CSS 选择器权重及实战
前端·css