Express知识点以及案例总结

Express

什么是Express

  • 官方给出的概念:Express是基于Node.js平台,快速,开放,极简的Web开发框架

通俗的理解:Express的作用和Node.js内置的http模块类似,是专门用来创建的Web服务器的

进一步理解Express

  • 思考:不使用Express能否创建Web服务器?

能,使用Node.js提供的原生http模块即可

  • 有了http模块,为什么还要用Express?

http模块用起来很复杂,开发效率低,Express是基于内置的http模块进一步封装出来的,能够极大的提高开发效率

  • http内置模块与Express是什么关系?

类似于Web API和jQuery的关系(),后者是前者进一步封装出来得到的

Express能做什么

对于前端程序员来说,最常见的两种服务器,分别是:

  • Web网站服务器:专门对外提供Web网页资源的服务器
  • API接口服务器:专门对外提供API接口的服务器

使用Express,我们可以方便,快捷的创建Web网站的服务器或API接口的服务器

创建基本的Web服务器

先下载express包 npm i express

监听GET请求

通过 app.get()方法,可以监听用户端的GET请求,具体语法格式如下

监听POST请求

通过app.post()方法,可以监听客户端的POST请求,具体的语法格式如下

把内容响应给客户端

通过res.send()方法,可以把处理好的内容,发送给客户端

Express的基本使用

获取URL中携带的参数

通过req.query对象,可以访问到客户端通过查询字符串的形式,发送到服务器的参数:

 获取URL中的动态参数

通过req.params对象,可以访问到URL中,通过:匹配到的动态参数:

// 1. 导入 express
const express = require('express')
// 2. 创建 web 服务器
const app = express()

// 4. 监听客户端的 GET 和 POST 请求,并向客户端响应具体的内容
app.get('/user', (req, res) => {
  // 调用 express 提供的 res.send() 方法,向客户端响应一个 JSON 对象
  res.send({ name: 'zs', age: 20, gender: '男' })
})
app.post('/user', (req, res) => {
  // 调用 express 提供的 res.send() 方法,向客户端响应一个 文本字符串
  res.send('请求成功')
})
app.get('/', (req, res) => {
  // 通过 req.query 可以获取到客户端发送过来的 查询参数
  // 注意:默认情况下,req.query 是一个空对象
  console.log(req.query)
  res.send(req.query)
})
// 注意:这里的 :id 是一个动态的参数
app.get('/user/:ids/:username', (req, res) => {
  // req.params 是动态匹配到的 URL 参数,默认也是一个空对象
  console.log(req.params)
  res.send(req.params)
})

// 3. 启动 web 服务器
app.listen(80, () => {
  console.log('express server running at http://127.0.0.1')
})

 托管静态资源

express.static()

express提供了一个非常好用的参数,叫做express.static(),通过它,我们可以非常方便的创建一个静态资源管理器,例如:通过如下代码就可以将public目录下的图片,CSS文件,JavaScript文件对外开放访问了

app.use(express.static('public'))

现在就可以访问public目录中所有的文件了

注意:Express在指定的静态目录中查找文件,并对外提供资源的访问路径,因此,存放静态文件的目录不会出现在URL中

静态资源中间件注意事项

  1. index.html文件为默认打开的资源
  2. 如果静态资源与路由规则同时匹配,谁先匹配谁就响应
  3. 路由响应动态资源,静态资源中间件响应静态资源

托管多个静态资源目录

如果要托管多个静态资源目录,请多次调用express.static()函数

访问静态资源文件时,express.static()函数会根据目录的添加顺序查找所需的文件

挂载路径前缀

如果希望在托管的静态资源访问路径之前,挂载路径前缀,则可以使用如下的方式:

app.use('/public',express.static('public'))
const express = require('express')
const app = express()

// 在这里,调用 express.static() 方法,快速的对外提供静态资源
app.use('/files', express.static('./files'))
app.use(express.static('./clock'))

app.listen(80, () => {
  console.log('express server running at http://127.0.0.1')
})

nodemon

为什么要使用nodemon

在编写调试Node.js项目的时候,如果修改了项目的代码,则需要频繁的手动close掉,然后重新启动,非常繁琐,我们可以使用nodemon这个工具,它能够监听项目文件的变动,当代码被修改后,nodemon会自动帮我们重启项目,极大方便了开发和调试

Express 路由

什么是路由

广义上来讲,路由就是映射关系

例如:人工客服中的每个按键都对应不同的服务,在这里,路由就是按键与服务之间的映射关系

