创建命令

创建命令

除了使用 Ace 命令,你也可以将自定义命令作为应用代码库的一部分来创建。命令存储在 commands 目录(位于根级别)。你可以通过运行以下命令来创建命令。

相关阅读:Make command

node ace make:command greet

上述命令会在 commands 目录中创建一个 greet.ts 文件。Ace 命令由一个类表示,并且必须实现 run 方法来执行命令逻辑。

命令元数据

命令元数据包含命令名称描述帮助文本,以及用于配置命令行为的选项

import { BaseCommand } from '@adonisjs/core/ace'
import { CommandOptions } from '@adonisjs/core/types/ace'
export default class GreetCommand extends BaseCommand {
static commandName = 'greet'
static description = 'Greet a user by name'
static options: CommandOptions = {
startApp: false,
allowUnknownFlags: false,
staysAlive: false,
}
}
commandName

commandName 属性用于定义命令名称。命令名称不应包含空格。此外,建议避免在命令名称中使用 *& 或不熟悉的斜杠等特殊的字符。

命令名称可以位于某个命名空间之下。例如,要在 make 命名空间下定义一个命令,可以在其前面加上 make:

description

命令描述会显示在命令列表和命令帮助屏幕上。你必须保持描述简短,并使用帮助文本来书写更长的描述。

help

帮助文本用于书写更长的描述或展示用法示例。

export default class GreetCommand extends BaseCommand {
static help = [
'The greet command is used to greet a user by name',
'',
'You can also send flowers to a user, if they have an updated address',
'{{ binaryName }} greet --send-flowers',
]
}

{{ binaryName }} 变量替换是对用于执行 ace 命令的二进制文件的引用。

aliases

你可以使用 aliases 属性为命令名称定义一个或多个别名。

export default class GreetCommand extends BaseCommand {
static commandName = 'greet'
static aliases = ['welcome', 'sayhi']
}
options.startApp

默认情况下,AdonisJS 在运行 Ace 命令时不会启动应用。这样可以确保命令运行快速,并且无需经过应用启动阶段即可执行简单任务。

但是,如果你的命令依赖应用状态,你可以告诉 Ace 在执行命令之前启动应用。

import { BaseCommand } from '@adonisjs/core/ace'
import { CommandOptions } from '@adonisjs/core/types/ace'
export default class GreetCommand extends BaseCommand {
static options: CommandOptions = {
startApp: true
}
}
options.allowUnknownFlags

默认情况下,如果你向命令传递了未知的标志,Ace 会打印错误。但是,你可以使用 options.allowUnknownFlags 属性在命令级别禁用严格的标志解析。

import { BaseCommand } from '@adonisjs/core/ace'
import { CommandOptions } from '@adonisjs/core/types/ace'
export default class GreetCommand extends BaseCommand {
static options: CommandOptions = {
allowUnknownFlags: true
}
}
options.staysAlive

AdonisJS 会在执行完命令的 run 命令后隐式终止应用。但是,如果你想在命令中启动一个长时间运行的进程,就必须告诉 Ace 不要杀死该进程。

相关阅读:终止应用 以及 应用终止前的清理 章节。

import { BaseCommand } from '@adonisjs/core/ace'
import { CommandOptions } from '@adonisjs/core/types/ace'
export default class GreetCommand extends BaseCommand {
static options: CommandOptions = {
staysAlive: true
}
}

命令生命周期方法

你可以在一个命令类上定义以下生命周期方法,Ace 会按照预定义的顺序执行它们。

import { BaseCommand, args, flags } from '@adonisjs/core/ace'
export default class GreetCommand extends BaseCommand {
async prepare() {
console.log('preparing')
}
async interact() {
console.log('interacting')
}
async run() {
console.log('running')
}
async completed() {
console.log('completed')
}
}
方法 描述
prepare 这是 Ace 在命令上执行的第一个方法。此方法可以设置运行命令所需的状态或数据。
interact interact 方法在 prepare 方法之后执行。它可以用于向用户显示交互提示。
run 命令的主要逻辑放在 run 方法中。此方法在 interact 方法之后被调用。
completed completed 方法在运行完所有其他生命周期方法后被调用。此方法可以用于执行清理,或处理/展示其他方法抛出的异常。

依赖注入

Ace 命令是通过 IoC 容器 构建和执行的。因此,你可以在命令的生命周期方法上标注依赖类型提示,并使用 @inject 装饰器来解析它们。

为了演示,让我们将 UserService 类注入到所有生命周期方法中。

import { inject } from '@adonisjs/core'
import { BaseCommand } from '@adonisjs/core/ace'
import UserService from '#services/user_service'
export default class GreetCommand extends BaseCommand {
@inject()
async prepare(userService: UserService) {
}
@inject()
async interact(userService: UserService) {
}
@inject()
async run(userService: UserService) {
}
@inject()
async completed(userService: UserService) {
}
}

处理错误与退出码

命令抛出的异常会通过 CLI 日志记录器显示,并且命令的 exitCode 会被设置为 1(非零错误码表示命令执行失败)。

不过,你也可以通过将代码包裹在 try/catch 块中,或使用 completed 生命周期方法来捕获错误。在这两种情况下,记得更新命令的 exitCodeerror 属性。

使用 try/catch 处理错误

import { BaseCommand } from '@adonisjs/core/ace'
export default class GreetCommand extends BaseCommand {
async run() {
try {
await runSomeOperation()
} catch (error) {
this.logger.error(error.message)
this.error = error
this.exitCode = 1
}
}
}

在 completed 方法中处理错误

import { BaseCommand } from '@adonisjs/core/ace'
export default class GreetCommand extends BaseCommand {
async run() {
await runSomeOperation()
}
async completed() {
if (this.error) {
this.logger.error(this.error.message)
/**
* 通知 Ace 错误已被处理
*/
return true
}
}
}

终止应用

默认情况下,Ace 会在执行完命令后终止应用。但是,如果你启用了 staysAlive 选项,就必须使用 this.terminate 方法显式终止应用。

假设我们建立一个 redis 连接来监控服务器内存。我们监听 redis 连接上的 error 事件,并在连接失败时终止应用。

import { BaseCommand } from '@adonisjs/core/ace'
import { CommandOptions } from '@adonisjs/core/types/ace'
export default class GreetCommand extends BaseCommand {
static options: CommandOptions = {
staysAlive: true
}
async run() {
const redis = createRedisConnection()
redis.on('error', (error) => {
this.logger.error(error)
this.terminate()
})
}
}

在应用终止前进行清理

多个事件都可能触发应用终止,包括 SIGTERM 信号。因此,你必须在命令中监听 terminating 钩子来执行清理工作。

你可以在 prepare 生命周期方法中监听 terminating 钩子。

import { BaseCommand } from '@adonisjs/core/ace'
import { CommandOptions } from '@adonisjs/core/types/ace'
export default class GreetCommand extends BaseCommand {
static options: CommandOptions = {
staysAlive: true
}
prepare() {
this.app.terminating(() => {
// 执行清理
})
}
async run() {
}
}