express中间件

Posted ygjzs

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了express中间件相关的知识,希望对你有一定的参考价值。

加载静态资源--复习以前学的express

express怎么用?
如何获取请求?
如何处理响应?
如何对向外暴露静态资源?
express核心:中间件:如何理解?

中间件:用来处理 http 请求的一个具体的环节(可能要执行某个具体的处理函数)
中间件一般都是通过修改 req 或者 res 对象来为后续的处理提供便利的使用
中间件分类:
use(function () {req, res, next}) 不关心请求方法和请求路径,没有具体路由规则,任何请求都会进入该中间件
use(‘请求路径‘, function (req, res, next) {}) 不关心请求方法,只关心请求路劲的中间件
get(‘请求路径‘, function (req, res, next) {}) 具体路由规则中间件
post(‘请求路径‘, function (req, res, next) {})

// 需求一:用户访问 / 响应 hello world
// 需求二:用户访问 /login 响应 hello login
// 需求三:将 public 目录开放为类似于 Apache 一样,可以直接通过路径去取访问该目录中的任意资源

const express = require('express')
const fs = require('fs')

// 1. 调用 express() 方法,得到一个 app 实例接口对象(类似于 http.createServer 得到的 server 实例)
const app = express()

// 这个就表示是一个中间件
// 目前下面这个 API ,任何请求进来都会执行对应的处理函数
// 不关心当前请求的具体请求方法和请求路径
// 该代码内部如果不发送响应或者做进一步处理则代码会一直停在这里,不会往后执行
app.use(function (req, res, next) {
  const urlPath = req.path
  // /puiblic/a.css
  // /public/main.js
  if (urlPath.startsWith('/public/')) {
    const filePath = `.${urlPath}` // 这里加 . 的原因是因为如果读文件是以 / 开头的则会去当前文件所属磁盘根目录去查找
    fs.readFile(filePath, (err, data) => {
      if (err) {
        return res.end('404 Not Found.')
      }
      res.end(data)
    })
  } else {
    // 如果请求路径不是以 /public/ 开头的,则调用 next ,next 是一个函数(不确定)
    // 这里调用了 next  的目的就是告诉 Express 继续往后执行:中间件
    // 具体执行哪个中间件:取决于对应的中间件的类型
    next()
  }
})

// 2. 通过 app 设置对应的路径对应的请求处理函数
//    回调处理函数中:
//      req 请求对象:用来获取当前客户端的一些请求数据或者请求报文信息
//          例如 req.query 用来获取查询字符串数据
//               req.method 用来当前请求方法
//      res 响应对象:用来向当前请求客户端发送消息数据的
//          例如 res.write('响应数据')
//               res.end() 结束响应
app.get('/', (req, res) => {
  res.write('hello ')
  res.write('expres')
  res.end()
})

app.get('/login', (req, res) => {
  // end 用来结束响应的同时发送响应数据
  res.end('hello login')
})

// 3. 开启监听,启动服务器
app.listen(3000, () => {
  console.log('服务已启动,请访问:http://127.0.0.1:3000/')
})

利用中间件实现封装static中间件

对于相同请求重复两次,会怎样处理?------对于一次请求来说,只能响应一次

express中的中间件
app.use(‘/public‘, express.static(‘开放目录的路径‘))

在 use 方法中,如果指定了第一个路径参数,则通过req.path 获取到的是不包含该请求路径的字符串
例如当前请求路劲是/public/a.jpg则通过req.path拿到的就是 a.jpg
/public/a/a.css a/a.css
目前已知传递给了 static 方法一个绝对路径c:/project/public
假设目前请求是 /public/a/a.css 拿到的 req.path a/a.css
c:/project/public + a/a.cs 拼接起来,读取

const express = require('express')
const fs = require('fs')
const path = require('path')
const static = require('./middlwares/static')

const app = express()

app.use('/public', static(path.join(__dirname, 'public')))
app.use('/node_modules', static(path.join(__dirname, 'node_modules')))

app.get('/', (req, res, next) => {
  console.log('/ 111')
  res.end('hello')
  next()
})

app.get('/', (req, res, next) => {
  console.log('/ 222')
  // 1. 在 http 中,没有请求就没有响应,服务端不可能主动给客户端发请求,就是一问一答的形式
  // 2. 对于一次请求来说,只能响应一次,如果发送了多次响应,则只有第一次生效
  res.end('world')
  next()
})

app.use((req, res, next) => {
  console.log(111)
  // 假如有请求进入了该中间件,这里调用的 next 会执行下一个能匹配的中间件
  // get /
  // get /a
  next()
})

// /  111 222 333
// /a 111 use /a
// /a next() 111 use/a 222 333
app.use('/a', (req, res, next) => {
  console.log('use /a')
  next()
})

app.use((req, res, next) => {
  console.log(222)
  next()
})

app.use((req, res, next) => {
  console.log(333)
})

app.listen(3000, () => {
  console.log('服务已启动,请访问:http://127.0.0.1:3000/')
})

加载的封装的static模块

const fs = require('fs')
const path = require('path')

module.exports = function (dirPath) {
  // 这里不需要调用 next
  // 因为如果不是以 /public 开头的,当前这个中间件压根儿就不会进来
  return (req, res, next) => {
    const filePath = path.join(dirPath, req.path)
    fs.readFile(filePath, (err, data) => {
      if (err) {
        return res.end('404 Not Found.')
      }
      res.end(data)
    })
  }
}

