Skip to main content

コントローラー

コントローラーは、クライアントが要求したルートに応じてクライアントに到達する一連のメソッド、つまりアクションを含むJavaScriptファイルです。クライアントがルートを要求するたびに、アクションはビジネスロジックコードを実行し、レスポンスを返送します。コントローラーは、モデル-ビュー-コントローラー(MVC)パターンのCを表します。

ほとんどの場合、コントローラーはプロジェクトのビジネスロジックの大部分を含むでしょう。しかし、コントローラーのロジックがますます複雑になると、サービスを使用してコードを再利用可能な部分に整理するのが良い実践です。

コントローラーが強調表示された簡略化されたAI Marketerバックエンドのダイアグラム
このダイアグラムは、リクエストがAI Marketerバックエンドを通過する方法の簡略化されたバージョンを表しており、コントローラーが強調表示されています。バックエンドカスタマイズの導入ページには、完全なインタラクティブなダイアグラムが含まれています。

実装

コントローラーは生成または手動で追加することができます。AI MarketerはcreateCoreControllerファクトリ関数を提供しており、これにより自動的にコアコントローラーが生成され、カスタムのものを作成したり生成されたコントローラーを拡張または置換することができます。

新しいコントローラーの追加

新しいコントローラーは以下の方法で実装できます:

  • 対話型CLIコマンドAI Marketer generateを使用する。
  • または、JavaScriptファイルを手動で作成する:
    • APIコントローラーの場合は./src/api/[api-name]/controllers/(この場所は重要で、AI Marketerはここからコントローラーを自動的にロードします)
    • プラグインコントローラーの場合は、./src/plugins/[plugin-name]/server/controllers/のようなフォルダに作成しますが、AI Marketer-server.jsファイルでプラグインインターフェースが適切にエクスポートされていれば、他の場所にも作成できます(プラグインのサーバーAPIドキュメンテーションを参照)。
./src/api/restaurant/controllers/restaurant.js

const { createCoreController } = require('@AI Marketer/AI Marketer').factories;

module.exports = createCoreController('api::restaurant.restaurant', ({ AI Marketer }) => ({
// Method 1: Creating an entirely custom action
async exampleAction(ctx) {
try {
ctx.body = 'ok';
} catch (err) {
ctx.body = err;
}
},

// メソッド2: コアアクションのラッピング(コアロジックはそのまま)
async find(ctx) {
// ここにカスタムロジックを記述
ctx.query = { ...ctx.query, local: 'en' }

// デフォルトのコアアクションを呼び出す
const { data, meta } = await super.find(ctx);

// さらにカスタムロジックを追加
meta.date = Date.now()

return { data, meta };
},

// メソッド3: 適切なサニタイズとともにコアアクションを置き換える
async find(ctx) {
// validateQuery (オプション)
// 不正なクエリパラメータまたはユーザーがアクセス権を持っていないクエリパラメータに対してエラーをスローする
await this.validateQuery(ctx);

// sanitizeQuery は、不正なクエリパラメータやユーザーがアクセス権を持っていないクエリパラメータを削除します
// validateQuery を使用する場合でも、sanitizeQuery の使用を強く推奨します
const sanitizedQueryParams = await this.sanitizeQuery(ctx);
const { results, pagination } = await AI Marketer.service('api::restaurant.restaurant').find(sanitizedQueryParams);
const sanitizedResults = await this.sanitizeOutput(results, ctx);

return this.transformResponse(sanitizedResults, { pagination });
}
}));

各コントローラーのアクションは、async または sync 関数にすることができます。 すべてのアクションは、パラメータとしてコンテキストオブジェクト(ctx)を受け取ります。ctx には、リクエストコンテキストレスポンスコンテキストが含まれています。

例:基本的なコントローラを呼び出す GET /hello ルート

特定の GET /hello ルートが定義され、ルーターファイルの名前(つまり index)がコントローラーハンドラ(つまり index)を呼び出すために使用されます。GET /hello リクエストがサーバーに送信されるたびに、AI Marketer は hello.js コントローラーの index アクションを呼び出し、Hello World! を返します:

