Delphi 的 OpenAPI 服务器

TsgcWSAPIServer_OpenAPI 提供你加载的 OpenAPI 3.x 文档服务,对照它匹配每一个传入的请求,在你的处理程序运行之前完成请求校验,并从同一个端口发布该文档和一个 Swagger UI 页面。一个 Delphi 组件,挂接到一个 TsgcHTTPServer 上。

OpenAPI 3.0 与 3.1
HTTP/2 + TLS 1.3
Swagger UI 位于 /docs
Spec-first 或 Code-first

TsgcWSAPIServer_OpenAPI

一个 Delphi 组件,把 OpenAPI 文档变成一个运行中、经过校验、自文档化的 REST 服务器。

组件类

TsgcWSAPIServer_OpenAPI,声明在 sgcWebSocket_Server_API_OpenAPI 单元中

宿主服务器

把 Server 赋值为一个 TsgcHTTPServer、TsgcHTTPRESTServer 或 TsgcWebSocketHTTPServer。端口、绑定和 TLS 都由宿主服务器负责。

规范格式

OpenAPI 3.0 和 3.1 文档,通过 LoadFromFile 和 LoadFromString 以 JSON 形式读取

两种工作方式

从已有的文档走规范优先,或者从带特性标注的 Delphi 类走代码优先。代码优先需要 Delphi XE7 或更高版本。

版本

随 sgcOpenAPI 一起提供。在 sgcWebSockets 中属于 Enterprise 版本,位于 SGC OpenAPI 面板页。

内置端点

/openapi.json 提供文档,/docs 提供 Swagger UI,两者都在 OpenAPIOptions.Endpoint 中开启

Spec-first 或 Code-first,由你选择

同一个组件支持两种模式。从一份 JSON 契约开始,或者在 Delphi 中描述 API,让扫描器为你生成文档。

1. Spec-first

加载 petstore.json,用 LoadFromFile,在 OnRequest 内部按 operation id 分发,然后开始服务。路由、路径与查询参数绑定以及校验全部来自契约,你只需要编写业务逻辑。

最适合:拥有共享设计契约的团队、API-led 集成,或者将规范作为真理之源的多语言后端。

2. Code-first

用 sgcServiceContract、sgcRoute、sgcHttpGet 以及 sgcFromPath / sgcFromQuery / sgcFromBody 参数特性标注一个普通的 Delphi 类。TsgcOpenAPICodeFirstScanner.GenerateSpec 会根据类的 RTTI 构建 OpenAPI 文档,你把它交给 LoadFromString,同一个 /openapi.json 端点就会发布它。

最适合:快速原型设计、内部服务,或将现有的 TIdHTTPServer / DataSnap REST 接口迁移到自文档化的 API。

20 行代码搞定一个可工作的服务器

创建组件,加载一份文档,把它挂接到一个 HTTP 服务器上。这就是全部配置。

Delphi
uses
  sgcHTTP_Server, sgcHTTP_OpenAPI_Server, sgcWebSocket_Server_API_OpenAPI;

procedure TForm1.FormCreate(Sender: TObject);
begin
  FServer := TsgcHTTPServer.Create(Self);
  FServer.Port := 8080;

  FOpenAPI := TsgcWSAPIServer_OpenAPI.Create(Self);
  FOpenAPI.LoadFromFile('petstore.json');      // any OpenAPI 3.x document
  FOpenAPI.OpenAPIOptions.Endpoint.ServeSpec := True;
  FOpenAPI.OpenAPIOptions.Endpoint.ServeSwaggerUI := True;
  FOpenAPI.OnRequest := OpenAPIRequest;
  FOpenAPI.Server := FServer;                // Server is the switch, there is no Active

  FServer.Active := True;
end;

// one event, dispatched by operation id
procedure TForm1.OpenAPIRequest(Sender: TObject;
  const aOperationId: string;
  const aContext: TsgcOpenAPIServerContext;
  var Handled: Boolean);
begin
  Handled := True;
  if aOperationId = 'getPetById' then
    aContext.RespondJSON(200, FPets.Values[aContext.PathParamAsString('petId')])
  else
    Handled := False;
end;

开箱即得的功能: GET /pets/{petId} 会到达上面的处理程序,此时 aOperationId 为 getPetById, GET /openapi.json 返回你加载的文档, GET /docs 打开 Swagger UI。 OpenAPIOptions.Endpoint.BasePath 可以把整个接口移到某个前缀之下,TLS 和 HTTP/2 都由宿主服务器提供。

在 OpenAPI 文档中声明的参数会通过单一的类型化上下文读取并转换。开启校验后,错误的类型会在你的处理程序运行之前就返回 400 Bad Request。

Delphi
// spec snippet
//   /pets:
//     get:
//       operationId: listPets
//       parameters:
//         - name: limit       in: query    schema: { type: integer, maximum: 100 }
//         - name: status      in: query    schema: { type: string, enum: [available, pending, sold] }
//         - name: X-Tenant-Id in: header   required: true

