sgcOpenAPI 2026.6 — スタンドアロンOpenAPIサーバー、スペックファーストまたはコードファースト

· リリース
sgcOpenAPI 2026.6 — スタンドアロンOpenAPIサーバー、スペックファーストまたはコードファースト | eSeGeCeブログ

sgcOpenAPIの次期リリース、6月に予定されているバージョン2026.6は、sgcWebSockets をインストールせずにホストできるOpenAPI 3.0サーバーを提供します。これを実現するのは 2 つのコンポーネントです。Indy ベースの HTTP サーバーであるTsgcHTTPServerと、それにアタッチする API コンポーネントであるTsgcWSAPIServer_OpenAPIです。API コンポーネントにスペックを指定し(または RTTI 属性を持つ Delphi クラスから生成し)、そのServerプロパティを割り当て、HTTP サーバーを起動すれば、Swagger UI が自動配信されるドキュメント付き REST API が完成します。

目玉となる変更点は、sgcOpenAPI が HTTP サーバーをホストするために sgcWebSockets を必要としなくなったことです。両方のコンポーネントは、sgcOpenAPI から完全に出荷、パッケージ化、インストールされます。すでに sgcWebSockets を使用している場合も、おなじみのTsgcWSServer_API_OpenAPIコンポーネントは変更なく引き続き動作します。同じクラスだからです。両方の製品が、同じエンジンからこれを構築しています。

得られるもの

このペアは 3 つの要素をもたらします:

クイックスタート — 最小限の例

Swagger UI を備えた動作する OpenAPI サーバーをホストするために必要なものは、これだけです。API コンポーネントにはActiveプロパティがないことに注意してください。Serverを割り当てることでアタッチされ、nilを割り当てるとデタッチされますが、その間も HTTP サーバーは動作し続けます。

uses
  sgcHTTP_Server, sgcWebSocket_Server_API_OpenAPI;

var
  oServer: TsgcHTTPServer;
  oOpenAPI: TsgcWSAPIServer_OpenAPI;
begin
  oServer := TsgcHTTPServer.Create(nil);
  oOpenAPI := TsgcWSAPIServer_OpenAPI.Create(nil);
  try
    oServer.Bindings.Add.Port := 8080;
    oOpenAPI.LoadFromFile('petstore.json');
    oOpenAPI.OnRequest := MyOnRequest;
    oOpenAPI.Server := oServer;
    oServer.Active := True;
    Readln;
  finally
    oOpenAPI.Free;
    oServer.Free;
  end;
end;

Swagger UIにはhttp://localhost:8080/docs、スペックにはhttp://localhost:8080/openapi.jsonにアクセスしてください。スペックで定義されたすべての操作は、解決されたoperationIdと完全に構築されたリクエストコンテキストを伴ってMyOnRequestハンドラーにルーティングされます。

スペックファースト — 既存のOpenAPI 3.0ファイルを読み込む

既存の OpenAPI 3.0 JSON ファイル(Petstore、社内 API コントラクト、モック化したい公開スキーマなど)がある場合、スペックファーストはそれを提供する最速の方法です。LoadFromFileはスペックを読み込んで解析し、pathsセクションからルートテーブルを構築し、受信したすべてのリクエストをそれと照合します。サーバーが読み込めるのは JSON だけなので、YAML のコントラクトはあらかじめ、たとえばsgcOpenAPI.exeで変換しておいてください。

各ルートのoperationIdがディスパッチキーになります。OnRequestの内部で、各操作を順番に処理します:

uses
  sgcHTTP_Server, sgcHTTP_OpenAPI_Server,
  sgcHTTP_OpenAPI_Server_Engine, sgcWebSocket_Server_API_OpenAPI;

procedure TForm1.OnOpenAPIRequest(Sender: TObject;
  const aOperationId: string; const aContext: TsgcOpenAPIServerContext;
  var Handled: Boolean);
begin
  Handled := True;
  if aOperationId = 'listPets' then
    HandleListPets(aContext)
  else if aOperationId = 'getPetById' then
    HandleGetPetById(aContext)
  else if aOperationId = 'createPet' then
    HandleCreatePet(aContext)
  else
    Handled := False;
