Morgan Express中间件库原理解析与代码示例

本文深入解析Morgan Express中间件库的工作原理,涵盖中间件概念、令牌系统、初始化过程和请求捕获机制,通过详细代码示例展示如何实现HTTP请求日志记录功能。

Morgan Express中间件库工作原理详解

Morgan是一个Express中间件库,用于检查HTTP请求并将请求详情记录到输出中。它是目前最流行的Express中间件库之一,拥有超过8,000个GitHub星标和9,000多个npm库依赖。GitHub报告显示,Morgan至少被360万个代码库使用。

什么是Express中间件?

根据Express文档,中间件是一个能够访问请求和响应对象以及Express请求周期中next函数的函数。它们通常用于在路由处理器处理请求之前或之后拦截请求以执行副作用操作。

中间件可用于:

  • 修改请求和响应对象:通过附加属性和头部信息来修改请求和响应对象
  • 终止请求-响应周期:在路由处理器处理请求之前或之后终止请求并向客户端发送响应
  • 执行堆栈中的下一个中间件:通过next函数参数触发后续中间件的执行

中间件的基本接口如下:

1
2
3
4
function middleware(request, response, next) {
    // 执行中间件时的操作
    next() // 执行下一个中间件
}

错误处理中间件接受四个参数:

1
function errorHandlerMiddleware(error, request, response, next) {}

像Morgan和cors这样的中间件是高阶函数。它们在初始化时接受配置参数,并返回一个中间件函数,在请求到达时由Express执行。

1
2
3
4
5
6
7
8
function initialise(...configArgs) {
    // 在此处使用配置参数
    return function middleware(request, response, next) {
        // 也可以在此处使用配置参数
        // 请求到达此中间件时执行的操作
        next() // 执行下一个中间件
    }
}

Morgan工作原理概述

1
2
3
4
import morgan from "morgan"
// morgan(format, [options])
morgan("tiny") // 初始化morgan并返回中间件
// 示例输出:GET /tiny 200 2 - 0.188 ms

Morgan通过执行必需的format参数和可选的options参数进行初始化。format参数可以是:

  • 预定义的Morgan格式名称
  • 包含预定义令牌的格式字符串
  • 返回字符串形式日志输出的自定义格式函数

options参数是可选的,它是一个包含三个属性的对象:

  • immediate(布尔值):如果为true,将在接收请求时创建日志输出,而不是在发送响应时。默认为false
  • skip(函数):该函数接受请求和响应对象作为参数,并根据其中的逻辑返回布尔值。如果返回true,则不会记录该请求的日志行
  • stream(可写流):用于写入日志行的输出流。默认为process.stdout,但也可以是文件

什么是Morgan令牌?

Morgan令牌是以冒号为前缀的字符串,对应请求或响应对象的属性或用户生成的值。例如,请求方法的令牌是’:method’,响应状态码的令牌是’:status’。

可以使用morgan.token函数创建新令牌:

1
2
3
morgan.token('type', function (req, res) {
    return res.headers['content-type']
})

Morgan有预定义的命名格式(tiny、dev、short、combined、common),每个命名格式都有特定的令牌集和配置。

Morgan初始化时发生了什么?

当Morgan初始化时,它会复制提供给它的参数。对于未提供的参数,Morgan会设置默认值。

Morgan然后设置formatLine函数 - 该函数在执行时创建并返回请求的日志行。首先,Morgan检查format是否为格式函数。如果是,则将格式函数分配给formatLine,然后Morgan设置输出流。

如果format不是函数,则将其作为参数传递给getFormatFunction。该函数检查format是否为:

  • Morgan的命名格式之一或通过morgan.format创建的用户定义命名格式
  • 令牌集

如果两者都不是,Morgan使用默认命名格式。

compile函数

compile函数接受令牌集并返回具有格式函数接口的函数。它使用JavaScript的replace方法和正则表达式搜索令牌集中所有令牌的出现并将其替换。

Morgan捕获请求时发生了什么?

当Morgan捕获请求时,它使用getip函数存储客户端的IP地址。接下来,它存储触发请求的时间,并将其附加到请求对象的_startAt和_startTime属性。

  • _startAt用于计算请求进入Morgan和响应完全写入连接之间的总时间(毫秒)
  • _startTime用于计算响应时间 - 即从Morgan捕获请求到响应头被写入的时间

接下来,Morgan尝试通过执行logRequest函数生成请求的日志行并记录它。

在logRequest内部,Morgan检查skip选项的值。如果它是一个函数,则执行它,如果返回true,Morgan不会为该请求创建日志输出并退出。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
function logRequest () {
  if (skip !== false && skip(req, res)) {
    debug('skip request')
    return
  }

  var line = formatLine(morgan, req, res)

  if (line == null) {
    debug('skip line')
    return
  }

  stream.write(line + '\n')
};

如果skip为false或执行结果为false,Morgan使用formatLine生成请求的日志行。如果日志行为null,Morgan退出,否则将日志行发送到输出介质并退出。

后续步骤

您已经学习了Morgan Express中间件如何输出日志。现在您具备了基础技能,可以学习其他中间件或Node.js库,研究它们的工作原理。选择一个,研究它,写下您的发现,并与他人分享。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计