Express中的路由

在Express中,路由指的是客户端的请求与服务器处理函数之间的映射关系

Express中的路由三个部分组成,分别是请求的类型,请求的url地址,处理函数,格式如下:

app.METHOD(PATH,HANDLER)
//path即为请求的URL地址
//handler为处理函数

Express中的路由的例子

路由的匹配过程

每当一个请求到达服务器之后,需要先经过路由的匹配,只有匹配成功之后,才会调用对应的处理函数。

在匹配时,会按照路由的顺序进行匹配,如果请求类型和请求的URL同时匹配成功,则Express会将这次请求,转交给对应的function函数进行处理

 路由匹配的注意点:

  1. 按照定义的先后顺序进行匹配
  2. 请求类型和请求的URL同时匹配成功,才会调用对应的处理函数

路由的使用

最简单的用法

在Express中使用路由最简单的方式,就是把路由挂载到app上,示例代码如下:

const express = require('express')
// 创建web服务器,命名为app
const app = express()

// 挂载路由
app.get('/', (req, res) => {
  console.log(req.method)
  console.log(req.url)
  console.log(req.httpVersion)
  console.log(req.headers)
  //express操作
  console.log(req.path)
  console.log(req.query)
  //获取ip
  console.log(req.ip)\
  //获取请求头
  console.log(req.get('host'))
  res.send('hello world.')
})
app.post('/', (req, res) => {
  res.send('Post Request.')
})
// 启动web服务器
app.listen(80, () => {
  console.log('http://127.0.0.1')
})

模块化路由

为了方便对路由进行模块化的管理,Express不建议将路由直接挂载到app上,而是推荐将路由抽离为单独的模块,将路由抽离为单独模块的步骤如下:

  1. 创建路由模块对应的.js文件
  2. 调用express.Router()函数创建路由对象
  3. 向路由对象上挂载具体的路由
  4. 使用module.exports向外共享路由对象
  5. 使用app.use()函数注册路由模块

为路由模块添加前缀

类似于托管静态资源时,为静态资源统一挂载访问前缀一样,路由模块添加前缀的方式也非常简单

app.use('/api', router)

03router.js:

// 这是路由模块
// 1. 导入 express
const express = require('express')
// 2. 创建路由对象
const router = express.Router()

// 3. 挂载获取用户列表的路由
router.get('/user/list', (req, res) => {
  //原生响应
  //res.statusCode-404
  //res.statusMessage='love'
  //res.setHeader('xxx','yyy')
  //res.write('hello express')
  //express响应
  //res.send('Get user list.')
  //res.status(500),set('abc','def').send('这都是OK')
  //其他响应
  res.redirect('http://atguigu.com')//重定向
  res.download('./package.json')//下载响应
  res.json()//响应JSON
  res.sendFile(__dirname+'/home.html')//响应文件内容
})
// 挂载添加用户的路由
router.post('/user/add', (req, res) => {
  res.send('Add new user.')
})

// 4. 向外导出路由对象
module.exports = router
// 导入express
const express = require('express')
// 创建路由对象
const app = express()

// app.use('/files', express.static('./files'))

// 1. 导入路由模块
const router = require('./03.router')
// 2. 注册路由模块 添加统一的访问前缀 /api
app.use('/api', router)

// 注意: app.use() 函数的作用,就是来注册全局中间件

app.listen(80, () => {
  console.log('http://127.0.0.1')
})

在Node.js的HTTP模块或Express框架中,res.end()res.send()方法都是用于响应客户端请求,但它们之间存在一些关键差异:

  1. 原生与框架方法:

    • res.end()是Node.js原生HTTP模块中的方法,它用于结束响应过程,可以发送一个可选的数据体作为响应的结束。如果在调用res.end()之前没有写入任何响应头,它会自动添加一个Content-Length头。
    • res.send()是Express框架提供的一个便捷方法,它不仅可以发送数据,还会自动设置Content-Type头部和HTTP状态码(默认为200 OK)。它内部会调用res.write()res.end()来完成响应的发送。
  2. 数据类型与自动处理:

    • res.end()主要接受字符串或Buffer作为参数,适合于发送简单的文本或二进制数据。它不会自动设置Content-Type,除非之前已手动设置。
    • res.send()则更加灵活,它可以发送各种数据类型(包括字符串、对象、数组等),并且会根据数据类型自动设置Content-Type。例如,发送JSON数据时,它会设置Content-Typeapplication/json
  3. 状态码处理:

    • 使用res.end()时,需要手动设置HTTP状态码,如res.statusCode = 404; res.end('Not Found');
    • res.send()可以自动处理状态码,也可以显式传递,例如通过res.status(404).send('Not Found')。在Express中,更推荐使用链式调用来设置状态码和发送响应。
  4. 使用场景:

    • 当只需要发送简单的响应体结束响应时,可以使用res.end()
    • 当需要更复杂的响应处理,包括自动设置头部、状态码以及处理不同数据类型时,应该使用res.send()

