カスタム リソース コマンド
Aspire アプリ モデル内の各リソースは IResource として表現され、分散アプリケーション ビルダーに追加されると、IResourceBuilder<T> インターフェイスのジェネリック型パラメーターになります。resource builder API を使って呼び出しを連鎖し、基になるリソースを構成できます。状況によっては、そのリソースにカスタム コマンドを追加したいことがあります。カスタム コマンドを作成する一般的なシナリオには、データベース移行の実行やデータベースのシード/リセットなどがあります。この記事では、キャッシュをクリアする Redis リソースにカスタム コマンドを追加する方法を学びます。
リソースにカスタム コマンドを追加する
Section titled “リソースにカスタム コマンドを追加する”Aspire AppHost プロジェクトから開始します。以下のウォークスルーでは、Redis リソースに clear-cache コマンドを登録します。C# 版は登録をラップする拡張メソッドを使用し、TypeScript 版は管理エンドポイントを公開する Node サービスに対してコマンドをインライン登録します。どちらのフローでも、同じダッシュボード/CLI 体験を得られます。
AppHost プロジェクトに RedisResourceBuilderExtensions.cs という新しいクラスを追加し、内容を次のコードに置き換えます。
using Microsoft.Extensions.DependencyInjection;using Microsoft.Extensions.Diagnostics.HealthChecks;using Microsoft.Extensions.Logging;using StackExchange.Redis;
namespace Aspire.Hosting;
internal static class RedisResourceBuilderExtensions{ public static IResourceBuilder<RedisResource> WithClearCommand( this IResourceBuilder<RedisResource> builder) { var commandOptions = new CommandOptions { UpdateState = OnUpdateResourceState, IconName = "AnimalRabbitOff", IconVariant = IconVariant.Filled };
builder.WithCommand( name: "clear-cache", displayName: "Clear Cache", executeCommand: context => OnRunClearCacheCommandAsync(builder, context), commandOptions: commandOptions);
return builder; }
private static async Task<ExecuteCommandResult> OnRunClearCacheCommandAsync( IResourceBuilder<RedisResource> builder, ExecuteCommandContext context) { var connectionString = await builder.Resource.GetConnectionStringAsync() ?? throw new InvalidOperationException( $"Unable to get the '{context.ResourceName}' connection string.");
await using var connection = ConnectionMultiplexer.Connect(connectionString); var database = connection.GetDatabase(); await database.ExecuteAsync("FLUSHALL");
return CommandResults.Success(); }
private static ResourceCommandState OnUpdateResourceState( UpdateCommandStateContext context) { var logger = context.ServiceProvider.GetRequiredService<ILogger<Program>>(); if (logger.IsEnabled(LogLevel.Information)) { logger.LogInformation( "Updating resource state: {ResourceSnapshot}", context.ResourceSnapshot); }
return context.ResourceSnapshot.HealthStatus is HealthStatus.Healthy ? ResourceCommandState.Enabled : ResourceCommandState.Disabled; }}前述のコード:
- AppHost から拡張が見えるように、
Aspire.Hosting名前空間を共有します。 IResourceBuilder<RedisResource>に対する拡張メソッドを含むstatic classです。WithClearCommandという単一の拡張メソッドを定義し、Redis リソースにclear-cacheコマンドを登録します。さらに、ダッシュボードがコマンド実行と有効化判定に使うexecuteCommandとupdateStateコールバックを定義します。- 呼び出し側でチェーン可能にするため、
IResourceBuilder<RedisResource>を返します。
TypeScript AppHost では、リソース ビルダー上でコマンドをインライン登録します。TypeScript には拡張メソッドがないため、クロージャーでコマンドに必要な状態をキャプチャします。
import { createBuilder, type ExecuteCommandContext, type ExecuteCommandResult,} from './.aspire/modules/aspire.mjs';
const builder = await createBuilder();
const cache = await builder .addNodeApp("cache", "./cache-service", "src/server.ts") .withHttpEndpoint();
await cache.withCommand( "clear-cache", "Clear Cache", async (_context: ExecuteCommandContext): Promise<ExecuteCommandResult> => { const endpoint = await cache.getEndpoint("http"); const url = await endpoint.url.get(); const res = await fetch(`${url}/admin/cache/clear`, { method: "POST" });
if (!res.ok) { return { success: false, message: `Cache service returned ${res.status} ${res.statusText}.`, }; }
return { success: true, message: "Cache cleared." }; }, { commandOptions: { description: "Drops all entries in the cache service.", confirmationMessage: "Clear all cached entries?", iconName: "AnimalRabbitOff", }, });
await builder.build().run();前述のコード:
cacheという名前の Node アプリを追加し、その上にclear-cacheコマンドを登録します。executeCommandコールバックを async アロー関数として実装し、リソースの HTTP エンドポイントを呼び出して実際のキャッシュ無効化を実行します。このパターンは、HTTP 経由で管理操作を公開する任意のリソースで機能します。代わりにクライアントを直接使いたいリソース(データベース、メッセージ ブローカー、…)では、同じコールバック内からそのクライアントを呼び出してください。- 4 番目の引数として
{ commandOptions: { ... } }を渡し、ダッシュボードの説明、確認プロンプト、アイコンを設定します。
WithCommand API は、リソースに適切な注釈を追加し、それらは Aspire ダッシュボード で利用されます。ダッシュボードはこれらの注釈を使って UI にコマンドを描画します。詳細へ進む前に、まず WithCommand メソッドのパラメーターを理解しておきましょう。
name: 呼び出すコマンド名。displayName: ダッシュボードに表示するコマンド名。executeCommand: コマンド呼び出し時に実行されるコールバック。C# では型がFunc<ExecuteCommandContext, Task<ExecuteCommandResult>>、TypeScript では(context: ExecuteCommandContext) => Promise<ExecuteCommandResult>です。updateState: ダッシュボード上でコマンドを有効化する状態を判定するコールバック。C# ではFunc<UpdateCommandStateContext, ResourceCommandState>で、CommandOptions.UpdateState経由で指定します。TypeScript では(context: UpdateCommandStateContext) => Promise<ResourceCommandState>で、CommandOptions上のupdateStateフィールドとして指定します。iconName: ダッシュボードに表示するアイコン名。アイコンは省略可能ですが、指定する場合は有効な Fluent UI Blazor アイコン名 である必要があります。iconVariant: ダッシュボードに表示するアイコンのバリアント。有効な値はRegular(既定)またはFilledです。
コマンド実行ロジック
Section titled “コマンド実行ロジック”executeCommand コールバックは、コマンド ロジックを実装する場所です。受け取る ExecuteCommandContext から、操作対象リソース、キャンセル、ログなどのコマンド固有データにアクセスできます。
C# ではパラメーター型は Func<ExecuteCommandContext, Task<ExecuteCommandResult>> です。ExecuteCommandContext が公開する内容:
ServiceProvider(IServiceProvider) — ロガーやResourceCommandServiceなどのサービス解決に使用します。ResourceName(string) — コマンド実行対象となるリソース インスタンス名です。CancellationToken(CancellationToken) — コマンド実行のキャンセルに使用します。Logger(ILogger) — ログをリソースのログへ直接書き込むために使用します。これらのログは Aspire ダッシュボード またはaspire logs <resource>コマンドで確認できます。
前述の例は、OnRunClearCacheCommandAsync という名前の private static メソッドに処理を委譲してキャッシュをクリアします。
private static async Task<ExecuteCommandResult> OnRunClearCacheCommandAsync( IResourceBuilder<RedisResource> builder, ExecuteCommandContext context){ var connectionString = await builder.Resource.GetConnectionStringAsync() ?? throw new InvalidOperationException( $"Unable to get the '{context.ResourceName}' connection string.");
await using var connection = ConnectionMultiplexer.Connect(connectionString);
var database = connection.GetDatabase();
await database.ExecuteAsync("FLUSHALL");
return CommandResults.Success();}前述のコード:
- Redis リソースから接続文字列を取得します。
- Redis インスタンスへ接続します。
- データベース インスタンスを取得します。
FLUSHALLコマンドを実行してキャッシュをクリアします。- コマンド成功を示す
CommandResults.Success()インスタンスを返します。
TypeScript でのコールバック シグネチャは (context: ExecuteCommandContext) => Promise<ExecuteCommandResult> です。ExecuteCommandContext はコマンド固有データの async アクセサーを公開します。
resourceName()— リソース インスタンス名。cancellationToken()—AbortSignalを受け取る API に転送できるCancellationToken。logger()— リソース ロガー。arguments()— コマンド実行時に渡された呼び出し引数。
TypeScript コールバックは通常、async アロー関数としてインライン定義します。次の例は、リソースの HTTP エンドポイントを取得し、キャンセル トークンを fetch に転送します。
async (context: ExecuteCommandContext): Promise<ExecuteCommandResult> => { const endpoint = await cache.getEndpoint("http"); const url = await endpoint.url.get(); const cancellation = await context.cancellationToken();
const res = await fetch(`${url}/admin/cache/clear`, { method: "POST", signal: cancellation as unknown as AbortSignal, });
if (!res.ok) { return { success: false, message: `Cache service returned ${res.status} ${res.statusText}.`, }; }
return { success: true, message: "Cache cleared." };}前述のコード:
- リソースの HTTP エンドポイント URL を読み取ります。
- リソースの管理エンドポイントを呼び出し、キャンセル トークンを abort signal として転送することで、ダッシュボードまたは CLI がコマンドをキャンセルしたときに要求を停止します。
- HTTP 呼び出しが失敗した場合は
{ success: false, message: ... }を返し、成功した場合は{ success: true, message: "Cache cleared." }を返します。
コマンド状態更新ロジック
Section titled “コマンド状態更新ロジック”updateState コールバックは、ダッシュボード上でコマンドを有効化するかどうかを判定します。ダッシュボードはリソース状態が変化するたびにこれを呼び出します。典型的なユース ケースは、破壊的なコマンドを正常なリソースに対してのみ有効化すること、または起動プローブ成功後にのみコマンドを有効化することです。
C# ではパラメーター型は Func<UpdateCommandStateContext, ResourceCommandState> です。UpdateCommandStateContext が公開する内容:
ServiceProvider(IServiceProvider) — サービス解決に使用します。ResourceSnapshot(CustomResourceSnapshot) — 正常性ステータス、ライフサイクル状態、プロパティ、URL を含む、リソース インスタンスの不変スナップショットです。
前述の例では、Redis リソースが正常なときだけ clear-cache コマンドを有効化します。
private static ResourceCommandState OnUpdateResourceState( UpdateCommandStateContext context){ var logger = context.ServiceProvider.GetRequiredService<ILogger<Program>>();
if (logger.IsEnabled(LogLevel.Information)) { logger.LogInformation( "Updating resource state: {ResourceSnapshot}", context.ResourceSnapshot); }
return context.ResourceSnapshot.HealthStatus is HealthStatus.Healthy ? ResourceCommandState.Enabled : ResourceCommandState.Disabled;}前述のコード:
- サービス プロバイダーからロガー インスタンスを取得します。
- リソース スナップショットの詳細をログ出力します。
- リソースが正常なら
ResourceCommandState.Enabled、それ以外はResourceCommandState.Disabledを返します。
TypeScript では CommandOptions の updateState フィールドは非同期コールバックで、UpdateCommandStateContext を受け取ります。コンテキストは resourceSnapshot() を公開します。これは、healthStatus とライフサイクル状態の state を含む、リソース インスタンスのスナップショットを返す非同期関数です。
次の例では、C# パターンと同様に、リソースが健全な状態のときのみ clear-cache コマンドを有効にします。
import { HealthStatus, ResourceCommandState } from './.modules/aspire.js';
await cache.withCommand( "clear-cache", "Clear Cache", executeClearCache, { commandOptions: { iconName: "AnimalRabbitOff", updateState: async (ctx) => { const snapshot = await ctx.resourceSnapshot();
return snapshot.healthStatus === HealthStatus.Healthy ? ResourceCommandState.Enabled : ResourceCommandState.Disabled; }, }, });上記のコードは以下を行います。
ctx.resourceSnapshot()を呼び出して現在のリソース スナップショットを取得します。- リソースが健全であれば
ResourceCommandState.Enabledを返し、そうでなければResourceCommandState.Disabledを返します。
HealthStatus と ResourceCommandState 列挙体は生成された SDK(aspire.ts)からエクスポートされており、直接インポートして使用できます。
カスタム コマンドをテストする
Section titled “カスタム コマンドをテストする”カスタム コマンドをテストするには、AppHost を更新してリソースとコマンドを配線します。
var builder = DistributedApplication.CreateBuilder(args);
var cache = builder.AddRedis("cache") .WithClearCommand();
var apiService = builder.AddProject<Projects.AspireApp_ApiService>("apiservice");
builder.AddProject<Projects.AspireApp_Web>("webfrontend") .WithExternalHttpEndpoints() .WithReference(cache) .WaitFor(cache) .WithReference(apiService) .WaitFor(apiService);
builder.Build().Run();前述のコードは WithClearCommand 拡張メソッドを呼び出し、Redis リソースにカスタム コマンドを追加します。
import { createBuilder } from './.aspire/modules/aspire.mjs';
const builder = await createBuilder();
const cache = await builder.addNodeApp("cache", "../cache/server.js");await cache.withHttpEndpoint();await cache.withCommand( "clear-cache", "Clear Cache", async (context) => { const endpoint = await cache.getEndpoint("http"); const url = await endpoint.url.get(); const cancellation = await context.cancellationToken();
const res = await fetch(`${url}/admin/cache/clear`, { method: "POST", signal: cancellation as unknown as AbortSignal, });
if (!res.ok) { return { success: false, message: `Cache service returned ${res.status} ${res.statusText}.`, }; }
return { success: true, message: "Cache cleared." }; }, { commandOptions: { iconName: "AnimalRabbitOff", description: "Clears the application cache.", confirmationMessage: "Are you sure you want to clear the cache?", }, });
const apiService = await builder.addProject("apiservice", "../ApiService/ApiService.csproj");
const web = await builder.addProject("webfrontend", "../Web/Web.csproj");await web.withExternalHttpEndpoints();await web.withReference(cache);await web.waitFor(cache);await web.withReference(apiService);await web.waitFor(apiService);
await builder.build().run();前述のコードは、キャッシュ サービスを HTTP エンドポイント付き Node アプリとして登録し、clear-cache コマンドを直接アタッチします。
アプリを実行して Aspire ダッシュボードへ移動します。キャッシュ リソース配下にカスタム コマンドが表示されるはずです。ダッシュボードの Resources ページで、Actions 列の下にある省略記号ボタンを選択します。

