# API ドキュメント

Source: https://docs.sisk-framework.org/ja/docs/extensions/api-documentation.html

`Sisk.Documenting` 拡張機能を使用すると、Sisk アプリケーションの API ドキュメントを自動的に生成できます。コード構造と属性を利用して包括的なドキュメントサイトを作成し、Open API（Swagger）形式へのエクスポートをサポートします。

> [!WARNING]
> This package is published as a prerelease (1.7.0-rc3). Its behavior and API may be subject to change in future updates.

NuGet から [Sisk.Documenting](https://www.nuget.org/packages/Sisk.Documenting/) パッケージをインストールします。ドキュメントを HTML ページとして提供するには、[Sisk.Documenting.Html](https://www.nuget.org/packages/Sisk.Documenting.Html/) もインストールしてください：

```sh
dotnet add package Sisk.Documenting --prerelease
dotnet add package Sisk.Documenting.Html --prerelease
```

ソースコードは [こちら](https://github.com/sisk-http/core/tree/main/extensions/Sisk.Documenting) で利用可能です。

`Sisk.Documenting` を使用するには、アプリケーションビルダーに登録し、ルートハンドラにドキュメント属性を付与する必要があります。

### ドキュメント生成の登録

`HttpServerHostContextBuilder` の `UseApiDocumentation` 拡張メソッドを使用して、アプリケーションを提供する同じルーターから生成された API ドキュメントを公開します。

```csharp
using Sisk.Documenting;
using Sisk.Documenting.Exporters;

// ...

host.UseApiDocumentation(
    context: new ApiGenerationContext()
    {
        ApplicationName = "My Application",
        ApplicationDescription = "Description of my application.",
        ApplicationVersion = "1.0.0"
    },
    routerPath: "/api/docs",
    exporter: new OpenApiExporter() { ServerUrls = ["http://localhost:5555/"] });
```

- **context**: アプリケーションの名前、説明、バージョンなどのメタデータを定義します。
- **routerPath**: ドキュメントのユーザーインターフェイス（または JSON）がアクセス可能になる URL パスです。
- **exporter**: ドキュメントのエクスポート方法を設定します。`OpenApiExporter` は Open API（Swagger）サポートを有効にします。

### エンドポイントのドキュメント化

ルートハンドラメソッドに `[ApiEndpoint]` と `[ApiQueryParameter]` 属性を付与して、エンドポイントを記述できます。

### `ApiEndpoint`

`[ApiEndpoint]` 属性を使用すると、エンドポイントの説明を提供できます。

```csharp
[ApiEndpoint(Description = "Returns a greeting message.")]
public HttpResponse Index(HttpRequest request) { ... }
```

### `ApiQueryParameter`

`[ApiQueryParameter]` 属性は、エンドポイントが受け取るクエリ文字列パラメータを文書化します。

```csharp
[ApiQueryParameter(name: "name", IsRequired = false, Description = "The name of the person to greet.", Type = "string")]
public HttpResponse Index(HttpRequest request) { ... }
```

- **name**: クエリパラメータの名前です。
- **IsRequired**: パラメータが必須かどうかを指定します。
- **Description**: パラメータの人間が読める説明です。
- **Type**: 期待されるデータ型（例: "string", "int"）です。

### `ApiEndpoint`

エンドポイントに一般的な情報を付与します。

*   **Name** (string, required in constructor): API エンドポイントの名前です。
*   **Description** (string): エンドポイントが何をするかの簡潔な説明です。
*   **Group** (string): エンドポイントをグループ化するために使用します（例: コントローラやモジュール単位）。
*   **InheritDescriptionFromXmlDocumentation** (bool, default: `true`): `true` の場合、`Description` が設定されていないときにメソッドの XML ドキュメントのサマリーを使用しようとします。

### `ApiHeader`

エンドポイントが期待または使用する特定の HTTP ヘッダーを文書化します。

*   **HeaderName** (string, required in constructor): ヘッダーのキー（例: "Authorization"）です。
*   **Description** (string): ヘッダーの目的を説明します。
*   **IsRequired** (bool): リクエストにおいてヘッダーが必須かどうかを示します。

### `ApiParameter`

エンドポイントの汎用パラメータを定義します。主にフォームフィールドや他の属性でカバーされないボディパラメータに使用されます。

*   **Name** (string, required in constructor): パラメータの名前です。
*   **TypeName** (string, required in constructor): パラメータのデータ型（例: "string", "int"）です。
*   **Description** (string): パラメータの説明です。
*   **IsRequired** (bool): パラメータが必須かどうかを示します。

### `ApiParameterExample`

特定の状況で文書化されたパラメータの例示値を追加します。この属性は同一メソッドに複数回適用できます。

*   **ParameterName** (string, required in constructor): 例を適用するパラメータの名前です。
*   **Title** (string, required in constructor): 例が示す状況を識別するタイトルです。
*   **Description** (string): 例が適用される時期や理由です。
*   **Example** (string): 例示値です。
*   **ExampleLanguage** (string): 例示値のフォーマットに使用する言語（該当する場合）です。

```csharp
[ApiQueryParameter("status", Type = "string", Description = "Filters orders by status.")]
[ApiParameterExample("status", "Pending orders", Example = "pending")]
[ApiParameterExample("status", "Shipped orders", Example = "shipped", Description = "Orders that left the warehouse.")]
```

### `ApiParametersFrom`

指定されたクラスまたは型のプロパティからパラメータのドキュメントを自動生成します。

*   **Type** (Type, required in constructor): プロパティを反映させるクラスの `Type` です。

### `ApiPathParameter`

パス変数（例: `/users/{id}`）を文書化します。

*   **Name** (string, required in constructor): パスパラメータの名前です。
*   **Description** (string): パラメータが何を表すかを説明します。
*   **Type** (string): 期待されるデータ型です。

### `ApiQueryParameter`

クエリ文字列パラメータ（例: `?page=1`）を文書化します。

*   **Name** (string, required in constructor): クエリパラメータのキーです。
*   **Description** (string): パラメータの説明です。
*   **Type** (string): 期待されるデータ型です。
*   **IsRequired** (bool): クエリパラメータが必須かどうかを示します。

### `ApiRequest`

期待されるリクエストボディを記述します。

*   **Description** (string, required in constructor): リクエストボディの説明です。
*   **Example** (string): リクエストボディの例を含む生文字列です。
*   **ExampleLanguage** (string): 例の言語（例: "json", "xml"）です。
*   **PayloadType** (Type): 設定した場合、構成されたコンテキストハンドラがサポートしていれば、この型から自動的に例とスキーマが生成されます。

### `ApiResponse`

エンドポイントからの可能なレスポンスを記述します。

*   **StatusCode** (HttpStatusCode, required in constructor): 返される HTTP ステータスコード（例: `HttpStatusCode.OK`）です。
*   **Description** (string): このレスポンスの条件を説明します。
*   **Example** (string): レスポンスボディの例を含む生文字列です。
*   **ExampleLanguage** (string): 例の言語です。
*   **PayloadType** (Type): 設定した場合、構成されたコンテキストハンドラがサポートしていれば、この型から自動的に例とスキーマが生成されます。

## タイプハンドラ

タイプハンドラは、.NET の型（クラス、列挙型など）をドキュメント用の例に変換する役割を担います。データモデルに基づくリクエストおよびレスポンスボディの自動例生成に特に有用です。

これらのハンドラは `ApiGenerationContext` 内で設定します。

```csharp
using Sisk.Documenting.Content;

var context = new ApiGenerationContext()
{
    // ...
    BodyExampleTypeHandler = new JsonContentTypeHandler(),
    ParameterExampleTypeHandler = new JsonContentTypeHandler(),
    ContentSchemaTypeHandler = new JsonContentTypeHandler()
};
```

### JsonContentTypeHandler

`JsonContentTypeHandler` は組み込みハンドラで、JSON の例、パラメータ例、JSON スキーマを生成します。`IExampleBodyTypeHandler`、`IExampleParameterTypeHandler`、`IContentSchemaTypeHandler` を実装しています。

特定の `JsonSerializerOptions` や `IJsonTypeInfoResolver` を使用して、アプリケーションのシリアライズロジックに合わせてカスタマイズできます。

```csharp
var jsonHandler = new JsonContentTypeHandler(new JsonSerializerOptions
{
    PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
    WriteIndented = true
});

context.BodyExampleTypeHandler = jsonHandler;
context.ParameterExampleTypeHandler = jsonHandler;
context.ContentSchemaTypeHandler = jsonHandler;
```

### カスタムタイプハンドラ

XML などの他形式をサポートしたり、例の生成方法をカスタマイズしたりするために、独自のハンドラを実装できます。

#### IExampleBodyTypeHandler

このインターフェイスを実装して、リクエストおよびレスポンスタイプのボディ例を生成します。

```csharp
public class XmlExampleTypeHandler : IExampleBodyTypeHandler
{
    public BodyExampleResult? GetBodyExampleForType(Type type)
    {
        // Generate XML string for the type
        string xmlContent = MyXmlGenerator.Generate(type);

        return new BodyExampleResult(xmlContent, "xml");
    }
}
```

#### IExampleParameterTypeHandler

このインターフェイスを実装して、型から詳細なパラメータ記述を生成します（`[ApiParametersFrom]` で使用）。

```csharp
public class CustomParameterHandler : IExampleParameterTypeHandler
{
    public ParameterExampleResult[] GetParameterExamplesForType(Type type)
    {
        var properties = type.GetProperties();
        var examples = new List<ParameterExampleResult>();

        foreach (var prop in properties)
        {
            examples.Add(new ParameterExampleResult(
                name: prop.Name,
                typeName: prop.PropertyType.Name,
                isRequired: true,
                description: "Generated description"
            ));
        }

        return examples.ToArray();
    }
}
```

## エクスポーター

エクスポーターは、収集された API ドキュメントメタデータを、他のツールが利用できる、またはユーザーに表示できる特定の形式に変換する役割を担います。

### OpenApiExporter

デフォルトのエクスポーターは `OpenApiExporter` で、[OpenAPI Specification 3.0.0](https://spec.openapis.org/oas/v3.0.0) に従った JSON ファイルを生成します。

```csharp
new OpenApiExporter()
{
    OpenApiVersion = "3.0.0",
    ServerUrls = new[] { "http://localhost:5555" },
    Contact = new OpenApiContact()
    {
        Name = "Support",
        Email = "support@example.com",
        Url = "https://example.com/support"
    },
    License = new OpenApiLicense()
    {
        Name = "MIT",
        Url = "https://opensource.org/licenses/MIT"
    },
    TermsOfService = "https://example.com/terms"
}
```

### LlmsApiExporter

`LlmsApiExporter` は [llms.txt](https://llmstxt.org/) スタイルのプレーンテキストドキュメントを生成し、言語モデルが読むことを想定しています。例で使用する `Hostname`、文書の冒頭に書き込まれる `HeaderText`、末尾に書き込まれる `FooterText` を設定できます。

```csharp
new LlmsApiExporter(
    hostname: "https://api.example.com",
    headerText: "Public API of the Example service.",
    footerText: "Contact support@example.com for an API key.")
```

### カスタムエクスポーターの作成

`IApiDocumentationExporter` インターフェイスを実装することで、独自のエクスポーターを作成できます。これにより、Markdown、HTML、Postman Collection など、任意の形式でドキュメントを出力できます。

インターフェイスは単一のメソッド `ExportDocumentationContent` の実装を要求します。

```csharp
using Sisk.Core.Http;
using Sisk.Documenting;

public class MyCustomExporter : IApiDocumentationExporter
{
    public HttpContent ExportDocumentationContent(ApiDocumentation documentation)
    {
        // 1. ドキュメントオブジェクトを処理する
        var sb = new StringBuilder();
        sb.AppendLine($"# {documentation.ApplicationName}");

        foreach(var endpoint in documentation.Endpoints)
        {
            sb.AppendLine($"## {endpoint.Method} {endpoint.Path}");
            sb.AppendLine(endpoint.Description);
        }

        // 2. 内容を HttpContent として返す
        return new StringContent(sb.ToString(), Encoding.UTF8, "text/markdown");
    }
}
```

Then, simply use it in your configuration:

```csharp
host.UseApiDocumentation(
    // ...
    exporter: new MyCustomExporter()
);
```

### 完全な例

以下は、`Sisk.Documenting` を設定し、シンプルなコントローラをドキュメント化する完全な例です。

```csharp
using Sisk.Core.Entity;
using Sisk.Core.Http;
using Sisk.Core.Routing;
using Sisk.Documenting;
using Sisk.Documenting.Annotations;
using Sisk.Documenting.Exporters;

using var host = HttpServer.CreateBuilder(5555)
    .UseCors(CrossOriginResourceSharingHeaders.CreatePublicContext())
    .UseApiDocumentation(
        context: new ApiGenerationContext()
        {
            ApplicationName = "My application",
            ApplicationDescription = "It greets someone."
        },
        routerPath: "/api/docs",
        exporter: new OpenApiExporter() { ServerUrls = ["http://localhost:5555/"] })
    .UseRouter(router =>
    {
        router.MapInstance(new MyController());
    })
    .Build();

await host.StartAsync();

class MyController
{
    [RouteGet]
    [ApiEndpoint(Description = "Returns a greeting message.")]
    [ApiQueryParameter(name: "name", IsRequired = false, Description = "The name of the person to greet.", Type = "string")]
    public HttpResponse Index(HttpRequest request)
    {
        string? name = request.Query["name"].MaybeNullOrEmpty() ?? "world";
        return new HttpResponse($"Hello, {name}!");
    }
}
```

この例では、`/api/docs` にアクセスすると、"My application" API の生成されたドキュメントが提供され、`GET /` エンドポイントとその `name` パラメータが記述されます。
