sgcOpenAPI 的下一个版本,2026.6,将于六月发布,带来一个无需安装 sgcWebSockets 就能托管的 OpenAPI 3.0 服务器。这需要两个组件:TsgcHTTPServer,也就是基于 Indy 的 HTTP 服务器,以及 TsgcWSAPIServer_OpenAPI,也就是你挂接到它上面的 API 组件。把 API 组件指向一份规范(或通过 RTTI 属性从 Delphi 类生成规范),赋值它的 Server 属性,启动 HTTP 服务器,即可拥有一个带自动提供的 Swagger UI 的、有文档化的 REST API。
本次更新的重头戏是 sgcOpenAPI 托管 HTTP 服务器时不再需要 sgcWebSockets。两个组件都完全由 sgcOpenAPI 提供、打包和安装。如果您已经在使用 sgcWebSockets,您熟悉的 TsgcWSServer_API_OpenAPI 组件会保持原样运作,因为它就是同一个类:两个产品都是从同一套引擎构建出来的。
你会得到什么
这一对组件带来三件事:
- 一个HTTP 服务器(基于 Indy),也就是
TsgcHTTPServer,具有常规的Bindings、Port和Active属性。 - OpenAPI 引擎:规范解析、带
{paramName}段的路径模板路由、JSON-Schema 验证、CORS、异常处理。 - 两个自动提供的端点:位于
/openapi.json的规范和位于/docs的 Swagger UI。两者默认均已启用,可在OpenAPIOptions.Endpoint中切换。
快速入门 — 最小示例
托管一个带 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;
访问 http://localhost:8080/docs 查看 Swagger UI,访问 http://localhost:8080/openapi.json 获取规范。规范中定义的每一个操作都会被路由到您的 MyOnRequest 处理器,并附带已解析的 operationId 和完整构建好的请求上下文。
Spec-First — 加载已有的 OpenAPI 3.0 文件
如果您已经有一份 OpenAPI 3.0 的 JSON 文件(例如 Petstore、一份内部 API 合约或一份您想要模拟的公共架构),spec-first 是提供该服务最快的方式。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.Code、Response.ContentType 和 Response.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 中,由扫描器从每个方法名(ListTasks、CreateTask、GetTask…)派生的 operationId 进行分派。
在启动时将类交给扫描器,并将生成的规范加载到服务器中:
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 选择动词,sgcSummary 和 sgcDescription 为操作编写文档,sgcTag 在 Swagger UI 中对其进行分组,sgcResponse(code, description) 声明每个响应,而 sgcFromPath / FromQuery / FromBody / FromHeader 与 sgcRequired 一起用于描述每个参数。
配置 — OpenAPIOptions
所有服务端配置都位于 API 组件的 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。
剩下的两个子选项各有各的作用:Security 为规范声明的 securitySchemes 驱动 OnValidateAPIKey、OnValidateBasic 和 OnValidateBearer,Mock 则会用规范自带的示例,为一个还没有处理程序的操作生成响应。
演示
sgcOpenAPI 2026.6 附带两个完整的演示,二者都由这一对独立组件承载,因此不需要安装 sgcWebSockets:
- Demos/30.Server/01.OpenAPI_Server_CodeFirst,一个完全使用 Delphi 类上的特性来定义的 Task Manager API。
- Demos/30.Server/02.OpenAPI_Server_SpecFirst,经典的 Petstore 示例,由
petstore.json提供服务。
升级
如果您目前在 sgcWebSockets 中使用 TsgcWSServer_API_OpenAPI,不会有任何变化。这个类、它的属性和事件全部保留,实现也委托给同一套共享引擎。TsgcWSAPIServer_OpenAPI 正是同一个类对外发布的后代,所以 sgcOpenAPI 改变的只是这个包来自哪里。
sgcOpenAPI 2026.6 将于六月在下载页面提供。
有问题、反馈或需要迁移帮助?联系我们 — 您将得到编写这些代码的人的回复。