总的来说,res.send()在Express应用中提供了更高级的功能和更简便的API,而res.end()则是更底层、更基础的结束响应的方法。在编写Express应用时,通常优先考虑使用res.send(),除非有特殊需求。

中间件

Express中间件地调用流程

当一个请求到达Express都服务器之后,可以连续调用多个中间件,从而对这次请求进行预处理

Express中间件的格式

Express的中间件,本质上就是一个function处理函数,Express中间件的格式如下:

 注意:中间件的形参列表中,必须包含next函数,而路由处理函数中只包含req和res

next函数的作用

next函数是实现多个中间件连续调用的关键,他表示把流转关系转交给下一个中间件或路由

Express中间件初体验

定义中间件函数

可以通过如下的方式,定义一个最简单的中间件函数:

全局生效的中间件

客户端发起的任何请求,到达服务器之后,都会触发的中间件,叫做全局生效的中间件

通过调用app.use(中间件函数),即可定义一个全局生效的中间件,示例代码如下:

定义全局中间件的简化形式

const express = require('express')
const app = express()

//  定义一个最简单的中间件函数
// const mw = function (req, res, next) {
//   console.log('这是最简单的中间件函数')
//    把流转关系,转交给下一个中间件或路由
//   next()
// }

//  将 mw 注册为全局生效的中间件
// app.use(mw)

// 这是定义全局中间件的简化形式
app.use((req, res, next) => {
  console.log('这是最简单的中间件函数')
  next()
})

app.get('/', (req, res) => {
  console.log('调用了 / 这个路由')
  res.send('Home page.')
})
app.get('/user', (req, res) => {
  console.log('调用了 /user 这个路由')
  res.send('User page.')
})

app.listen(80, () => {
  console.log('http://127.0.0.1')
})

const express=require('express')
const fs=require('fs')
const path=require('path')
const app=express()
function recordMiddleware(req,res,next){
   // 获取url ip
   let {url,ip}=req
   // console.log(url,ip)
   fs.appendFileSync(path.resolve(__dirname+'/access.log'),`${url},${ip}\r\n`)
   next()//请求之后先执行全局中间件函数,之后再执行请求之后的后续函数
}
app.use(recordMiddleware)
app.get('/home',(req,res)=>{
 
  res.send('前台首页')
})
app.get('/admin',(req,res)=>{
  res.send('后台首页')
})
app.all('*',(req,res)=>{
  res.send('<h1>404 NotFound</h1>')
})
app.listen(3000,()=>{
  console.log('服务已经启动,端口3000正在监听中')
})

中间件的作用

 多个中间件之间,共享同一份req和res,基于这样的特性,我们可以在上游的中间件中,统一为req和res对象添加自定义的属性或方法,供下游的中间件或路由使用

const express = require('express')
const app = express()

// 这是定义全局中间件的简化形式
app.use((req, res, next) => {
  // 获取到请求到达服务器的时间
  const time = Date.now()
  // 为 req 对象,挂载自定义属性,从而把时间共享给后面的所有路由
  req.startTime = time
  next()
})

app.get('/', (req, res) => {
  res.send('Home page.' + req.startTime)
})
app.get('/user', (req, res) => {
  res.send('User page.' + req.startTime)
})

app.listen(80, () => {
  console.log('http://127.0.0.1')
})

定义多个全局中间件

可以使用app.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.get('/user', (req, res) => {
  res.send('User page.')
})

app.listen(80, () => {
  console.log('http://127.0.0.1')
})

局部生效的中间件

不使用app.use()定义的中间件,叫做局部生效的中间件,示例代码如下:

定义多个局部中间件

可以在路由中,通过如下两种等价的方式,使用多个局部中间件

// 导入 express 模块
const express = require('express')
// 创建 express 的服务器实例
const app = express()

// 1. 定义中间件函数
const mw1 = (req, res, next) => {
  console.log('调用了第一个局部生效的中间件')
  next()
}

const mw2 = (req, res, next) => {
  console.log('调用了第二个局部生效的中间件')
  next()
}

