Express 框架

159 阅读8分钟

Express 框架是对 node.jshttp 模块的封装,可以让我们更好的进行服务相关的开发。

基本使用

一、安装包

npm i express

二、使用

// 1. 导入 express
const express = require('express')

// 2. 创建应用
const app = express()

// 3. 创建路由
app.get('/login', (req, res) => {
  // 获取请求路径
  console.log(req.path) 
  // 获取请求参数
  console.log(req.query)  Express 框架是对 node.js 的封装,可以让我们更好的进行开发。 基本使用 一、安装包 二、使用 express 框架封装了一些获取请求报文跟设置响应报文的方法,但是它也兼容原生的方法。 
  // 获取请求ip地址
  console.log(req.ip)  
  // 获取请求头
  console.log(req.get('host'))
  // 获取请求头需要使用第三方的包

  // 设置响应状态码
  res.status(500)  
  // 设置响应头
  res.set('a', 'b')  
  // 设置响应体,send 方法已经对字符集进行了设置,所以不会出现乱码的情况
  res.send('服务已经启动了') 
})

// 所有的路由都可以匹配,一般用来配置访问不存在的路由时的返回
app.all('*', (req, res) => {
  res.send('404 Not Found')
})

// 4. 启动服务
app.listen(8000, () => {
  console.log('服务已经启动了...')
})

express 框架封装了一些获取请求报文跟设置响应报文的方法,同时它也兼容原生的方法。

特殊的响应

const express = require('express')

const app = express()

app.get('/login', (req, res) => {
  // 重定向响应
  res.redirect('/admin')  
  // json 响应,传入一个对象,响应的是一个 json 格式数据
  res.json({ name: 'kobe' })  
  // 下载响应
  res.download(__dirname + '/index.html')  
  // 文件内容响应,相当于直接返回文件
  res.sendFile(__dirname + '/index.html')  
})

app.listen(8000, () => {
  console.log('服务已经启动了...')
})

路由参数

某些情况下我们需要让路由的部分路径来动态显示,同时我们希望能够获取到这个动态路由的数据,具体操作如下

const express = require('express')

const app = express()

// 在路径中通过 :变量名 的方式来设置动态路由
app.get('/login/:id.js', (req, res) => {
  // 通过 req.params 属性来获取所有的路由参数
  console.log(req.params.id)

  res.send('登录')
})

app.listen(8000, () => {
  console.log('服务已经启动了...')
})

中间件

本质上是一个用来实现某个功能的函数。

  • 全局中间件:所有的路由回调函数在执行之前都会执行的中间件函数。
  • 路由中间件:只有指定的路由回调函数在执行之前会执行的中间件函数。
// 定义中间件函数
function record(req, res, next) {}

// 全局使用中间件
app.use(record)

// 指定路由使用中间件
app.get('/login', record, (req, res) => {
  res.send('login')
})

静态资源中间件

之前我们做过静态资源访问的模块,Express 封装了对应的中间件,让我们可以直接使用。

// 创建静态资源中间件函数,将当前文件夹下的 public 目录作为网站的根目录
const staticMw = express.static(path.resolve(__dirname, './public'))

// 全局使用
app.use(staticMw)

静态资源中间件不仅帮我们拼接了路径,还帮我们做了 mime 类型处理。

静态资源一般是文件类型,动态资源一般是数据。

注:

  1. 当我们设置了静态资源的中间件时,会将 public 目录下的 index.html 文件作为默认打开的资源。
http://localhost:8000 --> http://localhost:8000/index.html

2. 如果静态资源跟路由对同一个路径进行了匹配,以书写在前的来匹配。

const staticMw = express.static(path.resolve(__dirname, './public'))

app.use(staticMw)

app.get('/', (req, res) => {
  res.send('首页')
})
  1. 一般用静态资源中间件来响应静态资源文件,用路由来响应动态资源。

  2. 静态资源请求只能以 get 方式发送。

获取 post 请求体数据的中间件

前面我们展示了获取请求报文的一些方法(见基本使用),但是没有讲到如何获取请求体中的数据。如果我们想要获取到 post 请求体中的数据,需要使用第三方的 body-parser 包来实现。

// 1. 安装包
npm install body-parser
// 2. 导入 body-parser 包
const bodyParser = require('body-parser')

// 3. 获取中间件函数
// 处理 JSON 格式数据的中间件
const jsonParser = bodyParser.json()
// 处理 querystring 格式数据的中间件
const urlencodedParser = bodyParser.urlencoded({ extended: false })

// 4. 设置为路由中间件
app.post('/login', urlencodedParser, (req, res) => {
  // 在 req 的 body 属性中就可以访问到请求体的数据
  console.log(req.body)
  res.send('login')
})

防盗链中间件

静态资源及路由的匹配规则

静态资源跟路由都是用来对发送到服务端的请求进行响应。它们的区别在于,静态资源一般用来响应文件,它的请求路径一般为文件后缀名形式;路由一般用来响应数据,它的请求路径就是普通的 url 。

在服务端我们针对这两种不同形式的 url 有不同的处理方式,那么问题来了,我们在服务端怎么去区分这两种 url 呢?

答案就是不用进行区分。

如果我们在服务端没有配置静态资源的目录,那么在请求时服务端就会根据请求路径去路由规则里匹配,匹配到就执行路由回调函数,匹配不到就返回 404。

如果在服务端配置了静态资源目录(一般都会配置),这时我们会根据书写在前的代码去进行匹配,如果匹配成功就直接执行操作,如果没有匹配到就去另外一个里面匹配,匹配成功就执行操作,匹配不到则返回 404

路由模块化

当页面中存在多个路由时,为了代码的可读性及可维护性,我们会将路由代码拆封为单独的模块来进行开发。

