likes
comments
collection
share

【Node】Express 框架

作者站长头像
站长
· 阅读数 17

原文来自我的个人博客

1. Express 初体验

1.1 认识Web框架

上一章我们在使用 http 内置模块来搭建 Web 服务器的过程中,会感到很多问题,那就是原生 http 模块 在进行很多处理时会较为复杂,比如:

  1. URL 判断
  2. Method 判断
  3. 参数处理
  4. 逻辑代码处理等等

都需要我们自己来处理和封装,当所有的内容都放在一起时,常常会显得非常混乱。

而这些都是框架能帮我们解决的问题

目前在 Node 中比较流行的 Web 服务器框架是 expresskoaexpress 要早于 koa 出现,并且在 Node 社区中迅速流行起来。

  • 我们可以基于 express 快速、方便的开发自己的 Web 服务器
  • 并且可以通过一些实用工具和中间件来扩展自己功能

Express 整个框架的核心就是中间件,理解了中间件其他一切都非常简单!

1.2 Express 安装

express 的使用过程有两种方式:

  1. 通过 express 提供的脚手架 express-generator,直接创建一个应用的骨架

  2. 0 搭建自己的 express 应用结构

1.2.1 脚手架的方式搭建

  1. 安装脚手架
npm install express-generator -g
  1. 创建项目
express express-generator-demo
  1. 安装依赖
npm install
  1. 启动项目
npm run start
  1. 在浏览器输入 localhost:3000,访问成功,表示服务已启动

【Node】Express 框架

1.2.3 从 0 搭建

  1. 创建一个新的文件夹并安装 express
mkdir express-demo
cd express-demo
npm init
npm i express
  1. 在这个文件夹下创建文件 01_express的基本使用.js
const express = require("express");

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

// home 的 get 请求处理
app.get("/home", (req, res) => {
  res.end("Hello Home");
});

// login 的 post 请求处理
app.post("/login", (req, res) => {
  res.end("Hello Login");
});

// 开启监听
app.listen(8000, () => {
  console.log("服务器启动成功~");
});
  1. 启动服务
node 01_express的基本使用.js
  1. 访问 http://localhost:8000/home 地址

【Node】Express 框架

2. Express 中间件

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

2.1 中间件是什么?

中间件本质是传递给 express 的一个回调函数,这个回调函数接受三个参数:

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

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

  1. 执行任何代码
  2. 更改请求和响应对象
  3. 结束请求-相应周期
  4. 调用栈中的下一个中间件

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

【Node】Express 框架