procedure TForm1.HandleListPets(const aContext: TsgcOpenAPIServerContext);
var
  vLimit:  Integer;
  vStatus: string;
  vTenant: string;
begin
  vLimit  := aContext.QueryParamAsInteger('limit', 20);        // default 20
  vStatus := aContext.QueryParamAsString ('status', 'available');
  vTenant := aContext.HeaderValue        ('X-Tenant-Id');   // required in the spec

  aContext.RespondJSON(200, PetRepo.List(vTenant, vStatus, vLimit));
end;

在处理程序运行前完成 Schema 校验

每一个传入的请求都会对照文档声明的 schema 进行检查。校验失败时会返回一份 RFC 7807 风格的问题文档,列出每一项错误,并且除非你特意允许,否则请求不会到达你的处理程序。

检查哪些内容

type、required、properties 和 additionalProperties,enum 和 const,minLength / maxLength,pattern,minimum / maximum 及其排他形式,multipleOf,items,minItems / maxItems,uniqueItems,nullable,not,以及 oneOf / anyOf / allOf。format 关键字会对 date、date-time、email、ipv4、uri 和 uuid 强制执行。

选择校验范围

Validation.ValidateRequest 是总开关,单独打开就会校验所有范围。用 ValidateRequestBody、ValidateQueryParams、ValidatePathParams、ValidateHeaderParams 和 ValidateCookieParams 来缩小范围。无论选择哪个范围,EnforceRequired 始终生效。

由你做最终决定

OnValidationError 会把 operation id 和完整的失败列表交给你。它的 Continue 标志到达时是 False,所以除非你特意把它设为 True,否则请求会被拒绝。加载完成后,Validation.Warnings 会列出文档中用到但未被强制执行的每一个 schema 关键字,空列表就意味着没有遗漏。

JSON,引擎实际写出的 400 响应
{
  "type":   "about:blank",
  "title":  "Bad Request",
  "status": 400,
  "detail": "Request validation failed",
  "errors": [
    "/email: invalid email format",
    "/age: must be <= 120",
    "/status: value not in enum"
  ]
}

由规范驱动的认证方案

把 Security.EnforceSecurity 设为开启后,文档声明的 securitySchemes 就会应用到传入的请求上。你只需编写凭据查找逻辑,组件负责解析请求,并在查找结果为否时返回 401 或 403。

API Key

从 header、查询参数或 cookie 中读取,具体取决于该方案的声明。OnValidateAPIKey 会收到方案、名称、位置和 key,并通过 Valid 返回结果。

HTTP Basic

Authorization 请求头已经替你解析好。OnValidateBasic 会收到用户名和密码,并通过 Valid 返回结果。凭据永远不会写入日志。

Bearer 与 JWT

Security.JWTSecret 用于验证令牌。原样使用即视为 HMAC 密钥,包含 -----BEGIN 的值则视为 PEM 公钥。ValidateExpiration、Issuer 和 Audience 会检查这些声明。

自定义校验器

把 JWTSecret 留空,令牌就只会检查是否存在,这样 OnValidateBearer 就可以把它交给你自己的令牌服务,并通过 Valid 返回结果。

401 还是 403

校验失败的请求会返回 401,如果请求已经通过身份验证只是权限不够,则返回 403。OnAuthenticate 最先运行,一旦你清除 Authenticated,就会以 401 拒绝请求。

在代码写好之前先模拟

Mock.Enabled 会用文档自带的示例和 schema,为一个还没有处理程序的操作生成响应,并附带 Mock.StatusCode,这样前端团队就能在实现完成之前先动手。

Delphi,用你自己的代码校验 bearer 令牌
FOpenAPI.OpenAPIOptions.Security.EnforceSecurity := True;
FOpenAPI.OpenAPIOptions.Security.JWTSecret := GetSecretFromEnvironment;
FOpenAPI.OpenAPIOptions.Security.ValidateExpiration := True;
FOpenAPI.OpenAPIOptions.Security.Issuer   := 'https://auth.example.com';
FOpenAPI.OpenAPIOptions.Security.Audience := 'api.example.com';
FOpenAPI.OnValidateBearer := OpenAPIValidateBearer;

procedure TForm1.OpenAPIValidateBearer(Sender: TObject;
  const aToken: string;
  const aContext: TsgcOpenAPIServerContext; var Valid: Boolean);
begin
  Valid := MyTokenService.Verify(aToken);
end;

内置 Swagger UI

无需外部依赖、无需 Node.js,部署流程中也不必构建文档。组件会自己生成这个页面,并读取你的服务器实际提供的文档。

/openapi.json

你加载的文档,在 Endpoint.ServeSpec 开启时提供服务,始终与服务器实际路由的内容保持一致。把任何客户端生成器指向这个 URL 都可以,包括 sgcOpenAPI。

/docs

