本文档说明平台 FHIR Restful 接口的通用调用方式,包括请求结构、公共参数、接口鉴权以及返回结果。调用方在访问 FHIR 资源接口前,需先通过 API 密钥和签名机制获取
AccessToken,再以 Bearer Token 方式访问业务接口。请求结构
平台调用过程分为两个阶段:
调用鉴权服务,使用
API Key、时间戳和签名换取 AccessToken。在访问 FHIR Restful 接口时,通过
Authorization: Bearer <AccessToken> 携带访问令牌。鉴权服务请求结构
请求方式:
POST /cmd/GetAccessToken HTTP/1.1Host: <FHIR_SERVICE_HOST>Content-Type: application/json
说明:
<FHIR_SERVICE_HOST> 为鉴权服务访问域名,请以腾讯健康数据服务控制台实例接入信息或部署方提供的服务访问地址为准。示例中的
<FHIR_SERVICE_HOST> 仅为占位符,调用时需替换为实际可访问域名,不要直接使用占位符发起请求。请求体结构:
{"header": {"version": "v0.1","flag": 0},"body": {"seq": 0,"cmd": "","token": "","traceid": "","client": {"platform": 0,"env": "","isTourist": 0,"product": 0},"payload": {"timestamp": 1730000000000,"apiKey": "<API_KEY>","signature": "<SIGNATURE>","scopes": [],"context": {}}}}
说明:
timestamp 为毫秒级 Unix 时间戳。signature 为基于 API Secret 计算得到的签名值。scopes 和 context 为扩展字段,可按实际授权场景传入。FHIR Restful 请求结构
请求方式根据接口能力不同而变化,常见方法如下:
POST:创建资源,或提交 Bundle 事务/批处理请求。GET:读取资源、读取历史版本、执行搜索。PUT:更新完整资源内容。PATCH:更新局部字段。DELETE:删除指定资源。通用请求示例:
GET /INSTANCE_ID/fhir/Patient/199963 HTTP/1.1Host: HOSTNAMEAuthorization: Bearer <AccessToken>Accept: application/fhir+json
请求地址通常采用以下形式:
资源读写:
[baseUrl]/[resourceType] 或 [baseUrl]/[resourceType]/[id]历史版本读取:
[baseUrl]/[resourceType]/[id]/_history/[versionId]搜索:
[baseUrl]/[resourceType]?[searchParams]患者全量信息检索:
[baseUrl]/Patient/[id]/$everythingBundle 事务/批处理:
[baseUrl]公共参数
鉴权请求公共参数
参数名称 | 类型 | 是否必填 | 说明 |
header.version | String | 是 | 请求协议版本,示例为 v0.1 |
header.flag | Integer | 是 | 请求标记,示例为 0 |
body.seq | Integer | 是 | 请求序号,示例为 0 |
body.cmd | String | 否 | 预留命令字段 |
body.token | String | 否 | 预留令牌字段,获取 AccessToken 时通常为空 |
body.traceid | String | 否 | 请求链路追踪标识 |
body.client.platform | Integer | 否 | 客户端平台标识 |
body.client.env | String | 否 | 调用环境标识 |
body.client.isTourist | Integer | 否 | 游客标识 |
body.client.product | Integer | 否 | 产品标识 |
body.payload.timestamp | Integer | 是 | 毫秒级时间戳。服务端允许的时间窗口为请求到达服务端时间前后3600000毫秒(1小时),超出窗口会被拒绝 |
body.payload.apiKey | String | 是 | API 密钥 |
body.payload.signature | String | 是 | 签名值 |
body.payload.scopes | Array | 条件必填 | 申请的权限范围。开启知情同意访问控制时必填,且至少含1个 actor/<资源类型>/<ID>(如 "actor/Practitioner/P001"),否则访问 FHIR 接口返回401(Claims 中缺少 scope 字段)。未开启时可留空([]) |
body.payload.context | Object | 否 | 上下文扩展参数 |
FHIR 请求公共参数
参数名称 | 类型 | 是否必填 | 说明 |
Host | String | 是 | |
Authorization | String | 是 | 访问令牌,格式为 Bearer <AccessToken> |
Content-Type | String | 否 | 请求体类型,写入类请求通常为 application/fhir+json;Patch 请求通常为 application/json-patch+json |
Accept | String | 否 | 响应格式,建议使用 application/fhir+json |
resourceType | String | 视接口而定 | FHIR 资源类型,如 Patient、Observation、Encounter |
id | String | 视接口而定 | 资源唯一标识 |
searchParams | Query String | 否 | 搜索参数,用于筛选结果集 |
versionId | String | 否 | 历史版本号,用于 vRead 等场景 |
接口鉴权
平台采用基于
API Key、API Secret 和签名机制的令牌鉴权方式。调用方需先向鉴权服务申请 AccessToken,再携带该令牌访问 FHIR 接口。鉴权流程
调用方持有服务对应的
API Key 和 API Secret。调用方生成毫秒级时间戳
timestamp。调用方使用签名算法生成
signature。调用方将
apiKey、timestamp、signature 提交至鉴权接口。鉴权服务校验签名和时间戳有效性。
校验通过后返回基于 JWT 的
AccessToken。调用方在访问 FHIR Restful 接口时通过
Authorization 请求头携带该令牌。FHIR 服务收到请求后校验
AccessToken。校验通过则返回业务结果,校验失败则拒绝访问。
签名算法
签名原文拼接方式:
message = apiKey + timestamp
签名算法:
signature = HMAC-SHA256(apiSecret, message)
说明:
message 使用 UTF-8 编码。timestamp 为毫秒级时间戳。签名结果使用十六进制字符串表示。
示例代码显式返回大写十六进制字符串;服务端进行校验时对签名大小写不敏感。
时间戳校验规则
服务端会校验请求时间戳是否在允许的超时时间窗口内。当前允许的时间窗口为请求到达服务端时间前后
3600000 毫秒(1 小时):若
timestamp > 服务端当前时间 + 3600000,请求将被拒绝。若
timestamp < 服务端当前时间 - 3600000,请求将被拒绝。典型失败情况如下:
signTime is timed outsignature is invalid访问令牌使用方式
获取到
AccessToken 后,调用方需在访问 FHIR 接口时将其放入 HTTP Header:Authorization: Bearer <AccessToken>
字段说明如下:
Header Key:
AuthorizationHeader Value:
Bearer <空格><AccessToken>示例代码
签名获取 access_token 方法。
import hashlibimport hmacimport timefrom typing import Optionalimport requestsdef sign(api_key: str, api_secret: str, sign_time: int) -> str:"""签名Args:api_key: API密钥api_secret: API密钥对应的秘密sign_time: 签名时间(毫秒)Returns:签名字符串(大写十六进制)"""message = f"{api_key}{sign_time}".encode("utf-8")h = hmac.new(api_secret.encode("utf-8"), message, hashlib.sha256)return h.hexdigest().upper()# 校验签名逻辑(本地自测用)def check_signature(api_key: str, api_secret: str, signature: str, sign_time: int, timeout: int) -> Optional[str]:"""校验签名Args:api_key: API密钥api_secret: API密钥对应的秘密signature: 签名字符串sign_time: 签名时间(毫秒)timeout: 超时时间(毫秒)Returns:None表示校验通过,否则返回错误信息"""now_time = int(time.time() * 1000) # 当前时间(毫秒)if sign_time > now_time + timeout or sign_time < now_time - timeout:return "signTime is timed out"calculated_sign = sign(api_key, api_secret, sign_time)if signature.upper() != calculated_sign.upper():return "signature is invalid"return Nonedef request_auth_server(api_key: str, api_secret: str) -> Optional[str]:sign_time = int(time.time() * 1000)signature = sign(api_key, api_secret, sign_time)rsp = requests.post(# 将 <FHIR_SERVICE_HOST> 替换为实际鉴权服务访问域名url="https://<FHIR_SERVICE_HOST>/cmd/GetAccessToken",json={"header": {"version": "v0.1","flag": 0,},"body": {"seq": 0,"cmd": "","token": "","traceid": "","client": {"platform": 0,"env": "","isTourist": 0,"product": 0,},"payload": {"timestamp": sign_time,"apiKey": api_key,"signature": signature,"scopes": [],"context": {},},},},timeout=10,)if rsp.status_code != 200:return Nonedata = rsp.json()if data.get("retcode", 0) != 0:return Nonepayload = data.get("payload") or {}return payload.get("accessToken")if __name__ == "__main__":key = "<KEY>"secret = "<SECRET>"token = request_auth_server(key, secret)print(token)
返回结果
鉴权接口返回结果
鉴权接口调用成功后,返回结果中包含
accessToken,调用方应妥善保存并在后续 FHIR 请求中使用。返回结果示例如下:{"payload": {"accessToken": "<ACCESS_TOKEN>"}}
若鉴权接口调用失败,可能出现以下情况:
HTTP 状态码非
200。响应中未返回
accessToken。签名校验失败或时间戳失效。
FHIR 接口返回结果
FHIR 接口返回结果遵循标准 FHIR 资源结构,不同接口场景返回内容不同:
资源创建、更新、读取类接口:通常返回对应资源内容。
搜索类接口:通常返回
Bundle。删除类接口:通常返回
OperationOutcome 或状态结果。事务/批处理类接口:通常返回结果
Bundle。常见返回头包括:
参数名称 | 说明 |
Status Code | HTTP 状态码,用于标识请求处理结果 |
ETag | 资源版本标识 |
Location / Content-Location | 资源或历史版本访问地址 |
返回结果处理
对写入类接口,建议结合
Status Code、ETag 和 Location 判断是否写入成功。对搜索类接口,建议优先解析
Bundle.entry、Bundle.total 和 Bundle.link。对删除和异常场景,建议结合
OperationOutcome 或错误响应体定位具体原因。