// 2. 创建路由
app.get('/', [mw1, mw2], (req, res) => {
  res.send('Home page.')
})
app.get('/user', (req, res) => {
  res.send('User page.')
})

// 调用 app.listen 方法,指定端口号并启动web服务器
app.listen(80, function () {
  console.log('Express server running at http://127.0.0.1')
})

了解中间件的5个注意事项

  1. 一定要在路由之前注册中间件
  2. 客户端发送过来的请求,可以连续调用多个中间件进行处理
  3. 执行完中间件的业务代码之后,不要忘记调用next()函数
  4. 为了防止代码逻辑混乱,调用next()函数后不要再写额外的代码
  5. 连续调用多个中间件时,多个中间件之间,共享req和res对象

中间件的分类

为了方便理解和记忆中间件的作用,Express官方把常见的中间件用法,分成了5大类,分别是

  • 应用级别的中间件

通过app.use()或app.get()或app.post(),绑定到app实例上的中间件,叫做应用级别的中间件,代码示例如下:

  • 路由级别的中间件

绑定到express.Router()实例上的中间件,叫做路由级别的中间件,它的用法和应用级别的中间件没有任何区别,只不过,应用级别的中间件是绑定到app实例上,路由级别的中间件绑定到router实例上,代码示例如下:

  • 错误级别的中间件

错误级别中间件的作用:专门用来捕获整个项目中发生的异常错误,从而防止项目异常崩溃的问题。

格式:错误级别中间件的function处理函数中,必须有4个形参,形参顺序从前到后,分别是(err,req,res,next)

// 导入 express 模块
const express = require('express')
// 创建 express 的服务器实例
const app = express()

// 1. 定义路由
app.get('/', (req, res) => {
  // 1.1 人为的制造错误
  throw new Error('服务器内部发生了错误!')
  res.send('Home page.')
})

// 2. 定义错误级别的中间件,捕获整个项目的异常错误,从而防止程序的崩溃
app.use((err, req, res, next) => {
  console.log('发生了错误!' + err.message)
  res.send('Error:' + err.message)
})

// 调用 app.listen 方法,指定端口号并启动web服务器
app.listen(80, function () {
  console.log('Express server running at http://127.0.0.1')
})

 

注意:错误级别的中间件应该放在所有路由之后 ,去捕获所有的错误

  • Express内置的中间件

自Express4.16.0版本开始,Express内置了3个常用的中间件,极大的提高了Express项目的开发效率和体验

  1. express.static快速托管静态资源的内置文件,例如:HTML文件,图片,CSS样式等(无兼容性)
  2. express.json解析JSON格式的请求体数据(有兼容性,仅在4.16.0+版本可使用)
  3. express.urlencoded解析 URL-encoded格式的请求体数据(有兼容性,仅在4.16.0+版本可使用)

// 导入 express 模块
const express = require('express')
// 创建 express 的服务器实例
const app = express()

// 注意:除了错误级别的中间件,其他的中间件,必须在路由之前进行配置
// 通过 express.json() 这个中间件,解析表单中的 JSON 格式的数据
app.use(express.json())
// 通过 express.urlencoded() 这个中间件,来解析 表单中的 url-encoded 格式的数据
app.use(express.urlencoded({ extended: false }))

app.post('/user', (req, res) => {
  // 在服务器,可以使用 req.body 这个属性,来接收客户端发送过来的请求体数据
  // 默认情况下,如果不配置解析表单数据的中间件,则 req.body 默认等于 undefined
  console.log(req.body)
  res.send('ok')
})

app.post('/book', (req, res) => {
  // 在服务器端,可以通过 req,body 来获取 JSON 格式的表单数据和 url-encoded 格式的数据
  console.log(req.body)
  res.send('ok')
})

// 调用 app.listen 方法,指定端口号并启动web服务器
app.listen(80, function () {
  console.log('Express server running at http://127.0.0.1')
})
  • 第三方的中间件

非Express官方内置的,而是由第三方开发出来的中间件,叫做第三方中间件,在项目中,大家可以按需下载并配置第三方中间件,从而提高项目的开发效率

例如:在Express@4.16.0之前的版本中,经常使用body-parser这个第三方中间件,来解析请求体数据,使用步骤如下:

  1. 运行npm install body-parser安装中间件
  2. 使用require导入中间件
  3. 调用app.use()注册并使用中间件
// 导入 express 模块
const express = require('express')
// 创建 express 的服务器实例
const app = express()

