服务
¥Services
服务是一组可重用的功能。它们对于尊重 "不要重复自己" (DRY) 编程概念和简化 controllers 逻辑特别有用。
¥Services are a set of reusable functions. They are particularly useful to respect the "don’t repeat yourself" (DRY) programming concept and to simplify controllers logic.
执行
¥Implementation
服务可以 手动生成或添加。Strapi 提供了 createCoreService
工厂功能,可自动生成核心服务并允许构建自定义服务或 扩展或替换生成的服务。
¥Services can be generated or added manually. Strapi provides a createCoreService
factory function that automatically generates core services and allows building custom ones or extend or replace the generated services.
添加新服务
¥Adding a new service
可以实现一个新的服务:
¥A new service can be implemented:
-
¥with the interactive CLI command
strapi generate
-
或通过在适当的文件夹中手动创建 JavaScript 文件(参见 项目结构):
¥or manually by creating a JavaScript file in the appropriate folder (see project structure):
-
./src/api/[api-name]/services/
用于 API 服务¥
./src/api/[api-name]/services/
for API services -
或
./src/plugins/[plugin-name]/services/
对应 插件服务。¥or
./src/plugins/[plugin-name]/services/
for plugin services.
-
要手动创建服务,请导出返回服务实现的工厂函数(即具有方法的对象)。该工厂函数接收 strapi
实例:
¥To manually create a service, export a factory function that returns the service implementation (i.e. an object with methods). This factory function receives the strapi
instance:
- JavaScript
- TypeScript
const { createCoreService } = require('@strapi/strapi').factories;
module.exports = createCoreService('api::restaurant.restaurant', ({ strapi }) => ({
// Method 1: Creating an entirely new custom service
async exampleService(...args) {
let response = { okay: true }
if (response.okay === false) {
return { response, error: true }
}
return response
},
// Method 2: Wrapping a core service (leaves core logic in place)
async find(...args) {
// Calling the default core controller
const { results, pagination } = await super.find(...args);
// some custom logic
results.forEach(result => {
result.counter = 1;
});
return { results, pagination };
},
// Method 3: Replacing a core service
async findOne(documentId, params = {}) {
return strapi.documents('api::restaurant.restaurant').findOne(documentId, this.getFetchParams(params));
}
}));
import { factories } from '@strapi/strapi';
export default factories.createCoreService('api::restaurant.restaurant', ({ strapi }) => ({
// Method 1: Creating an entirely custom service
async exampleService(...args) {
let response = { okay: true }
if (response.okay === false) {
return { response, error: true }
}
return response
},
// Method 2: Wrapping a core service (leaves core logic in place)
async find(...args) {
// Calling the default core controller
const { results, pagination } = await super.find(...args);
// some custom logic
results.forEach(result => {
result.counter = 1;
});
return { results, pagination };
},
// Method 3: Replacing a core service
async findOne(documentId, params = {}) {
return strapi.documents('api::restaurant.restaurant').findOne(documentId, this.getFetchParams(params));
}
}));
要开始创建你自己的服务,请参阅 文档服务 API 文档中的 Strapi 内置函数。
¥To get started creating your own services, see Strapi's built-in functions in the Document Service API documentation.
Example of a custom email service (using Nodemailer)
服务的目标是存储可重用的功能。sendNewsletter
服务可用于从我们的代码库中具有特定目的的不同功能发送电子邮件:
¥The goal of a service is to store reusable functions. A sendNewsletter
service could be useful to send emails from different functions in our codebase that have a specific purpose:
- JavaScript
- TypeScript
const { createCoreService } = require('@strapi/strapi').factories;
const nodemailer = require('nodemailer'); // Requires nodemailer to be installed (npm install nodemailer)
// Create reusable transporter object using SMTP transport.
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) {
// Setup e-mail data.
const options = {
from,
to,
subject,
text,
};
// Return a promise of the function that sends the email.
return transporter.sendMail(options);
},
}));
import { factories } from '@strapi/strapi';
const nodemailer = require('nodemailer'); // Requires nodemailer to be installed (npm install nodemailer)
// Create reusable transporter object using SMTP transport.
const transporter = nodemailer.createTransport({
service: 'Gmail',
auth: {
user: 'user@gmail.com',
pass: 'password',
},
});
export default factories.createCoreService('api::restaurant.restaurant', ({ strapi }) => ({
sendNewsletter(from, to, subject, text) {
// Setup e-mail data.
const options = {
from,
to,
subject,
text,
};
// Return a promise of the function that sends the email.
return transporter.sendMail(options);
},
}));
现在可以通过 strapi.service('api::restaurant.restaurant').sendNewsletter(...args)
全局变量使用该服务。它可以在代码库的另一部分中使用,例如在以下控制器中:
¥The service is now available through the strapi.service('api::restaurant.restaurant').sendNewsletter(...args)
global variable. It can be used in another part of the codebase, like in the following controller:
- JavaScript
- TypeScript
module.exports = createCoreController('api::restaurant.restaurant', ({ strapi }) => ({
// GET /hello
async signup(ctx) {
const { userData } = ctx.body;
// Store the new user in database.
const user = await strapi.service('plugin::users-permissions.user').add(userData);
// Send an email to validate his subscriptions.
strapi.service('api::restaurant.restaurant').sendNewsletter('welcome@mysite.com', user.email, 'Welcome', '...');
// Send response to the server.
ctx.send({
ok: true,
});
},
}));
export default factories.createCoreController('api::restaurant.restaurant', ({ strapi }) => ({
// GET /hello
async signup(ctx) {
const { userData } = ctx.body;
// Store the new user in database.
const user = await strapi.service('plugin::users-permissions.user').add(userData);
// Send an email to validate his subscriptions.
strapi.service('api::restaurant.restaurant').sendNewsletter('welcome@mysite.com', user.email, 'Welcome', '...');
// Send response to the server.
ctx.send({
ok: true,
});
},
}));
当创建新的 content-type 时,Strapi 会使用占位符代码构建通用服务,准备进行自定义。
¥When a new content-type is created, Strapi builds a generic service with placeholder code, ready to be customized.
拓展核心服务
¥Extending core services
核心服务是为每种内容类型创建的,controllers 可以使用核心服务通过 Strapi 项目执行可重用逻辑。可以定制核心服务来实现你自己的逻辑。以下代码示例应该可以帮助你入门。
¥Core services are created for each content-type and could be used by controllers to execute reusable logic through a Strapi project. Core services can be customized to implement your own logic. The following code examples should help you get started.
核心服务可以完全替换为 创建定制服务,并将其命名为与核心服务相同的名称(例如 find
、findOne
、create
、update
或 delete
)。
¥A core service can be replaced entirely by creating a custom service and naming it the same as the core service (e.g. find
, findOne
, create
, update
, or delete
).
Collection type examples
- find()
- findOne()
- create()
- update()
- delete()
async find(params) {
// some logic here
const { results, pagination } = await super.find(params);
// some more logic
return { results, pagination };
}
async findOne(documentId, params) {
// some logic here
const result = await super.findOne(documentId, params);
// some more logic
return result;
}
async create(params) {
// some logic here
const result = await super.create(params);
// some more logic
return result;
}
async update(documentId, params) {
// some logic here
const result = await super.update(documentId, params);
// some more logic
return result;
}
async delete(documentId, params) {
// some logic here
const result = await super.delete(documentId, params);
// some more logic
return result;
}
Single type examples
- find()
- update()
- delete()
async find(params) {
// some logic here
const document = await super.find(params);
// some more logic
return document;
}
async createOrUpdate({ data, ...params }) {
// some logic here
const document = await super.createOrUpdate({ data, ...params });
// some more logic
return document;
}
async delete(params) {
// some logic here
const document = await super.delete(params);
// some more logic
return document;
}
用法
¥Usage
创建服务后,可以从 controllers 或其他服务访问它:
¥Once a service is created, it's accessible from controllers or from other services:
// access an API service
strapi.service('api::apiName.serviceName').FunctionName();
// access a plugin service
strapi.service('plugin::pluginName.serviceName').FunctionName();
在上面的语法示例中,serviceName
是 API 服务的服务文件的名称,或者用于将服务文件导出到 services/index.js
的插件服务的名称。
¥In the syntax examples above, serviceName
is the name of the service file for API services or the name used to export the service file to services/index.js
for plugin services.
要列出所有可用服务,请运行 yarn strapi services:list
。
¥To list all the available services, run yarn strapi services:list
.