原代码如下:

const express = require('express')
const path = require('path')

const app = express()

// 设置静态资源中间件
app.use(express.static(path.resolve(__dirname, './public')))

// 设置路由
app.get('/login', (req, res) => {
  res.send('login')
})

app.all('*', (req, res) => {
  res.send('404 Not Found')
})

// 监听端口
app.listen(8000, () => {
  console.log('服务已经启动了...')
})

我们将 login 路由相关的代码拆封后如下:

在 login.js 中

const express = require('express')

// 创建路由对象
const router = express.Router()

// 设置路由
router.get('/login', (req, res) => {
  res.send('login')
})

// 导出路由对象
module.exports = router

在原文件中:

const express = require('express')
const path = require('path')

// 导入路由对象
const router = require('./login')

const app = express()

// 设置静态资源中间件
app.use(express.static(path.resolve(__dirname, './public')))

// 使用路由对象
app.use(router)

app.all('*', (req, res) => {
  res.send('404 Not Found')
})

app.listen(8000, () => {
  console.log('服务已经启动了...')
})

ejs 模板引擎

模板引擎

众所周知,我们是无法在一个 HTML 模板中直接使用 JavaScript 中定义的变量的。

<body>
  <script>
    const uname = 'kobe'
  </script>

  <h2> uname </h2>  // 不能使用
  <h2> ${ uname } </h2>  // 不能使用
</body>

所以我们一般是通过 JavaScript 来实现模板跟变量的拼接。

但是这样会存在一些问题,例如在 JavaScript 文件中书写模板很不方便,以及模板跟变量之间的耦合性太强。这时候我们就可以使用模板引擎。

模板引擎就是用来将 HTML 结构跟 JavaScript 代码分离开的。模板引擎一般适用于服务端。

ejs 模板引擎的基本使用

// 1. 安装 ejs
npm i ejs

01.html 文件

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Document</title>
</head>

<body>
  <h2>
    <!-- ejs 语法,通过这种方式来使用变量 --> 
    <%= uname %>
  </h2>
</body>
</html>

js 文件

// 导入文件读取模块
const fs = require('fs')

// 2. 导入 ejs
const ejs = require('ejs')

//3. 定义在 html 文件中使用的变量
const uname = 'kobe'

// 4. 读取html文件
const html = fs.readFileSync('./01.html').toString()

// 5. 使用 ejs 来渲染
const result = ejs.render(html, { uname })

console.log(result)

列表渲染跟条件渲染

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Document</title>
</head>

<body>
  <ul>
    <!-- 列表渲染  -->
    <% list.forEach(item => { %> 
      <li><%= item %></li>
    <% }) %>
  </ul>
    
  <!-- 条件渲染  -->  
  <% if(flag) {%>
    <p>登录</p>
  <% } else { %>
    <p>注册</p>
  <% } %>
</body>
</html>

js 文件

// 导入文件读取模块
const fs = require('fs')

// 2. 导入 ejs
const ejs = require('ejs')

//3. 定义在 html 文件中使用的变量
const list = ['kobe', 'james', 'caohan']
const flag = true

// 4. 读取html文件
const html = fs.readFileSync('./01.html').toString()

// 5. 使用 ejs 来渲染
const result = ejs.render(html, { list, flag })

console.log(result)

在 Express 中使用 ejs 模板引擎

我们在使用 Express 创建服务时有时会返回一个页面,这个时候就可以使用模板引擎。

不使用模板引擎的情况:

const express = require('express')

const app = express()

app.get('/login', (req, res) => {
  // 直接返回页面
  res.sendFile(__dirname + '/index.html')  
})

app.listen(8000, () => {
  console.log('服务已经启动了...')
})

使用模板引擎返回页面的情况:

// 在 Express 框架中对模板引擎已经做了集成,可以直接使用
const express = require('express')
const path = require('path')

const app = express()

// 1. 设置模板引擎使用的类型为 ejs
app.set('view engine', 'ejs')

// 2. 设置模板文件的存放位置为当前目录的 views 文件夹下
app.set('views', path.resolve(__dirname, './views'))

app.get('/login', (req, res) => {
  // 定义变量
  const title = '登录页'
  // 3. express 会去 views 文件夹下查找对应名称的 .ejs 文件,将变量传入后渲染,然后返回
  res.render('index',{ title }) 
})

app.listen(8000, () => {
  console.log('服务已经启动了...')
})

./views/index.ejs 文件中

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Document</title>
</head>
<body>
  <!-- 使用变量 -->
  <h2><%= title %></h2>
</body>
</html>

Express 应用程序生成器

通过应用生成器工具 express-generator 可以快速创建一个应用的骨架。

处理文件上传

处理文件上传我们需要用到一个第三方的库 formidable

我们可以通过 form 表单来上传文件

<form action="/file" method="post" enctype="multipart/form-data">
  <input type="text" name="username" />
  <input type="file" name="upload" />
</form>

在路由文件中

const express = require('express')

// 1. 导入 formidable
const formidable = require('formidable')

const router = express.Router()

// 文件上传的路由
router.post('/file', (req, res) => {
  // 2. 创建 form 对象
  const form = formidable({
    multiples: true,
    // 设置上传文件的保存目录为静态资源文件夹下的 images 目录
    uploadDir: __dirname + '/../public/images',
    // 保持文件的后缀名
    keepExtensions: true,
  })
  // 3. 解析请求报文
  form.parse(req, (err, fields, files) => {
    if (err) {
      next(err)
      return
    }
    // 4. 获取文件的地址,后续保存到数据库中
    const fileURL = '/images/' + files.upload.newFilename
    
    res.send()
  })
})

// 导出路由对象
module.exports = router