3. FHIR资源与RESTful API基础
FHIR 的互操作性建立在两个支柱之上:一是以“资源(Resource)”为最小交换单元的数据模型,二是以 RESTful 风格暴露这些资源的 API 约定。理解这两者的配合方式,是后续做集成、映射与事务处理的前提。
资源:FHIR 的数据原子
FHIR 把医疗信息切分为一组有明确边界的资源类型,例如 Patient(患者)、Observation(检验/观察)、Encounter(就诊)、MedicationRequest(用药医嘱)、Practitioner(医务人员)。每个资源实例都有统一的骨架字段:
resourceType:资源类型名,决定该实例遵循哪套结构定义。id:在某个服务器内的逻辑标识,配合resourceType构成资源的唯一地址。meta:元数据,含versionId、lastUpdated、profile等,用于版本控制与规范约束。text:供人阅读的叙述性内容,便于非结构化展示。- 业务字段:如
Patient.name、Patient.birthDate、Observation.code、Observation.value[x]。
资源之间通过“引用(Reference)”建立关系,而不是嵌套复制。例如 Observation.subject 指向一个 Patient,Observation.encounter 指向一次 Encounter。引用通常写成 Patient/123 这样的相对形式,也可用绝对 URL。这种“引用而非内嵌”的设计让同一份患者数据只存一处,避免多副本不一致。
当标准字段不足以表达本地需求时,FHIR 提供“扩展(Extension)”。扩展以 extension 数组出现,每个扩展由 url 标识其定义,value[x] 承载取值。扩展必须由 StructureDefinition 约束,不能随意发明字段名,否则接收方无法解释。
RESTful API:对资源的统一操作
FHIR 把 HTTP 动词映射到资源操作,形成一套可预测的接口。以 Patient 为例:
| 操作 | HTTP 方法 | 路径示例 | 语义 |
|---|---|---|---|
| 读取 | GET | /Patient/123 | 取回指定患者 |
| 创建 | POST | /Patient | 新建患者,服务器分配 id |
| 更新 | PUT | /Patient/123 | 整体替换该患者 |
| 局部更新 | PATCH | /Patient/123 | 按补丁修改部分字段 |
| 删除 | DELETE | /Patient/123 | 删除或标记删除 |
| 搜索 | GET | /Patient?name=张 | 按条件查询集合 |
创建时,客户端 POST 一个不含 id 的资源,服务器返回 201 Created 并在 Location 头中给出新地址,如 Location: Patient/123/_history/1。更新时若带上 If-Match 头指定版本,可实现乐观锁,防止并发覆盖。
搜索参数:把查询变成可组合的条件
FHIR 搜索不是自由文本,而是基于预定义“搜索参数”。常见类型包括:
- 字符串:
name=张,默认前缀匹配、忽略大小写。 - 令牌:
code=http://loinc.org|1234-5,用于编码值,格式为system|code。 - 日期:
date=ge2024-01-01,支持ge、le、gt、lt前缀。 - 引用:
subject=Patient/123,按引用目标过滤。 - 数量:
value=gt100,用于数值型观察。
多个参数之间是“与”关系,如 /Observation?patient=123&code=1234-5&date=ge2024-01-01 表示该患者、该检验项目、该日期之后的观察。返回结果是一个 Bundle,类型为 searchset,其中 entry 数组承载匹配资源,total 表示总数,link 中的 next 用于分页。
Bundle 与事务处理
Bundle 是 FHIR 的“容器资源”,可承载多个资源并表达它们之间的关系。常见类型有:searchset(搜索结果)、collection(集合)、transaction(事务)、batch(批处理)。其中 transaction 最关键:它要求服务器把包内所有操作作为一个原子单元执行,要么全部成功,要么全部回滚。
事务包中每个 entry 包含 request(方法、URL)和可选的 resource。若条目之间需要互相引用,可用 fullUrl 配合临时 id(如 urn:uuid:...)建立占位引用,服务器在提交后解析为真实地址。下面是一个事务请求的示例结构:
{
"resourceType": "Bundle",
"type": "transaction",
"entry": [
{
"fullUrl": "urn:uuid:patient-1",
"resource": { "resourceType": "Patient", "name": [{"family": "张"}] },
"request": { "method": "POST", "url": "Patient" }
},
{
"resource": {
"resourceType": "Observation",
"status": "final",
"code": { "coding": [{"system": "http://loinc.org", "code": "1234-5"}] },
"subject": { "reference": "urn:uuid:patient-1" },
"valueQuantity": { "value": 5.6, "unit": "mmol/L" }
},
"request": { "method": "POST", "url": "Observation" }
}
]
}
服务器处理成功后返回 200 OK 或 201 Created,响应 Bundle 中每个条目带 response.status 与 response.location,客户端据此获知新建资源的真实地址。若任一条目失败,整个事务回滚,响应中会指出失败条目及原因。
把上述要素串起来看:资源定义“数据长什么样”,引用定义“资源之间怎么连”,扩展定义“标准之外怎么补”,搜索参数定义“怎么找”,CRUD 定义“怎么改”,Bundle 与事务定义“怎么把一组改动作为一个整体提交”。这六件事共同构成 FHIR 集成的最小知识闭环。
urn:uuid: 或 urn:oid: 形式,且同一包内唯一;服务器解析引用时按 fullUrl 匹配,不要用业务字段充当临时标识。