服务是一组可重用的功能。它们对于遵守“不要重复自己”(DRY) 编程理念和简化 控制器 逻辑特别有用。
该图表示请求如何通过 Strapi 后端的简化版本,其中突出显示了服务。后端定制介绍页面包含一个完整的交互式图表。
实施
服务可以手动生成或添加。Strapi 提供了一个 createCoreService
工厂函数,可自动生成核心服务并允许构建自定义服务或扩展或替换生成的服务。
添加新服务
新服务可以通过以下方式实现:
- 使用 交互式 CLI 命令
strapi generate
- 或通过在相应文件夹中创建 JavaScript 文件手动实现(参见 项目结构):
- API 服务为
./src/api/[api-name]/services/
- 或 插件服务 为
./src/plugins/[plugin-name]/services/
。
要手动创建服务,请导出一个返回服务实现(即具有方法的对象)的工厂函数。此工厂函数接收“strapi”实例:
// ./src/api/restaurant/services/restaurant.js
const { createCoreService } = require('@strapi/strapi').factories
module.exports = createCoreService('api::restaurant.restaurant', ({ strapi }) => ({
// 方法 1:创建全新的自定义服务
async exampleService(...args) {
const response = { okay: true }
if (response.okay === false) {
return { response, error: true }
}
return response
},
// 方法 2:包装核心服务(保留核心逻辑)
async find(...args) {
// 调用默认核心控制器
const { results, pagination } = await super.find(...args)
// 一些自定义逻辑
results.forEach((result) => {
result.counter = 1
})
return { results, pagination }
},
// 方法 3:替换核心服务
async findOne(entityId, params = {}) {
return strapi.entityService.findOne('api::restaurant.restaurant', entityId, this.getFetchParams(params))
}
}))
要开始创建您自己的服务,请参阅 实体服务 API 文档中的 Strapi 内置函数。
自定义电子邮件服务示例(使用 Nodemailer)
服务的目标是存储可重复使用的函数。sendNewsletter
服务可用于从代码库中具有特定用途的不同函数发送电子邮件:
// ./src/api/restaurant/services/restaurant.js
const { createCoreService } = require('@strapi/strapi').factories
const nodemailer = require('nodemailer') // 需要安装 nodemailer (npm install nodemailer)
// 使用 SMTP 传输创建可重复使用的传输器对象。
const transporter = nodemailer.createTransport({
service: 'Gmail',
auth: {
user: 'user@gmail.com',
pass: 'password',
},
})
module.exports = createCoreService('api::restaurant.restaurant', ({ strapi }) => ({
sendNewsletter(from, to, subject, text) {
// 设置电子邮件数据。
const options = {
from,
to,
subject,
text,
}
// 返回发送电子邮件的函数的承诺。
return transporter.sendMail(options)
},
}))
该服务现在可通过 strapi.service('api::restaurant.restaurant').sendNewsletter(...args)
全局变量使用。它可以在代码库的另一部分使用,例如在以下控制器中:
// ./src/api/restaurant/controllers/restaurant.js
module.exports = createCoreController('api::restaurant.restaurant', ({ strapi }) => ({
// GET /hello
async signup(ctx) {
const { userData } = ctx.body
// 将新用户存储在数据库中。
const user = await strapi.service('plugin::users-permissions.user').add(userData)
// 发送电子邮件以验证其订阅。
strapi.service('api::restaurant.restaurant').sendNewsletter('welcome@mysite.com', user.email, 'Welcome', '...')
// 向服务器发送响应。
ctx.send({
ok: true,
})
},
}))
当创建新的 内容类型 时,Strapi 会构建一个带有占位符代码的通用服务,随时可以进行自定义。
要了解自定义服务的可能高级用法,请阅读后端自定义示例手册的 服务和控制器 页面。
扩展核心服务
核心服务是为每种内容类型创建的,控制器 可以使用它通过 Strapi 项目执行可重用逻辑。可以自定义核心服务以实现您自己的逻辑。以下代码示例应该可以帮助您入门。
可以通过创建自定义服务并将其命名为与核心服务相同的名称(例如find
、findOne
、create
、update
或delete
)来完全替换核心服务。
Collection type examples
async find(params) {
// some logic here
const { results, pagination } = await super.find(params);
// some more logic
return { results, pagination };
}
Single type examples
async find(params) {
// some logic here
const entity = await super.find(params);
// some more logic
return entity;
}
核心服务中的 find()
方法可以使用两种类型的分页,即按页面或按偏移量分页(请参阅 REST API)。
用法
创建服务后,即可从 controllers 或其他服务访问该服务:
// 访问 API 服务
strapi.service('api::apiName.serviceName').FunctionName()
// 访问插件服务
strapi.service('plugin::pluginName.serviceName').FunctionName()
在上面的语法示例中,serviceName
是 API 服务的服务文件的名称,或用于将服务文件导出到插件服务的 services/index.js
的名称。
要列出所有可用服务,请运行“yarn strapi services:list”。