// 1. 导入解析表单数据的中间件 body-parser
const parser = require('body-parser')
// 2. 使用 app.use() 注册中间件
app.use(parser.urlencoded({ extended: false }))
// app.use(express.urlencoded({ extended: false }))

app.post('/user', (req, res) => {
  // 如果没有配置任何解析表单数据的中间件,则 req.body 默认等于 undefined
  console.log(req.body)
  res.send('ok')
})

// 调用 app.listen 方法,指定端口号并启动web服务器
app.listen(80, function () {
  console.log('Express server running at http://127.0.0.1')
})

注意:Express内置的express.urlencoded中间件,就是基于body-parser这个第三方中间件进一步封装出来的

 body-parser

使用路由中间件,并不是所有的请求都要处理返回数据,使用前先下载body-parser

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <h2>用户登录</h2>
  <hr>
  <form action="http://127.0.0.1:3000/login" method="post">
    用户名:<input type="text" name="username"><br>
    密码:<input type="password" name="password"><br>
    <button>登录</button>
  </form>
</body>
</html>
const express=require('express')
const bodyParser=require('body-parser')
const app=express()
// 解析JSON格式的请求体的中间件
const jsonParser=bodyParser.json()
// 解析querystring格式请求体的中间件
const urlencodeParser=bodyParser.urlencoded({extended:false})
app.get('/login',(req,res)=>{
  // res.send('表单页面')
  res.sendFile(__dirname+'/form.html')

})
// 当urlencodeParser执行完毕之后,会给req身上添加body的的属性
app.post('/login',urlencodeParser,(req,res)=>{
  // 获取请求体数据
  res.send('获取用户的数据')
  console.log(req.body)
})
app.listen(3000,()=>{
  console.log('3000端口监听中')
})

 

 防盗链

通过确认主机名来判断图片是否可被外界网站使用

app.use((req,res,next)=>{
  // 检测请求头中的referer是否为127.0.0.1
  let referer=req.get('referer')
  if(referer){
    // 实例化
    let url=new URL(referer)
    // 获取hostname(请求头中的主机名)
    let hostname=url.hostname
    console.log(hostname)
    if(hostname!=='127.0.0.1'){
      // 响应404
      res.status(404).send('<h1>404 Not Found</h1>')
      return
    }
    next()
  }
})

自定义中间件

需求描述与实现步骤

自己手动模拟一个类似于express.urlencoded这样的中间件,来解析POST提交到服务器的表单数据

实现步骤:

  • 定义中间件

使用app.use()来定义全局生效的中间件,代码如下:

app.use(function(req,res,next){
//中间件的业务逻辑

})
  • 监听req的data事件

在中间件中,需要监听req对象的data事件,来获取客户端发送到服务器的数据

如果数据量比较大,无法一次性发送完毕,则客户端会把数据切割后,分批发送到服务器,所以data事件可能会触发多次,每一次触发data事件时,获取到数据只是完整数据的一部分,需要手动对接收的数据进行拼接

  • 监听req的end事件

当请求体数据接收完毕之后,会自动触发req的end事件

因此,我们可以在req的end事件中,拿到并处理完整的请求体数据,示例代码如下:

  • 使用querystring模块解析请求体数据

Node.js内置了一个querystring模块,专门用来处理查询字符串,通过这个模块提供的parse()函数,可以轻松的把查询字符串,解析成对象的格式,示例代码如下:

  • 将解析出来的数据对象挂载为req.body

上游的中间件和下游的中间件及路由之间,共享一份req和res,因此,我们可以将解析出来的数据,挂载为req的自定义属性,命名为req.body,

供下游使用,示例代码如下:

// 导入 express 模块
const express = require('express')
// 创建 express 的服务器实例
const app = express()
// 导入 Node.js 内置的 querystring 模块
const qs = require('querystring')

// 这是解析表单数据的中间件
app.use((req, res, next) => {
  // 定义中间件具体的业务逻辑
  // 1. 定义一个 str 字符串,专门用来存储客户端发送过来的请求体数据
  let str = ''
  // 2. 监听 req 的 data 事件
  req.on('data', (chunk) => {
    str += chunk
  })
  // 3. 监听 req 的 end 事件
  req.on('end', () => {
    // 在 str 中存放的是完整的请求体数据
    // console.log(str)
    // TODO: 把字符串格式的请求体数据,解析成对象格式
    const body = qs.parse(str)
    req.body = body
    next()
  })
})