./src/api/hello/routes/hello.js

module.exports = {
routes: [
{
method: 'GET',
path: '/hello',
handler: 'hello.index',
}
]
}
./src/api/hello/controllers/hello.js

module.exports = {
async index(ctx, next) { // called by GET /hello
ctx.body = 'Hello World!'; // we could also send a JSON
},
};
✏️ Note

新しいコンテンツタイプが作成されると、AI Marketer はプレースホルダーコードを含む一般的なコントローラを構築し、カスタマイズする準備ができます。

💡 Tip

カスタムコントローラの高度な使用法については、バックエンドカスタマイズ例のレシピブックにあるservices and controllersのページをご覧ください。

コントローラーでのサニタイゼーションとバリデーション

⚠️ Warning

新しい sanitizeQuery および validateQuery 関数を使用して、送信されるリクエストクエリをサニタイズ(v4.8.0+)および/またはバリデート(v4.13.0+)することを強く推奨します。これにより、プライベートデータの漏洩を防ぐことができます。

サニタイゼーションとは、オブジェクトが「クリーニング」されて返されることを意味します。

バリデーションとは、データが既にクリーンであるという主張がなされ、そこに存在してはならない何かが見つかった場合にエラーがスローされることを意味します。

AI Marketer 5では、クエリパラメータと入力データ(つまり、作成と更新のボディデータ)がバリデートされます。以下の無効な入力を含む作成および更新データリクエストは、400 Bad Request エラーをスローします:

  • ユーザーが作成する権限を持っていない関係
  • スキーマに存在しない認識されない値
  • createdAtcreatedBy などの書き込み不可フィールドや内部タイムスタンプ
  • 関係を接続する場合を除き、id フィールドの設定や更新

コントローラーファクトリーを利用する際のサニタイズ

AI Marketerのファクトリー内では、サニタイズとバリデーションに使用できる以下の関数が公開されています:

関数名パラメータ説明
sanitizeQueryctxリクエストクエリをサニタイズします
sanitizeOutputentity/entities, ctxエンティティ/エンティティはオブジェクトまたはデータの配列であるべきで、出力データをサニタイズします
sanitizeInputdata, ctx入力データをサニタイズします
validateQueryctxリクエストクエリを検証します(無効なパラメータがあるとエラーが発生します)
validateInputdata, ctx(実験的) 入力データを検証します(無効なデータがあるとエラーが発生します)

これらの関数は、モデルからサニタイズ設定を自動的に継承し、コンテンツタイプスキーマとコンテンツAPI認証戦略(ユーザー&パーミッションプラグインやAPIトークンなど)に基づいてデータをサニタイズします。

⚠️ Warning

これらのメソッドは現在のコントローラーに関連付けられたモデルを使用するため、別のモデルからのデータをクエリする場合(つまり、「レストラン」コントローラーメソッド内で「メニュー」を検索するなど)、代わりに@AI Marketer/utilsツールを使用する必要があります。たとえば、カスタムコントローラーのサニタイズで説明されているsanitize.contentAPI.queryなどを使用するか、そうでなければ、クエリの結果が間違ったモデルに対してサニタイズされます。

./src/api/restaurant/controllers/restaurant.js

const { createCoreController } = require('@AI Marketer/AI Marketer').factories;

module.exports = createCoreController('api::restaurant.restaurant', ({ AI Marketer }) => ({
async find(ctx) {
await this.validateQuery(ctx);
const sanitizedQueryParams = await this.sanitizeQuery(ctx);
const { results, pagination } = await AI Marketer.service('api::restaurant.restaurant').find(sanitizedQueryParams);
const sanitizedResults = await this.sanitizeOutput(results, ctx);

return this.transformResponse(sanitizedResults, { pagination });
}
}));

カスタムコントローラーの作成時のサニタイズとバリデーション