处理日志

该处理放在前面,要保证其每次发起请求都被执行到,并记录日志

const express = require('express')
const fs = require('fs')
const path = require('path')
const static = require('./middlwares/static')

const app = express()

app.use((req, res, next) => {
  const log = `请求方法:${req.method}  请求路径:${req.url} 请求时间:${+new Date()}
`
  fs.appendFile('./log.txt', log, err => {
    if (err) {
      return console.log('记录日志失败了')
    }
    next()
  })
})


app.listen(3000, () => {
  console.log('服务已启动,请访问:http://127.0.0.1:3000/')
})

日志文件

请求方法:GET  请求路径:/ 请求时间:1486537214226请求方法:GET  请求路径:/favicon.ico 请求时间:1486537214735请求方法:GET  请求路径:/ 请求时间:1486537249534
请求方法:GET  请求路径:/favicon.ico 请求时间:1486537250459
请求方法:GET  请求路径:/ 请求时间:1486537253228
请求方法:GET  请求路径:/favicon.ico 请求时间:1486537254353

错误处理和404处理

注意这里这两种中间的所处位置

const express = require('express')
const fs = require('fs')

const app = express()

app.get('/', function aaa(req, res, next) {
  // 通过 JSON.parse 解析查询字符串中的某个
  try {
    const data = JSON.parse('{abc')
    res.json(data)
  } catch (e) {
    next(e)
  }
})

app.get('/a', (req, res, next) => {
  fs.readFile('./dnsajndsja', (err, data) => {
    if (err) {
      // 这里调用的 next 会被 app.use((err, req, res, next)) 这个中间件匹配到
      next(err)
    }
  })
})

app.get('/b', (req, res, next) => {
  res.end('hello index')
})

// 该中间件只有被带有参数的 next 才能调用到
//    带参数的 next 只能被具有四个参数的处理中间件匹配到
// 注意:这里一定要写全四个参数,否则会导致问题
// 这个中间件就是用来全局统一处理错误的
app.use((err, req, res, next) => {
  const error_log = `
====================================
错误名:${err.name}
错误消息:${err.message}
错误堆栈:${err.stack}
错误时间:${new Date()}
====================================


`
  fs.appendFile('./err_log.txt', error_log, err => {
    res.writeHead(500, {})
    res.end('500 服务器正忙,请稍后重试')
  })
})

// 404 处理中间件
app.use((req, res, next) => {
  res.end('404')
})

app.listen(3000, () => {
  console.log('running...')
})

错误日志文件


====================================
错误名:SyntaxError
错误消息:Unexpected token a in JSON at position 1
错误堆栈:SyntaxError: Unexpected token a in JSON at position 1
    at Object.parse (native)
    at aaa (C:UserslpzDesktop0-Node-第2天-内容1、内容24-源代码express-demoapp-middleware-404.js:9:23)
    at Layer.handle [as handle_request] (C:UserslpzDesktop0-Node-第2天-内容1、内容24-源代码express-demo
ode_modulesexpresslib
outerlayer.js:95:5)
    at next (C:UserslpzDesktop0-Node-第2天-内容1、内容24-源代码express-demo
ode_modulesexpresslib
outer
oute.js:131:13)
    at Route.dispatch (C:UserslpzDesktop0-Node-第2天-内容1、内容24-源代码express-demo
ode_modulesexpresslib
outer
oute.js:112:3)
    at Layer.handle [as handle_request] (C:UserslpzDesktop0-Node-第2天-内容1、内容24-源代码express-demo
ode_modulesexpresslib
outerlayer.js:95:5)
    at C:UserslpzDesktop0-Node-第2天-内容1、内容24-源代码express-demo
ode_modulesexpresslib
outerindex.js:277:22
    at Function.process_params (C:UserslpzDesktop0-Node-第2天-内容1、内容24-源代码express-demo
ode_modulesexpresslib
outerindex.js:330:12)
    at next (C:UserslpzDesktop0-Node-第2天-内容1、内容24-源代码express-demo
ode_modulesexpresslib
outerindex.js:271:10)
    at expressInit (C:UserslpzDesktop0-Node-第2天-内容1、内容24-源代码express-demo
ode_modulesexpresslibmiddlewareinit.js:33:5)
错误时间:Wed Feb 08 2017 15:52:52 GMT+0800 (中国标准时间)
====================================



====================================
错误名:Error
错误消息:ENOENT: no such file or directory, open 'C:UserslpzDesktop0-Node-第2天-内容1、内容24-源代码express-demodnsajndsja'
错误堆栈:Error: ENOENT: no such file or directory, open 'C:UserslpzDesktop0-Node-第2天-内容1、内容24-源代码express-demodnsajndsja'
    at Error (native)
错误时间:Wed Feb 08 2017 15:53:14 GMT+0800 (中国标准时间)
====================================

Express API

express官网

  • express()
  • Application
  • Request
  • Response
  • Router

以上是关于express中间件的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 express 中间件处理所有路由?

关于Express的中间件

express-middleware

使用express.static中间件

二express中间件

如何使用 jest 在 javascript 中测试 try catch 代码并在 express 中包含带有中间件的 next() 调用?