app.post('/user', (req, res) => {
  res.send(req.body)
})

// 调用 app.listen 方法,指定端口号并启动web服务器
app.listen(80, function () {
  console.log('Express server running at http://127.0.0.1')
})

 将自定义中间件封装为模块

为了优化代码结构,我们可以把自定义的中间件函数,封装为独立的模块,示例代码如下:

custom-body-parser.js:

// 导入 Node.js 内置的 querystring 模块
const qs = require('querystring')

const bodyParser = (req, res, next) => {
  // 定义中间件具体的业务逻辑
  // 1. 定义一个 str 字符串,专门用来存储客户端发送过来的请求体数据
  let str = ''
  // 2. 监听 req 的 data 事件
  req.on('data', (chunk) => {
    str += chunk
  })
  // 3. 监听 req 的 end 事件
  req.on('end', () => {
    // 在 str 中存放的是完整的请求体数据
    // console.log(str)
    // TODO: 把字符串格式的请求体数据,解析成对象格式
    const body = qs.parse(str)
    req.body = body
    next()
  })
}

module.exports = bodyParser
// 导入 express 模块
const express = require('express')
// 创建 express 的服务器实例
const app = express()

// 1. 导入自己封装的中间件模块
const customBodyParser = require('./14.custom-body-parser')
// 2. 将自定义的中间件函数,注册为全局可用的中间件
app.use(customBodyParser)

app.post('/user', (req, res) => {
  res.send(req.body)
})

// 调用 app.listen 方法,指定端口号并启动web服务器
app.listen(80, function () {
  console.log('Express server running at http://127.0.0.1')
})

 EJS模板引擎

模板引擎是分离用户界面和业务数据的一种技术(分离HTML和JS)

EJS是一个高效的JavaScript的模板引擎

官网:EJS -- Embedded JavaScript templates

中文站:EJS -- 嵌入式 JavaScript 模板引擎 | EJS 中文文档

EJS初体验

// EJS初体验
// 1.安装 ejs
// 2.导入EJS
const ejs=require('ejs')
const fs=require('fs')

// 字符串
let china='中国'
// let str=`我爱你中国 ${china}`
// 使用ejs渲染
// let str='我爱你 <%= china %>'
let weather='我今天天气不错'
let str=fs.readFileSync('./ejs.html').toString()
let result=ejs.render(str,{china:china,weather})//render函数(渲染)把尖括号里面的东西变为china对象中china属性的值
console.log(result)
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <h2>我爱你 <%= china %></h2>
  <p><%= weather %></p>
</body>
</html>

ejs列表渲染

const ejs=require('ejs')
const xiyou=['唐僧','孙悟空','猪八戒','沙僧']
// // 原生js
// let str='<ul>'
// xiyou.forEach(item=>{
//   str+=`<li>${item}</li>`
// })
// str+='</ul>'
// console.log(str)
// EJS实现
const fs=require('fs')
let str=fs.readFileSync('./ejs.html').toString()
let result=ejs.render(str,{xiyou:xiyou})
console.log(result)
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <ul>
    <% xiyou.forEach(item=>{ %>
      <li><%= item %></li>
      <% }) %>
  </ul>
</body>

EJS条件渲染

// 条件渲染

/**

 * 通过isLogin决定最终的输出内容

 * true 输出 <span>欢迎回来</span>

 * false 输出 <button>登录</button> <button>注册</button>

 */

 在express中使用ejs

// 在express中使用ejs
const express=require('express')
const path=require('path')
const app=express()
// 设置模板引擎
app.set('view engine','ejs')
// 设置模板文件存放位置 模板文件:具有模板语法内容的文件
app.set('views',path.resolve(__dirname,'./views'))
app.get('/home',(req,res)=>{
  // render响应
  // res.render('模板的文件名','数据')
  // 声明变量
  let title='尚硅谷 - 让天下没有难学的技术'
  res.render('home',{title})

})
app.listen(3000,()=>{
  console.log('服务已经启动,端口3000正在监听中')
})

注意要提前创建文件夹views,并且里面有名为home的子文件夹,文件类型为ejs

 express - generator

npm install -g express-generator

express -e 文件名    安装generator,在该文件夹下打开终端 npm i,下载依赖包

 文件上传报文以及处理文件上传请求

在上述generator文件夹中找到routes文件夹下的index.js,在里面写路由模块

在views中建立portrait.ejs文件

有关formidable的使用,参见121_express框架_处理文件上传_哔哩哔哩_bilibili

