sgcOpenAPI 2026.6 — 独立 OpenAPI 服务器,Spec-First 或 Code-First

· 版本发布

sgcOpenAPI 的下一个版本 — 2026.6,将于六月发布 — 推出了一个全新的组件:TsgcHTTPServer_OpenAPI。这是一个单一的、自包含的 Delphi 组件,内嵌 HTTP 服务器并托管 OpenAPI 3.0 服务。将其放到窗体上,指向一份规范(或通过 RTTI 属性从 Delphi 类生成规范),设置 Active := True,即可拥有一个带有自动提供的 Swagger UI 的、有文档化的 REST API。

本次更新的重头戏是 sgcOpenAPI 托管 HTTP 服务器时不再需要 sgcWebSockets。新组件完全由 sgcOpenAPI 提供、打包和安装。如果您已经使用 sgcWebSockets,之前的 TsgcWSServer_API_OpenAPI 组件将保持原样运作 — 二者内部共享同一套引擎。

一个组件囊括的内容

TsgcHTTPServer_OpenAPI 整合了三大要素:

快速入门 — 最小示例

托管一个带 Swagger UI 的可用 OpenAPI 服务器,所需要的就是这些:

uses
  sgcHTTPServer_OpenAPI;

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

访问 http://localhost:8080/docs 查看 Swagger UI,访问 http://localhost:8080/openapi.json 获取规范。规范中定义的每一个操作都会被路由到您的 MyOnRequest 处理器,并附带已解析的 operationId 和完整构建好的请求上下文。

Spec-First — 加载已有的 OpenAPI 3.0 文件

如果您已经有一份 OpenAPI 3.0 的 JSON 或 YAML 文件(例如 Petstore、一份内部 API 合约或一份您想要模拟的公共架构),spec-first 是提供该服务最快的方式。LoadFromFile 读取并解析规范,根据 paths 部分构建路由表,然后将每一个传入请求与之匹配。

每条路由的 operationId 是分派的关键。在 OnRequest 中,您依次处理每一个操作:

uses
  sgcHTTP_OpenAPI_Server, sgcHTTP_OpenAPI_Server_Engine,
  sgcHTTPServer_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 以获得完全控制。

Code-First — 从 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。实际工作发生在 OnRequest 中,由扫描器从每个方法名(ListTasksCreateTaskGetTask…)派生的 operationId 进行分派。

在启动时将类交给扫描器,并将生成的规范加载到服务器中:

uses
  sgcHTTP_OpenAPI_Server_CodeFirst, sgcHTTPServer_OpenAPI;

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

  oServer := TsgcHTTPServer_OpenAPI.Create(nil);
  oServer.LoadFromString(vSpec);
  oServer.Bindings.Add.Port := 8081;
  oServer.OnRequest := MyOnRequest;
  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

所有服务端配置都位于 OpenAPIOptions 之下,分为三个子选项:

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 Schemas 进行检查 — 必填字段、类型、格式、枚举、范围。失败会触发 OnValidationError 事件,并附带错误列表和一个用于接受或拒绝该请求的标志。

事件

该组件为请求生命周期暴露了六个事件:

OnBeforeRequest: 在分派前触发;设置 Accept := False 可以用 403 Forbidden 拒绝。适用于限流、日志记录或按路由的网关。

OnAuthenticate: 在主处理器之前触发;设置 Authenticated := False 可以用 401 Unauthorized 拒绝。检查头、Cookie 或查询参数来决定。

OnValidationError: 验证失败时触发;接收错误列表。设置 Continue := False 可以用 400 Bad Request 拒绝。

OnRequest: 主分派事件。查看 aOperationId,将响应写入 aContext.Response,设置 Handled := True

OnAfterRequest: 在处理器返回后触发 — 非常适合做指标统计或审计日志。

OnException: 如果有未处理的异常从您的处理器中冒出,则触发此事件。如果您希望返回 500 Internal Server Error 以外的内容,请调整 aResponseCode

演示

sgcOpenAPI 2026.6 附带两个完整的演示,二者都仅使用新的独立组件 — 无需安装 sgcWebSockets:

升级

如果您当前结合 sgcWebSockets 使用 TsgcWSServer_API_OpenAPI,则不会有任何变化 — 类、属性和事件全部保留,并且实现现在委托给与新独立组件相同的共享引擎。您可以保持现有代码原样,或者一次一个窗体地迁移到 TsgcHTTPServer_OpenAPI,以去除对 sgcWebSockets 的依赖。

sgcOpenAPI 2026.6 将于六月在下载页面提供。

有问题、反馈或需要迁移帮助?联系我们 — 您将得到编写这些代码的人的回复。