Strapi的官方文档提供了扩展插件的方式,例如在src/extensions/users-permissions/strapi-server.ts
中自定义,但文档提供的例子直接重写了原有插件的函数,而无法通过类似自定义API调用super.find()
等方式调用原有插件的函数。
// 自定义API的方式
const uid: Common.UID.ContentType = 'api::api-name···';
export default factories.createCoreController(uid, ({ strapi: Strapi }) => ({
async find(ctx: Context) {
// TODO ...
return await super.find(ctx);
},
}));
此时需要将原插件的函数复制出来,并在重写的函数里调用,以自定义/users/me接口为例。
users-permissions的/users/me接口提供了对获得当前用户信息的方法,若需要自定义获得哪些信息,可通过修改原有me函数实现,users-permissions对me函数的定义位于@strapi/plugin-users-permissions/server/controllers/user.js
中,尝试通过导入这个函数,并在自定义的me函数中调用这个函数。
失败的方案
进一步观察代码,该me函数位于user.js的导出对象中,user.js的导出对象是controllers导出对象的成员,位于@strapi/plugin-users-permissions/server/controllers/index.js
中,controllers又是server导出函数的返回值成员,位于@strapi/plugin-users-permissions/server/index.js
中,故要访问user.js中的方法,可通过以下方式。
const { controllers } = server();
controllers.user.me(ctx);
导入原有me函数时,要注意users-permissions插件的导出对象名,查看/@strapi/plugin-users-permissions/package.json
发现,其exports
对象导出名为strapi-server
,故实际引用users-permissions的server对象,需要使用
import server from '@strapi/plugin-users-permissions/strapi-server';
此时,如果在重写的me函数中直接调用controllers.user.me(ctx);
则会陷入死递归,因为Strapi初始化时已经将原有的me函数覆盖为自定义的me函数,实际上调用controllers.user.me(ctx);
即调用的当前函数,故该方法无法实现。
可行的方案
既然如此,就把整个原有的me函数照搬过来,相当于复制了一份me函数,进入@strapi/plugin-users-permissions/server/controllers/user.js
中,发现me函数的定义如下:
/**
* Retrieve authenticated user.
* @return {Object|Array}
*/
async me(ctx) {
const authUser = ctx.state.user;
const { query } = ctx;
if (!authUser) {
return ctx.unauthorized();
}
await validateQuery(query, ctx);
const sanitizedQuery = await sanitizeQuery(query, ctx);
const user = await getService('user').fetch(authUser.id, sanitizedQuery);
ctx.body = await sanitizeOutput(user, ctx);
},
其调用了validateQuery
、sanitizeQuery
、getService
和sanitizeOutput
4个方法,其中validateQuery
、sanitizeQuery
和sanitizeOutput
均在user.js中有定义,可直接照搬
// @strapi/plugin-users-permissions/server/controllers/user.js
const utils = require('@strapi/utils');
const { sanitize, validate } = utils;
const sanitizeOutput = async (user, ctx) => {
const schema = strapi.getModel('plugin::users-permissions.user');
const { auth } = ctx.state;
return sanitize.contentAPI.output(user, schema, { auth });
};
const validateQuery = async (query, ctx) => {
const schema = strapi.getModel('plugin::users-permissions.user');
const { auth } = ctx.state;
return validate.contentAPI.query(query, schema, { auth });
};
const sanitizeQuery = async (query, ctx) => {
const schema = strapi.getModel('plugin::users-permissions.user');
const { auth } = ctx.state;
return sanitize.contentAPI.query(query, schema, { auth });
};
而getService
位于@strapi/plugin-users-permissions/server/utils/index.js
中
// @strapi/plugin-users-permissions/server/utils/index.js
const sanitize = require('./sanitize');
const getService = (name) => {
return strapi.plugin('users-permissions').service(name);
};
module.exports = {
getService,
sanitize,
};
观察@strapi/plugin-users-permissions/server/index.js
发现,utils
并未被server
导出,故无法在外部引用该函数
// @strapi/plugin-users-permissions/server/index.js
const register = require('./register');
const bootstrap = require('./bootstrap');
const contentTypes = require('./content-types');
const middlewares = require('./middlewares');
const services = require('./services');
const routes = require('./routes');
const controllers = require('./controllers');
const config = require('./config');
module.exports = () => ({
register,
bootstrap,
config,
routes,
controllers,
contentTypes,
middlewares,
services,
});
不过所幸getService
仅为一句简单的return strapi.plugin('users-permissions').service(name);
并未引用新的外部依赖,故可以在自定义的文件中重新定义一次该函数。
故最终实现代码如下:
// src/extensions/users-permissions/strapi-server.ts
import type { Strapi, Common, CoreApi } from '@strapi/types';
import { sanitize, validate } from '@strapi/utils';
const getService = (name) => {
return strapi.plugin('users-permissions').service(name);
};
const validateQuery = async (query, ctx) => {
const schema = strapi.getModel('plugin::users-permissions.user');
const { auth } = ctx.state;
return validate.contentAPI.query(query, schema, { auth });
};
const sanitizeQuery = async (query, ctx) => {
const schema = strapi.getModel('plugin::users-permissions.user');
const { auth } = ctx.state;
return sanitize.contentAPI.query(query, schema, { auth });
};
const sanitizeOutput = async (user, ctx) => {
const schema = strapi.getModel('plugin::users-permissions.user');
const { auth } = ctx.state;
return sanitize.contentAPI.output(user, schema, { auth });
};
const originMe = async (ctx) => {
const authUser = ctx.state.user;
const { query } = ctx;
if (!authUser) {
return ctx.unauthorized();
}
await validateQuery(query, ctx);
const sanitizedQuery = await sanitizeQuery(query, ctx);
const user = await getService('user').fetch(authUser.id, sanitizedQuery);
// ctx.body = await sanitizeOutput(user, ctx);
return await sanitizeOutput(user, ctx);
};
export default async (plugin) => {
const uid: Common.UID.ContentType = 'plugin::users-permissions.user';
plugin.controllers.user.me = async (ctx) => {
// Custom code here;
const res = await originMe(ctx); // 调用了原有的me函数
return res;
};
return plugin;
};