lowdb的使用参见125_案例实践_04_lowdb的介绍与演示_哔哩哔哩_bilibili

使用Express写接口

创建基本的服务器

创建API路由模块

 编写GET接口

编写POST接口

注意:如果要获取URL-encoded格式的请求体数据,必须配置中间件 app.use(express.urlencoded({extended:false}))

 16.apiRouter.js

const express = require('express')
const router = express.Router()

// 在这里挂载对应的路由
router.get('/get', (req, res) => {
  // 通过 req.query 获取客户端通过查询字符串,发送到服务器的数据
  const query = req.query
  // 调用 res.send() 方法,向客户端响应处理的结果
  res.send({
    status: 0, // 0 表示处理成功,1 表示处理失败
    msg: 'GET 请求成功!', // 状态的描述
    data: query, // 需要响应给客户端的数据
  })
})

// 定义 POST 接口
router.post('/post', (req, res) => {
  // 通过 req.body 获取请求体中包含的 url-encoded 格式的数据
  const body = req.body
  // 调用 res.send() 方法,向客户端响应结果
  res.send({
    status: 0,
    msg: 'POST 请求成功!',
    data: body,
  })
})

// 定义 DELETE 接口
router.delete('/delete', (req, res) => {
  res.send({
    status: 0,
    msg: 'DELETE请求成功',
  })
})

module.exports = router
// 导入 express
const express = require('express')
// 创建服务器实例
const app = express()

// 配置解析表单数据的中间件
app.use(express.urlencoded({ extended: false }))

// 必须在配置 cors 中间件之前,配置 JSONP 的接口
app.get('/api/jsonp', (req, res) => {
  // TODO: 定义 JSONP 接口具体的实现过程
  // 1. 得到函数的名称
  const funcName = req.query.callback
  // 2. 定义要发送到客户端的数据对象
  const data = { name: 'zs', age: 22 }
  // 3. 拼接出一个函数的调用
  const scriptStr = `${funcName}(${JSON.stringify(data)})`
  // 4. 把拼接的字符串,响应给客户端
  res.send(scriptStr)
})



// 导入路由模块
const router = require('./16.apiRouter')
// 把路由模块,注册到 app 上
app.use('/api', router)

// 启动服务器
app.listen(80, () => {
  console.log('express server running at http://127.0.0.1')
})

接口的跨域问题

刚才的GET和POST接口,存在一个很严重的问题:不支持跨域请求(网页为file协议,接口为https协议)

 解决接口跨域问题的方案主要有两种:

  1. CORS(主流的解决方案:推荐使用)
  2. JSONP(有缺陷的解决方案,只支持GET请求)

COTS跨域资源共享

使用cors中间件解决跨域问题

cors是Express的一个第三方中间件,通过安装和配置cors中间件,可以很方便的解决跨域问题

使用步骤分为如下三步:

  1. 运行 npm install cors 安装中间件
  2. 使用const cors=require('cors')导入中间件
  3. 在路由之前调用app.use(cors())配置中间件
// 一定要在路由之前,配置 cors 这个中间件,从而解决接口跨域的问题
const cors = require('cors')
app.use(cors())
// 导入 express
const express = require('express')
// 创建服务器实例
const app = express()

// 配置解析表单数据的中间件
app.use(express.urlencoded({ extended: false }))

// 必须在配置 cors 中间件之前,配置 JSONP 的接口
app.get('/api/jsonp', (req, res) => {
  // TODO: 定义 JSONP 接口具体的实现过程
  // 1. 得到函数的名称
  const funcName = req.query.callback
  // 2. 定义要发送到客户端的数据对象
  const data = { name: 'zs', age: 22 }
  // 3. 拼接出一个函数的调用
  const scriptStr = `${funcName}(${JSON.stringify(data)})`
  // 4. 把拼接的字符串,响应给客户端
  res.send(scriptStr)
})

// 一定要在路由之前,配置 cors 这个中间件,从而解决接口跨域的问题
const cors = require('cors')
app.use(cors())

// 导入路由模块
const router = require('./16.apiRouter')
// 把路由模块,注册到 app 上
app.use('/api', router)

// 启动服务器
app.listen(80, () => {
  console.log('express server running at http://127.0.0.1')
})

什么是CORS

CORS(Cross-Origin Resource Sharing,跨域资源共享)由一系列HTTP响应头组成,这些HTTP响应头决定浏览器是否阻止JS代码跨域获取资源