交互式 Swagger UI 页面,在 Endpoint.ServeSwaggerUI 开启时提供服务。可以试用操作、浏览 schema、查看示例,全部数据都来自你正在运行的服务器。

可固定版本,也可完全离线

该页面默认从公共 CDN 加载 CSS 和 JavaScript。Endpoint.SwaggerUIBaseURL 可以固定版本,Endpoint.SwaggerUIAssetsPath 则可以让 swagger-ui.css 和 swagger-ui-bundle.js 从本地目录提供服务,这样即使在完全离线的机器上也能工作。

一切都在 OpenAPIOptions 之下

五个持久化的子对象,全部可见于对象检查器,也都可以在运行时赋值。

Endpoint

BasePath 会给每个路由和两个内置端点都加上前缀。ServeSpec 和 ServeSwaggerUI 用于开关它们。SpecFile 是懒加载的,只会在既不是这两者的第一个请求上加载,所以当文档必须从第一次调用起就完整时,请使用 LoadFromFile。

Validation

ValidateRequest 加上五个范围开关,以及 EnforceRequired。每次加载后,Warnings 会报告文档用到但该校验器未强制执行的 schema 关键字。

CORS

Enabled、AllowOrigins、AllowHeaders 和 AllowMethods。引擎只在其文档拥有的路径上盖这些响应头,所以要给宿主服务器拥有的路径设置相同的值。

Security

EnforceSecurity、JWTSecret、ValidateExpiration、Issuer 和 Audience。内置检查无法判定的一切都会交给 OnValidateAPIKey、OnValidateBasic 或 OnValidateBearer。

Mock

Enabled 和 StatusCode。没有处理程序的操作会用文档自带的示例和 schema 来回答,这样契约在实现完成之前也能调用。

特意保留的 Not Implemented

把 Handled 留在 False,引擎就会返回 501 Not Implemented 并指明该操作,而不是看起来像路由出错的 404。

一个 HTTP 服务器,多种接口

TsgcWSAPIServer_OpenAPI 挂接到承载你的 WebSocket 端点、AI/LLM 流和静态文件的同一个 sgcWebSockets HTTP 服务器上。一个端口、一份 TLS 证书、一条日志流。

Server 就是开关

这里没有 Active 属性。给 Server 赋值就会挂接组件,把它设为 nil 就会分离,宿主服务器全程保持运行不受影响。分离之后,其文档拥有的路径会直接落到你原来的处理程序上。

它绝不会接管服务器

每个请求都会先交给这个组件,它只回答其文档声明的路径。其余的请求照旧到达 OnCommandGet,所以一个契约优先的区域可以和手写的路由以及 DocumentRoot 提供的静态内容共存于同一个端口上。

宿主的 TLS 和 HTTP/2

端口、绑定、证书和 HTTP/2 协商都属于宿主服务器,所以 REST 接口会原样继承它们。把它挂接到一个 TsgcHTTPRESTServer 上,该服务器的 CORS、指标、健康检查和租户功能也同样适用。

典型部署

公开 REST API

带版本、契约测试,自动生成的 SDK 可供客户从 /openapi.json 下载。

内部微服务

能挺过重构的服务间契约 — 规范就是集成测试。

工业 / 物联网网关

边缘设备从同一个 Delphi 二进制文件中既暴露文档化的 REST 控制面,又暴露 MQTT 或 WebSocket 遥测接口。

Webhook 接收端

每个供应商的 webhook 载荷都变成类型化的 Pascal 记录 — Stripe、GitHub、Twilio、Slack — 内置校验与幂等性。

遗留系统现代化

在不重写业务逻辑的前提下,把老旧的 DataSnap 或 RemObjects 后端包装在干净的 OpenAPI 接口之后。

BFF(Backend-for-Frontend)

把两三个上游 API 聚合到一份贴合消费者形态的规范背后 — 你的 SPA 或移动 App 只与一个类型化的端点对话。

搭配使用

OpenAPI Parser

把任意外部规范加载到与服务器使用的同一模型中 — 相同的校验、相同的类型系统、相同的安全原语。

预构建的云 SDK

1,195+ 个为 AWS、Azure、GCP、Stripe、GitHub、Kubernetes 等生成的 SDK — 你的服务器可以用同一族组件调用任何一个。

sgcWebSockets

WebSocket、MQTT、AMQP、WebRTC、AI/LLM、IoT — HTTP 服务器能与你的 REST 接口一同承载的一切。

sgcSign

用 XAdES / PAdES / CAdES 对请求和响应体进行签名,面向受监管行业 — 每个操作都具有 eIDAS 级别的完整性。

超值之选:All-AccesseSeGeCe 全部产品,含高级支持,每年 €1,059 起。
查看 All-Access 价格

几分钟内构建你的第一个 OpenAPI 服务器

下载免费试用版。完整的服务器、两套 UI、所有认证方案 — 没有功能限制,评估期间也没有时间炸弹。