end;

procedure TForm1.HandleGetPetById(const aContext: TsgcOpenAPIServerContext);
var
  vId, vPetJSON: string;
begin
  vId := aContext.PathParamAsString('petId');
  vPetJSON := FPets.Values[vId];
  if vPetJSON <> '' then
    aContext.RespondJSON(200, vPetJSON)
  else
    aContext.RespondError(404, 'Not Found', 'Pet ' + vId + ' not found');
end;

TsgcOpenAPIServerContextはリクエストのすべてに型付きアクセサを提供します: テンプレート化されたセグメント用のPathParamAsString / PathParamAsInteger、デフォルト値付きのQueryParamAsString / QueryParamAsInteger / QueryParamAsBoolean、リクエストボディ用のBodyAsString / BodyAsJSON、受信ヘッダー用のHeaderValue。応答するには、ヘルパーRespondJSON(code, content)RespondError(code, title, detail)を使うか、完全な制御のためにResponse.CodeResponse.ContentTypeResponse.Contentを直接設定します。

コードファースト — Delphiクラスからスペックを生成する

DelphiでAPIコントラクトを記述してスペックを生成させたい場合は、クラスをRTTI属性で装飾します。TsgcOpenAPICodeFirstScannerがクラスを走査し、完全なOpenAPI 3.0 JSONドキュメントを構築するので、LoadFromStringでサーバーに読み込みます。これにはDelphi XE7以降が必要です(拡張RTTIのため)。

uses
  sgcHTTP_OpenAPI_Server_CodeFirst;

type
  [sgcServiceContract('Task Manager API',
    'A simple task management demo', '1.0.0')]
  [sgcRoute('/api/v1')]
  TTaskManagerService = class
  public
    [sgcHttpGet]
    [sgcRoute('/tasks')]
    [sgcSummary('List all tasks')]
    [sgcTag('Tasks')]
    [sgcResponse(200, 'A list of tasks')]
    procedure ListTasks([sgcFromQuery] const status: string); virtual;

    [sgcHttpPost]
    [sgcRoute('/tasks')]
    [sgcSummary('Create a new task')]
    [sgcTag('Tasks')]
    [sgcResponse(201, 'Task created successfully')]
    procedure CreateTask([sgcFromBody] const body: string); virtual;

    [sgcHttpGet]
    [sgcRoute('/tasks/{taskId}')]
    [sgcSummary('Get a task by ID')]
    [sgcTag('Tasks')]
    [sgcResponse(200, 'The requested task')]
    [sgcResponse(404, 'Task not found')]
    procedure GetTask([sgcFromPath][sgcRequired]
      const taskId: Integer); virtual;
  end;

メソッド本体はスタブです — コンパイラがRTTIを生成するためだけに存在します。実際の処理は、スキャナーが各メソッド名から導出する operationId (ListTasksCreateTaskGetTask…)によってディスパッチされるOnRequest内で行われます。

起動時にクラスをスキャナーに渡し、生成されたスペックをサーバーに読み込みます:

uses
  sgcHTTP_Server, sgcHTTP_OpenAPI_Server_CodeFirst,
  sgcWebSocket_Server_API_OpenAPI;

var
  oScanner: TsgcOpenAPICodeFirstScanner;
  oServer: TsgcHTTPServer;
  oOpenAPI: TsgcWSAPIServer_OpenAPI;
  vSpec: string;
begin
  oScanner := TsgcOpenAPICodeFirstScanner.Create;
  try
    vSpec := oScanner.GenerateSpec(TTaskManagerService);
  finally
    oScanner.Free;
  end;

  oServer := TsgcHTTPServer.Create(nil);
  oServer.Bindings.Add.Port := 8081;

  oOpenAPI := TsgcWSAPIServer_OpenAPI.Create(nil);
  oOpenAPI.LoadFromString(vSpec);
  oOpenAPI.OnRequest := MyOnRequest;
  oOpenAPI.Server := oServer;

  oServer.Active := True;
end;