浏览器的同源安全策略默认会阻止网页”跨域“获取资源,但如果接口服务器配置了CORS相关的HTTP响应头,就可以解除浏览器端的跨域访问限制

 

 CORS跨域资源共享

CORS的注意事项

  1. CORS主要在服务端进行配置,客户端浏览器无需做任何额外的配置,即可请求开启了CORS的接口
  2. CORS在浏览器中有兼容性,只有支持XMLHttpRequest Level2的浏览器,才能正常的访问开启了CORS的服务端接口(例如:IE10+,Chrome4+,FireFox3.5+)

CORS响应头部 - Access-Control-Allow-Origin

响应头部中可以携带一个Access-Control-Allow-Origin字段,其语法如下:

Access-Control-Allow-Origin:<origin>|*

其中,origin参数的值指定了允许访问该资源的外域URL

 例如:下面的字段值将只允许来自http://itcast.cn的请求

res.setHeader('Access-Control-Allow-Origin','http://itcast.cn')

如果指定了Access-Control-Allow-Origin字段的值为通配符*,表示允许来自任何域的请求,示例代码如下:

res.setHeader('Access-Control-Allow-Origin','*')

CORS响应头部-Access-Control-Allow-Headers

默认情况下,CORS支持客户端向服务器发送如下的9个请求头

 如果客户端向服务器发送了额外的请求头信息,则需要在服务器端,通过Access-Control-Allow-Headers对额外的请求头进行声明,否则这次请求会失败!

CORS响应头部- Access-Control-Allow-Methods

默认情况下,CORS仅支持客户端发起GET,POST,HEAD请求

如果客户端希望通过PUT,DELETE等方式请求服务器的资源,则需要在服务器端,通过Access-Control-Allow-Methods来指明实际请求所允许使用的HTTP方法

 

 CORS请求的分类

客户端在请求CORS接口时,根据请求方式和请求头的不同,可以将CORS的请求分为两大类,分别是:

  • 简单请求

同时满足两大条件的请求,就属于简单请求:

  1. 请求方式:GET,POST,HEAD三者之一
  2. HTTP头部信息不超过以下几种字段:无定义头部字段

  • 预检请求

只要符合以下任何一个条件的请求,都需要进行预检请求:

  1. 请求方式为GET,POST,HEAD之外的请求Method类型
  2. 请求头中包含自定义头部字段
  3. 向服务器发送了application/json格式的数据
在浏览器与服务器正式通信之前,浏览器会先发送OPTION请求进行预检,以获知服务器是否允许该实际请求,所以这一次的OPTION请求称为"预检请求",服务器成功响应预检请求后,才会发送真正的请求,并且携带真实数据

 简单请求和预检请求的区别:

简单请求的特点:客户端与服务器之间只会发生一次请求

预检请求的特点:客户端与服务器之间会发生两次请求,OPTION预检请求成功之后,才会发起真正的请求

 JSONP接口

JSONP概念与特点

概念:浏览器1通过<script>标签的src属性,请求服务器上的数据,同时,服务器返回一个函数的调用,这种请求数据的方式叫做JSONP

特点

  1. JSONP不属于真正的Ajax请求,因为他没有使用XMLHttpRequest这个对象
  2. JSONP仅支持GET请求,不支持POST,PUT,DELETE等请求

如果项目中已经配置了CORS跨域资源共享,为了防止冲突,必须在配置CORS中间件之前声明JSONP的接口,否则JSONP接口会被处理成开启了CORS的接口,示例代码如下:

实现JSONP接口的步骤

  1. 获取客户端发送过来的回调函数的名字
  2. 得到要通过JSONP形式发送给客户端的数据
  3. 根据前两步得到的数据,拼接出一个函数调用的字符串
  4. 把上一步拼接得到的字符串,响应给客户端的<script>标签进行解析执行
// 必须在配置 cors 中间件之前,配置 JSONP 的接口
app.get('/api/jsonp', (req, res) => {
  // TODO: 定义 JSONP 接口具体的实现过程
  // 1. 得到函数的名称
  const funcName = req.query.callback//查询字符串中的callback属性的值
  // 2. 定义要发送到客户端的数据对象
  const data = { name: 'zs', age: 22 }
  // 3. 拼接出一个函数的调用
  const scriptStr = `${funcName}(${JSON.stringify(data)})`
  // 4. 把拼接的字符串,响应给客户端
  res.send(scriptStr)
})

在网页中使用jQuery发起JSONP请求

调用$.ajax()函数,提供JSONP的配置选项,从而发起JSONP请求,示例代码如下:

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值