2.3 如何应用中间件

  • express 主要提供两种方式:
    1. app/router.use;
    2. app/router.methods (methods 指的是常用的请求方式,比如 get post

2.3.1. 注册最普通用的中间件

express 接收到客户端发送的网络请求时,在所有中间件中开始进行匹配。

当匹配到第一个符合要求的中间件时,那么就会执行这个中间件

后续的中间件是否执行,取决于上一个中间件有没有执行 next

  1. 通过 use 方法注册的中间件是最普通/简单的中间件
  2. 通过 use 注册的中间件,无论是什么请求方式都可以匹配上
const express = require("express");

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

//
app.use((req, res, next) => {
  console.log("我是最普通的中间件1");
  next();
});

app.use((req, res, next) => {
  console.log("我是最普通的中间件2");
  next();
});
// 开启监听

app.listen(8000, () => {
  console.log("服务器启动成功~");
});

启动服务,访问 localhost:8000 下面的两个中间件会依次执行 【Node】Express 框架

2.3.2 注册路径匹配的中间件

路径匹配的中间件不会对请求方式进行限制

const express = require("express");

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

// 注册路径匹配中间件
app.use('/home',(req,res,next) => {
  console.log('我是匹配到 /home 执行的中间件')
})
// 开启监听
app.listen(8000, () => {
  console.log("服务器启动成功~");
});

启动服务,访问 http://localhost:8000/home,只有访问 '/home' 路径才会执行上面的中间件

2.3.3 注册路径和请求方式中间件

如果相对路径和请求方式都做限制可以使用 app.method(path, middleware) 的方式

const express = require("express");

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


app.get('/home',(req,res,next) => {
  console.log('我是匹配到 /home 的 get 请求时执行的中间件')
})

// 开启监听
app.listen(8000, () => {
  console.log("服务器启动成功~");
});

只有当匹配到 /homeget 请求时才执行上面的中间件

2.3.4 注册多个中间件

当我们相对某个复杂的操作进行拆分的时候,就可以使用以下方式注册多个中间件

app.get(路径,中间件1,中间件2,中间件3)

const express = require("express");

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

app.get(
  "/home",
  (req, res, next) => {
    console.log("我是匹配到 /home 的 get 请求时执行的中间件1");
    next()
  },
  (req, res, next) => {
    console.log("我是匹配到 /home 的 get 请求时执行的中间件2");
    next()
  },
  (req, res, next) => {
    console.log("我是匹配到 /home 的 get 请求时执行的中间件3");
    next()
  }
);

// 开启监听
app.listen(8000, () => {
  console.log("服务器启动成功~");
});

2.4 内置 & 第三方中间件

并非所有的中间件都需要我们从零去编写:

  1. express 有内置一些帮助我们完成对 request 解析的中间件;
  2. registry 仓库中也有很多可以辅助我们开发的中间件;

2.4.1 body-parse

在客户端发送post请求时,会将数据放到body中:

  1. 客户端可以通过json的方式传递;
  2. 也可以通过form表单的方式传递

如果让我么你自己写中间件的话,通常需要做一些额外的逻辑判断

const express = require("express");

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

app.use((req,res,next) => {
  if(req.headers['content-type'] === 'application/json') {
    req.on('data', data => {
      const userInfo = JSON.parse(data.toString())
      req.body = userInfo
    })

    req.on('end', () => {
      next()
    })
  } else {
    
  }
})

// 开启监听
app.listen(8000, () => {
  console.log("服务器启动成功~");
});

但是,事实上我们可以使用 expres 内置的中间件或者使用 body-parser 来完成

app.use(express.json())

就像上面,我们只需要一行代码即可搞定,非常方便

如果解析的是 application/x-www-form-urlencoded,可以使用

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

2.4.2 morgan

如果我们希望将请求日志记录下来,那么可以使用 express 官网开发的第三方库:morgan

因为是第三方库,所以需要先单独安装 morgan

npm i morgan
const fs = require("fs");
const express = require("express");
const morgan = require("morgan");

const loggerWriter = fs.createWriteStream("./log/access.log", {
  flags: "a+",
});

// 创建服务器
const app = express();
app.use(morgan("combined", { stream: loggerWriter }));
// 开启监听
app.listen(8000, () => {
  console.log("服务器启动成功~");
});

启动服务,接着在 ./log 目录下就会保存我们所有的请求日志了

【Node】Express 框架

2.4.3 multer

上传文件,我们可以使用 express 提供的 multer 来完成:

同样需要先安装 multer

npm i multer
const express = require("express");
const multer = require("multer");
const path = require("path");
// 创建服务器
const app = express();

const upload = multer({
  storage: multer.diskStorage({
    destination(req, file, cb) {
      cb(null, "uploads/");
    },
    filename: (req, file, cb) => {
      cb(null, Date.now() + "_" + path.extname(file.originalname));
    },
  }),
});

// 单文件上传
app.post("/upload", upload.single("file"), (req, res, next) => {
  console.log(req.file);
  res.end("文件上传成功");
});

// 多文件上擦混
app.post("/photos", upload.array("photos"), (req, res, next) => {
  console.log(req.files);
  res.end("文件上传成功");
});

// 开启监听
app.listen(8000, () => {
  console.log("服务器启动成功~");
});

接着我们尝试通过 postman 上传一张图片

【Node】Express 框架

【Node】Express 框架

如果我们希望借助于 multer 帮助我们解析一些 form-data 中的普通数据,那么我们可以使用 any

【Node】Express 框架

app.use(upload.any())

app.use('login', (req,res,next) => {
    console.log(req.body)
})

2.5 其他

2.5.1 query & params

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

  1. 方式一:通过 get 请求中的 URLparams
  2. 方式二:通过 get 请求中的 URLquery
  3. 方式三:通过 post请求中的 bodyjson 格式(中间件中已经使用过);
  4. 方式四:通过 post 请求中的 bodyx-www-form-urlencoded 格式(中间件使用过);
  5. 方式五:通过 post 请求中的 form-data 格式(中间件中使用过);

关于传递参数 paramsquery 的使用也是非常简单的

请求地址:http://localhost:8000/login/abc/zhangsan

  • 获取参数
app.use('/login/:id/:name',(req,res,next) => {
    console.log(req.params)
    res.json('请求成功')
})

请求地址:http://localhost:8000/login?username=zhangsan&password=123456

  • 获取参数
app.use('/login',(req,res,next) => {
    console.log(req.qeury)
    res.json('请求成功')
})

2.5.2 服务端的错误处理

app.use((err, req, res, next) => {
  const message = err.message;
  switch (message) {
    case "USER DOES NOT EXISTS":
      res.status(400).json({ message });
  }

  res.status(500);
});

2.5.3 静态资源服务器

部署静态资源我们可以选择很多方式:

  • Node 也可以作为静态资源服务器,并且 express 给我们提供了方便部署静态资源的方法;
const express = require("express");

const app = express()

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

app.listen(8000, ()) => {
  console.log('静态服务器启动成功')
})