属性は一般的なメタデータをカバーします: sgcServiceContractはOpenAPIのinfoブロックを設定し、sgcRouteはクラスまたはメソッドレベルでパスを設定し、sgcHttpGet / Post / Put / Delete / Patch / Head / Optionsは動詞を選択し、sgcSummarysgcDescriptionは操作をドキュメント化し、sgcTagはSwagger UIでグループ化し、sgcResponse(code, description)は各応答を宣言し、sgcFromPath / FromQuery / FromBody / FromHeadersgcRequiredと組み合わせて各パラメータを記述します。

設定 — OpenAPIOptions

すべてのサーバーサイド設定は、API コンポーネントのOpenAPIOptionsの下にあり、5 つのサブオプションにグループ化されています。このうち 3 つが日常的な設定を担います:

oServer.OpenAPIOptions.Endpoint.BasePath        := '/api';
oServer.OpenAPIOptions.Endpoint.ServeSpec       := True;   // /openapi.json
oServer.OpenAPIOptions.Endpoint.ServeSwaggerUI  := True;   // /docs

oServer.OpenAPIOptions.CORS.Enabled             := True;
oServer.OpenAPIOptions.CORS.AllowOrigins        := '*';
oServer.OpenAPIOptions.CORS.AllowHeaders        := 'Content-Type, Authorization';
oServer.OpenAPIOptions.CORS.AllowMethods        := 'GET, POST, PUT, DELETE, PATCH, OPTIONS';

oServer.OpenAPIOptions.Validation.ValidateRequest     := True;
oServer.OpenAPIOptions.Validation.ValidateRequestBody := True;
oServer.OpenAPIOptions.Validation.ValidateQueryParams := True;
oServer.OpenAPIOptions.Validation.ValidatePathParams  := True;
oServer.OpenAPIOptions.Validation.ValidateRequired    := True;

バリデーションを有効にすると、受信したすべてのリクエストは、ハンドラーに到達する前に、スペックで宣言されたJSONスキーマに対してチェックされます — 必須フィールド、型、フォーマット、列挙、範囲。失敗するとエラー一覧とリクエストを受け入れるか拒否するかのフラグを伴ってOnValidationErrorイベントが発生します。

イベント

6 つのイベントがリクエストのライフサイクルをカバーします:

OnBeforeRequest: ディスパッチ前に発生します。Accept := Falseを設定すると403 Forbiddenで拒否されます。レート制限、ロギング、またはルートごとのゲートに便利です。

OnAuthenticate: メインハンドラーの前に発生します。Authenticated := Falseを設定すると401 Unauthorizedで拒否されます。ヘッダー、クッキー、またはクエリパラメータを検査して判断します。

OnValidationError: バリデーションが失敗したときに発生し、エラー一覧を受け取ります。Continue := Falseを設定すると400 Bad Requestで拒否されます。

OnRequest: メインディスパッチイベント。aOperationIdを見て、aContext.Responseに応答を書き込み、Handled := Trueを設定します。

OnAfterRequest: ハンドラーが戻った後に発生します — メトリクスや監査ロギングに最適です。

OnException: ハンドラーから処理されない例外が発生した場合に発生します。500 Internal Server Error以外を返したい場合はaResponseCodeを調整します。

残る 2 つのサブオプションにもそれぞれ役割があります。Securityは、スペックが宣言するsecuritySchemesに対してOnValidateAPIKeyOnValidateBasicOnValidateBearerを駆動し、Mockはハンドラーのないオペレーションにスペック自身の例から応答します。

デモ

sgcOpenAPI 2026.6 には 2 つの完全なデモが同梱されており、どちらもスタンドアロンのペアでホストされるため、sgcWebSockets のインストールは不要です:

アップグレード

現在 sgcWebSockets でTsgcWSServer_API_OpenAPIを使用している場合、何も変わりません。クラス、プロパティ、イベントはすべて保持され、実装は共有エンジンに委譲されます。TsgcWSAPIServer_OpenAPIは、その同じクラスを公開した子孫にあたるので、sgcOpenAPI が変えるのはパッケージの出所だけです。

sgcOpenAPI 2026.6は6月にダウンロードページで入手できるようになります。

ご質問、フィードバック、移行の支援が必要な場合 お問い合わせください — コードを書いた本人から返信があります。