前述の画像には、Redis リソースへ追加した Clear cache コマンドが表示されています。アイコンは、依存リソースの速度がクリアされることを示すため、取り消し線付きのウサギとして表示されます。
Clear cache コマンドを選択して、Redis リソースのキャッシュをクリアします。コマンドは正常に実行され、キャッシュはクリアされるはずです。コマンドの結果は 通知センター で確認できます。

コマンドをプログラムで実行する
Section titled “コマンドをプログラムで実行する”Aspire ダッシュボード を介してコマンドを実行するだけでなく、ResourceCommandService を使ってプログラムからコマンドを実行することもできます。このサービスは、次のような場合に有用です。
- アプリケーション コード内からコマンドを実行する
- ワークフローの一部としてコマンド実行を自動化する
- リソース制御が必要なカスタム ツールを構築する
最も一般的なシナリオは、他のカスタム コマンド実装からコマンドを呼び出すことです。C# では ExecuteCommandContext.ServiceProvider を使って ResourceCommandService を取得します。TypeScript では、コマンドを登録する前に AppHost の実行コンテキストからサービスを解決し、コールバックでキャプチャします。
カスタム コマンドからコマンドを実行する
Section titled “カスタム コマンドからコマンドを実行する”次の例は、他のコマンドを実行するカスタム コマンドの作成方法を示しています。このケースでは、“reset-all” コマンドがキャッシュをクリアし、データベースを再起動します。
using Aspire.Hosting.ApplicationModel;using Microsoft.Extensions.DependencyInjection;
var builder = DistributedApplication.CreateBuilder(args);
var cache = builder.AddRedis("cache") .WithClearCommand(); // 前述のとおり "clear-cache" コマンドを定義
var database = builder.AddPostgres("postgres") .WithCommand("restart", "Restart Database", async (context, ct) => { // データベース再起動の実装 return CommandResults.Success(); });
var api = builder.AddProject<Projects.Api>("api") .WithReference(cache) .WithReference(database) .WithCommand("reset-all", "Reset Everything", async (context, ct) => { var commandService = context.ServiceProvider.GetRequiredService<ResourceCommandService>();
context.Logger.LogInformation("Starting full system reset...");
var clearResult = await commandService.ExecuteCommandAsync( resource: cache.Resource, commandName: "clear-cache", cancellationToken: ct);
var restartResult = await commandService.ExecuteCommandAsync( resource: database.Resource, commandName: "restart", cancellationToken: ct);
if (!clearResult.Success || !restartResult.Success) { return CommandResults.Failure("System reset failed"); }
context.Logger.LogInformation("System reset completed successfully"); return CommandResults.Success(); });
builder.Build().Run();import { createBuilder } from './.modules/aspire.js';
const builder = await createBuilder();
const executionContext = await builder.executionContext();const serviceProvider = await executionContext.serviceProvider();const commandService = await serviceProvider.getResourceCommandService();
const cache = await builder.addRedis("cache");await cache.withCommand("clear-cache", "Clear Cache", async () => { // キャッシュ クリアの実装 return { success: true };});
const database = await builder.addPostgres("postgres");await database.withCommand("restart", "Restart Database", async () => { // データベース再起動の実装 return { success: true };});
const api = await builder.addProject("api", "../Api/Api.csproj");await api.withReference(cache);await api.withReference(database);await api.withCommand("reset-all", "Reset Everything", async (ctx) => { const logger = await ctx.logger(); const cancellationToken = await ctx.cancellationToken();
await logger.logInformation("Starting full system reset...");
const clearResult = await commandService.executeCommandAsync( "cache", "clear-cache", { cancellationToken });
const restartResult = await commandService.executeCommandAsync( "postgres", "restart", { cancellationToken });
if (!clearResult.success || !restartResult.success) { return { success: false, message: "System reset failed" }; }
await logger.logInformation("System reset completed successfully"); return { success: true };});
await builder.build().run();この例では:
- C# では
contextパラメーターでサービス プロバイダーとロガーにアクセスします。 - TypeScript では、コマンドを登録する前に AppHost 実行コンテキストのサービス プロバイダーから
ResourceCommandServiceを取得します。 - C# では
cache.Resourceとdatabase.Resourceを使ってリソース インスタンスに対してコマンドを実行し、TypeScript では"cache"や"postgres"などのリソース名文字列を使って実行します。 - 各コマンド結果の成功を確認してから次へ進みます。
リソース ID でコマンドを実行する
Section titled “リソース ID でコマンドを実行する”リソース ID またはリソース名を使ってコマンドを実行することもできます。リソース ID はリソース インスタンスの一意識別子で、リソース名は表示名です(この方法を使うには一意である必要があります)。
var result = await commandService.ExecuteCommandAsync( resourceId: "cache", commandName: "clear-cache", cancellationToken: cancellationToken);
if (result.Success){ logger.LogInformation("Command executed successfully");}else if (result.Canceled){ logger.LogWarning("Command was canceled");}else{ logger.LogError("Command failed: {Message}", result.Message);}const result = await commandService.executeCommandAsync( "cache", "clear-cache", { cancellationToken });
if (result.success) { console.log("Command executed successfully");}else if (result.canceled) { console.warn("Command was canceled");}else { console.error(`Command failed: ${result.message}`);}リソース識別子引数(C# の resourceId、または TypeScript で文字列を渡す場合の最初の引数)には、次のいずれかを指定できます:
- リソースの一意 ID(例: レプリカを持つリソースの
cache-abcdwxyz) - 重複名がない場合の表示名(例:
cache)
レプリカを持つリソースでコマンドを実行する
Section titled “レプリカを持つリソースでコマンドを実行する”複数レプリカを持つリソースに対して IResource オーバーロードでコマンドを実行すると、すべてのインスタンスで並列実行されます。
// レプリカを持つリソースでは、すべてのインスタンスでコマンドが実行されるvar result = await commandService.ExecuteCommandAsync( resource: cache.Resource, commandName: "clear-cache", cancellationToken: cancellationToken);TypeScript では、前述のリソース識別子オーバーロードを使います。特定のレプリカ ID を渡して 1 つのインスタンスを対象にするか、重複名がない場合は一意のリソース名を渡します。
複数レプリカ上で実行した場合の動作:
- コマンドはすべてのインスタンスで並列実行されます。
- すべてのコマンドが成功した場合、結果は成功を示します。
- いずれかのコマンドが失敗した場合、結果には失敗の詳細が含まれます。
- 非成功のすべてがキャンセルだった場合、結果はキャンセルを示します。
コマンド実行結果を処理する
Section titled “コマンド実行結果を処理する”ExecuteCommandResult 型はコマンドの結果を保持します。両言語で同じ概念フィールドを公開します。
Success/success(boolean) — コマンドが正常完了したかどうか。Canceled/canceled(boolean) — ユーザー操作でコマンドがキャンセルされたかどうか。Message/message(string?) — 通知センター と CLI 出力に表示される人間向けメッセージ。成功メッセージとエラー メッセージの両方に使われます。Data/data(CommandResultData?) — 省略可能な構造化出力(プレーン テキスト、JSON、Markdown)。コマンドから構造化出力を返す を参照してください。
CommandResults ヘルパー クラスは、一般的な形状のファクトリー メソッドを提供します。
// ペイロードなしで成功return CommandResults.Success();
// ステータス メッセージ付きで失敗return CommandResults.Failure("Error occurred during execution");
// キャンセル(通常は確認プロンプトでユーザーが戻った場合)return CommandResults.Canceled();
// 例外由来の失敗(Exception.Message を使用)return CommandResults.Failure(ex);TypeScript にはヘルパー クラスがないため、ExecuteCommandResult に準拠するプレーン オブジェクトを返します。
// ペイロードなしで成功return { success: true };
// ステータス メッセージ付きで失敗return { success: false, message: "Error occurred during execution" };
// キャンセル(通常は確認プロンプトでユーザーが戻った場合)return { canceled: true };
// 捕捉した例外由来の失敗return { success: false, message: err instanceof Error ? err.message : String(err) };コマンドから構造化出力を返す
Section titled “コマンドから構造化出力を返す”リソース コマンドは、成功/失敗シグナルに加えて、プレーン テキスト、JSON、Markdown の構造化ペイロードを返せます。このペイロードは Aspire パイプライン全体を自動で流れます。
-
Dashboard: 成功通知に View response アクションが追加され、ペイロードを表示するテキスト ビジュアライザー ダイアログが開きます。
Json結果は JSON シンタックス ハイライトに固定され、Markdown結果は Markdown レンダリングに固定されます。DisplayImmediatelyを設定すると、コマンド完了直後にダイアログが開きます(クリック不要)。 -
CLI: ステータス メッセージは
stderrに、ペイロードはstdoutに出力されるため、jqのようなツールへ安全にパイプできます。Terminal aspire resource cache issue-access-token | jq -r .token -
MCP tools: ステータス メッセージの後に続く 2 番目の
TextContentBlockとして、ペイロードがツール応答に追加されます。
テキストまたは JSON を返す
Section titled “テキストまたは JSON を返す”一般的なシナリオは、カスタム コマンドから アクセス トークン を発行することです。開発者はそれをダッシュボードから直接コピーしたり、CLI から Bearer ヘッダーにパイプしたりできます。Text モードでは結果はトークン文字列のみ、Json モードでは有効期限とスコープを含む結果になります。
成功結果へペイロードを添付するには、CommandResults.Success(message, result, resultFormat) オーバーロードを使います。message は通知センター/CLI のステータス、result はペイロード文字列、resultFormat はダッシュボード ビジュアライザーの解釈方法を制御します。
using System.Security.Cryptography;using Aspire.Hosting.ApplicationModel;
var builder = DistributedApplication.CreateBuilder(args);
builder.AddProject<Projects.MyService>("myservice") .WithCommand( name: "issue-access-token", displayName: "Issue Access Token", executeCommand: context => { var token = Convert.ToBase64String(RandomNumberGenerator.GetBytes(32));
return Task.FromResult(CommandResults.Success( message: "Access token issued.", result: token, resultFormat: CommandResultFormat.Text)); });
builder.Build().Run();有効期限とスコープ付き JSON を返すには、形式を CommandResultFormat.Json に切り替えます。
using System.Security.Cryptography;using System.Text.Json;using Aspire.Hosting.ApplicationModel;
var builder = DistributedApplication.CreateBuilder(args);
builder.AddProject<Projects.MyService>("myservice") .WithCommand( name: "issue-access-token", displayName: "Issue Access Token", executeCommand: context => { var token = Convert.ToBase64String(RandomNumberGenerator.GetBytes(32)); var json = JsonSerializer.Serialize(new { token, expiresAt = DateTimeOffset.UtcNow.AddHours(1), scopes = new[] { "read", "write" }, });
return Task.FromResult(CommandResults.Success( message: "Access token issued.", result: json, resultFormat: CommandResultFormat.Json)); });
builder.Build().Run();TypeScript では、CommandResultData 型の data プロパティを持つ ExecuteCommandResult オブジェクト リテラルを返します。
import { createBuilder, CommandResultFormat, type ExecuteCommandContext, type ExecuteCommandResult,} from './.aspire/modules/aspire.mjs';
const builder = await createBuilder();
await builder .addNodeApp("myservice", "./myservice", "src/server.ts") .withCommand("issue-access-token", "Issue Access Token", async (_context: ExecuteCommandContext): Promise<ExecuteCommandResult> => { const token = crypto.randomUUID().replace(/-/g, "");
return { success: true, message: "Access token issued.", data: { value: token, format: CommandResultFormat.Text, }, }; });
await builder.build().run();有効期限とスコープ付き JSON を返すには、形式を CommandResultFormat.Json に切り替えます。
import { createBuilder, CommandResultFormat, type ExecuteCommandContext, type ExecuteCommandResult,} from './.aspire/modules/aspire.mjs';
const builder = await createBuilder();
await builder .addNodeApp("myservice", "./myservice", "src/server.ts") .withCommand("issue-access-token", "Issue Access Token", async (_context: ExecuteCommandContext): Promise<ExecuteCommandResult> => { const token = crypto.randomUUID().replace(/-/g, ""); const json = JSON.stringify({ token, expiresAt: new Date(Date.now() + 3_600_000).toISOString(), scopes: ["read", "write"], });
return { success: true, message: "Access token issued.", data: { value: json, format: CommandResultFormat.Json, }, }; });
await builder.build().run();結果形式を選択する
Section titled “結果形式を選択する”CommandResultFormat には 3 つの値があります。
| 値 | ダッシュボードでの動作 |
|---|---|
Text | プレーン テキスト。固定形式なしでテキスト ビジュアライザー ダイアログに開き、ユーザーは別の構文へ切り替えて確認できます。 |
Json | JSON。ビジュアライザーは JSON モードに固定され、シンタックス ハイライトと整形表示を行います。 |
Markdown | Markdown。ビジュアライザーは Markdown レンダリングに固定されます。 |
コマンドに表示すべきペイロードがない場合は、Data を省略してください(CommandResults.Success() または { success: true } を返します)。
失敗時にデータを返す
Section titled “失敗時にデータを返す”対応する CommandResults.Failure(errorMessage, result, resultFormat) オーバーロードを使うと、失敗結果にも同じペイロード形状を添付できます。これは、ユーザー(または下流ツール)が確認できる診断詳細を表面化するのに便利です。
return CommandResults.Failure( errorMessage: "Migration failed.", result: errorJson, resultFormat: CommandResultFormat.Json);return { success: false, message: "Migration failed.", data: { value: errorJson, format: CommandResultFormat.Json, },};ダッシュボードで結果ダイアログを自動表示する
Section titled “ダッシュボードで結果ダイアログを自動表示する”既定では、ダッシュボードは 通知センター の通知に View response アクションとしてペイロードを表示します。CommandResultData の DisplayImmediately を設定すると、コマンド完了時にビジュアライザーを即座に開けます。短いコマンドで応答自体が目的の場合に便利です。
return CommandResults.Success( message: "Access token issued.", value: new CommandResultData { Value = json, Format = CommandResultFormat.Json, DisplayImmediately = true, });return { success: true, message: "Access token issued.", data: { value: json, format: CommandResultFormat.Json, displayImmediately: true, },};レプリカ集約
Section titled “レプリカ集約”リソースが複数レプリカを持つ場合、コマンドはすべてのインスタンスで並列実行されます。複数レプリカからペイロードが返された場合、最初に成功したペイロードのみが呼び出し側へ伝搬し、他は破棄されます。
CLI からコマンドを呼び出す
Section titled “CLI からコマンドを呼び出す”WithCommand で登録した任意のコマンドは、組み込みの start、stop、restart コマンドを含め、aspire resource コマンドを使ってターミナルから呼び出せます。
aspire resource <resource-name> <command-name> [--apphost <path>]CLI はステータス出力とペイロード出力を分離するため、結果はスクリプト処理しやすい形式になります。
Messageと進捗テキストは stderr に書き込まれます。Data.Value(存在する場合)は stdout にそのまま書き込まれます。JsonとTextペイロードはそのまま出力され、Markdownは見出し、リスト、コード ブロックを端末向けに装飾してレンダリングされます。- 終了コードは、成功時が
0、失敗またはキャンセル時が非 0 です。
これにより、構造化ペイロードを jq に直接パイプしたり、ファイルへリダイレクトしたり、終了コードで分岐したりできます。
$ aspire resource cache issue-access-token | jq -r .tokeney7WqGxk2vL...ステータス メッセージ Access token issued. は stderr に書き込まれるため、stdout はパイプ用にクリーンなまま保たれます。
if aspire resource db migrate > migrations.json; then echo "Applied $(jq length migrations.json) migrations."else echo "Migration failed — see error above." exit 1fi