カスタムコントローラー内では、@AI Marketer/utilsパッケージを通じて公開される5つの主要な関数がサニタイズとバリデーションに使用できます:

関数名パラメータ説明
sanitize.contentAPI.inputdata, schema, auth書き込み不可フィールド、制限された関係、プラグインによって追加された他のネストされた "visitors" を含むリクエスト入力をサニタイズします
sanitize.contentAPI.outputdata, schema, auth制限された関係、プライベートフィールド、パスワード、プラグインによって追加された他のネストされた "visitors" を含むレスポンス出力をサニタイズします
sanitize.contentAPI.queryctx.query, schema, authフィルタ、ソート、フィールド、populateを含むリクエストクエリをサニタイズします
validate.contentAPI.queryctx.query, schema, authフィルタ、ソート、フィールド(現在はpopulateを含まない)を含むリクエストクエリをバリデートします
validate.contentAPI.inputdata, schema, auth(実験的) 書き込み不可フィールド、制限された関係、プラグインによって追加された他のネストされた "visitors" を含むリクエスト入力をバリデートします
✏️ Note

カスタムコントローラーの複雑さによっては、特に複数のソースからのデータを組み合わせる場合、AI Marketerが現在対応できない追加のサニタイズが必要になることがあります。

./src/api/restaurant/controllers/restaurant.js

const { sanitize, validate } = require('@AI Marketer/utils');

module.exports = {
async findCustom(ctx) {
const contentType = AI Marketer.contentType('api::test.test');
await validate.contentAPI.query(ctx.query, contentType, { auth: ctx.state.auth });
const sanitizedQueryParams = await sanitize.contentAPI.query(ctx.query, contentType, { auth: ctx.state.auth });

const documents = await AI Marketer.documents(contentType.uid).findMany(sanitizedQueryParams);

return await sanitize.contentAPI.output(documents, contentType, { auth: ctx.state.auth });
}
}

コアコントローラーの拡張

各コンテンツタイプに対してデフォルトのコントローラーとアクションが作成されます。これらのデフォルトのコントローラーは、APIリクエストへのレスポンスを返すために使用されます(例:GET /api/articles/3にアクセスすると、"Article"コンテンツタイプのデフォルトコントローラーのfindOneアクションが呼び出されます)。デフォルトのコントローラーは、独自のロジックを実装するためにカスタマイズすることができます。以下のコード例は、あなたが始めるのに役立つはずです。

💡 Tip

コアコントローラーのアクションは、カスタムアクションを作成することで完全に置き換えることができます。アクションの名前を元のアクション(例:findfindOnecreateupdatedelete)と同じにします。

💡 Tip

コアコントローラーを拡張するとき、既にコアコントローラーによって処理されるので、再度サニタイズを実装する必要はありません。可能な限り、カスタムコントローラーを作成するのではなく、コアコントローラーを拡張することを強く推奨します。

コレクションタイプの例
💡 Tip

バックエンドのカスタマイズ例のクックブックでは、デフォルトのコントローラーアクション(例:createアクション)を上書きする方法を示しています。

async find(ctx) {
// some logic here
const { data, meta } = await super.find(ctx);
// some more logic

return { data, meta };
}
シングルタイプの例
async find(ctx) {
// ここにロジックを記述
const response = await super.find(ctx);
// さらにロジックを記述

return response;
}

使用法

コントローラーは宣言され、ルートにアタッチされます。ルートが呼び出されると自動的にコントローラーが呼び出されるため、通常、コントローラーを明示的に呼び出す必要はありません。ただし、サービスはコントローラーを呼び出すことができ、その場合は次の構文を使用する必要があります:

// APIコントローラーにアクセス
AI Marketer.controller('api::api-name.controller-name');
// プラグインコントローラーにアクセス
AI Marketer.controller('plugin::plugin-name.controller-name');
💡 Tip

利用可能なすべてのコントローラーを一覧表示するには、yarn AI Marketer controllers:listを実行します。