overview.md 16 KB

API

作者:Sand,2017.11.07

概述

本文档为REST API调用规范,旨在为第三方应用提供访统一的问平台调用与交互方式,通过这些接口可以访问患者的健康数据,医疗资源及医疗机构等数据。

所有API都是通过向平台的REST服务器发送HTTP请求来实现,REST服务器支持gzip,可有效降低网络开销,建议第三方应用加入gzip支持。

术语解释

  • Client ID: 在平台注册App时,由平台提供的唯一App标识。
  • Client Secret:在平台注册App时为其分配的应用密码。
  • 用户名:在平台注册用户时,由平台提供的唯一用户标识。
  • 用户密码:与用户ID相匹配的用户密码,由用户保管。
  • 机构代码:在平台注册机构时,使用此机构在国家机构数据库中注册的代码。
  • 机构公钥:在平台注册机构时,平台为机构提供的公钥。

版本

平台API均被版本化,当前版本为v1.0,版本号通过URL指定,例如:

api/v1.0/user?age=20

请务必在请求中包含版本号以便请求能够得到正确响应。

请求与响应通用模式

所有API请求均通过HTTPS协议。API服务器地址:

数据发送与接收均以JSON作为载体。例如:数组参数array。普通字符串直接使用URL。

curl -i https://ehr.yihu.com/api/v1.0/users

HTTP/1.1 200 OK
Server: nginx
Date: Fri, 12 Oct 2012 23:33:14 GMT
Content-Type: application/json; charset=utf-8
Connection: keep-alive
Status: 200 OK
ETag: "a00049ba79152d03380c34652f2cb612"
Content-Length: 5
Cache-Control: max-age=0, private, must-revalidate

JSON结构中字段为空的时候,使用null表示,而不是将其丢弃。所有时间戳数据均以ISO 8601格式返回:

YYYY-MM-DDTHH:MM:SSZ

资源摘要

当请求资源列表时,返回值是特定资源属性的一个子集,而非所有属性,即“资源摘要”(某些资源的属性会占用过多的系统资源), 鉴于性能原因应用默认不返回这些属性。若要获取这些属性,通过资源的详细信息来获取。

例如,当请求用户列表的时候,你将得到用户列表,其中只有部分属性有返回值

GET /users

资源详情

当请求单个资源时,平台将会返回这个资源的所有属性,即“资源详情”(注意,用于不同的用户权限,其返回值可能有所不同)。

例如,当请求某个用户的详细信息时,将会返回这个用户的所有数据:

GET /users/1024

参数

很多API都含有可选参数。例如,对于GET请求,非路径变量参数可以通过HTTP查询参数指定:

curl -i "https://ehr.yihu.com/v1/users/1024?age=20"

在这个示例中,用户的年龄参数通过*:age*查询字符串来指定。

对于POST, PUT与DELETE请求,不在URL中指定的参数必须封装成JSON,并指定Content-Type为'application/json':

curl -i -u username -d '{"age":["20"]}' https://ehr.yihu.com/users

全局入口

通过向根路径发送GET请求,将会得到平台所有支持的API列表:

curl https://ehr.yihu.com/api

可以从返回的JSON得到你所需要的API列表。入口处显示平台API列表可以点击进去。列表只显示各API的第一个GET方法链接。若含有多个GET方法会被忽略。 GET方法点击进去之后,根据方法的实际情况:

  • 若方法不需要认证,可以修改URL中的查询参数就可以得到结果
  • 若方法需要认证,此时返回API的错误信息。结构如下

    {

    message: "需要认证",
    documentation_url: http://ehr.yihu.com/docs/help.html
    

    }

另外,平台提供了Swagger工具,用户可以直接访问API列表。

客户端错误

客户端请求时,服务端可能会返回以下三种类型的API错误,具体的错误信息在响应体中:

  • 若请求参数是一个无效的JSON,服务端将返回400错误:

    HTTP/1.1 400 Bad Request Content-Length: 35

    {"message":"Problems parsing JSON"}

  • 若客户端发送的是一个错误的JSON值,服务端将返回400错误:

    HTTP/1.1 400 Bad Request Content-Length: 40

    {"message":"Body should be a JSON object"}

  • 若客户端发送的数据包含无效的字段,服务端将返回422错误:

    HTTP/1.1 422 Unprocessable Entity Content-Length: 149

    { "message": "Validation Failed", "errors": [

    {
      "resource": "Issue",
      "field": "title",
      "code": "missing_field"
    }
    

    ] }

所有的错误对象都含有相应的资源与属性字段,用以告诉客户端可能的错误原因。另外,错误对象中还有一个错误代码,用于告诉哪个字段发生错误。 错误代码列表如下:

错误名称 描述
missing 资源不存在
missing_field 请求的资源不含请求的字段
invalid 字段的格式无效。请参考指定的资源文档以获取详细信息
already_exists 此资源的字段值与另一个资源一样。这种情况大多发生在资源的主键重复,例如:对象ID

以上是通用的错误对象,资源也可能返回一些自定义错误消息,自定义错误消息包含一个message字段描述该错误,且大多数错误消息都 含有一个documentation_url字段指向可能对解决问题有帮助的页面。

HTTP重定向

平台API在适当的时候会使用HTTP重定义功能。客户端应该假设所有的请求都有可能发生重定向。客户端接收到HTTP重定向时, 这不是一个错误并且客户端需要跟随重定向链接。重定向链接包含在URI资源的返回头*Location*字段中。

HTTP状态码 描述
301 永久重定向。所请求的资源已被永久转移到*Location*指定的位置,客户端应该使用该URI重新请求该资源。
302,307 临时重定向。所请求的资源暂时转移到*Location*指定的位置,客户端应该使用该URI重新请求该资源。

HTTP动词

平台使用HTTP动词代表一个操作,目前支持以下动词

动词 描述
GET 获取资源
POST 创建资源
PUT 更新一个或多个资源
DELETE 删除资源

授权

平台API有以下两种鉴权模式。大多数情况下,若不请求中不包含token会返回 *403 Forbidden*,但有些API出于保护资源的目的可能会返回*404 Not Found*错误。

更多关于OAuth2的信息,请参考(OAuth2规范)[https://developer.github.com/v3/oauth/]。OAuth2 Token获取请查询授权接口。

该请求返回的Token仅能用于服务端-服务端的场景,请不要部署两个一样的应用,然后分别向服务端请求Token,会互相覆盖。 请保管好应用Secret,不要泄露。

  • 登录失败处理

使用非法凭据来请求资源将会返回*401 Unauthorized*错误:

curl https://ehr.yihu.com/api/resource?access_token=OAUTH-TOKEN
HTTP/1.1 401 Unauthorized

{
  "message": "Bad credentials",
  "documentation_url": "https://ehr.yihu.com/api"
}

如果在短时间内,服务端接收到多个使用非法凭据的请求,后续该开发者的所有API请求在一段时间内都将被拒绝(包括使用合法凭据的请求),返回的错误为403 Forbidden

curl -i https://ehr.yihu.com/api/resource?access_token=INVALID-OAUTH-TOKEN
HTTP/1.1 403 Forbidden

{
  "message": "Maximum number of login attempts exceeded. Please try again later.",
  "documentation_url": "https://ehr.yihu.com/api"
}

超媒体

所有的资源都含有一个或多个*_url的属性,以链接到其他资源。这样做可以帮助客户端减少自己构建其关联资源的构建代码。强烈建议客户端使用这些URL。 这样做对以后开发者升级API会有帮助。所有的URL都符合 RFC 6570URI 模板。

>> tmpl = URITemplate.new('/notifications{?since,all,participating}')
>> tmpl.expand
=> "/notifications"

>> tmpl.expand :all => 1
=> "/notifications?all=1"

>> tmpl.expand :all => 1, :participating => 1
=> "/notifications?all=1&participating=1"

分页

当请求的资源过多时,会默认以15条/页的数据返回。可以指定新的页码*page*与页大小*size*以返回更多的数据。

curl 'https://aehr.yihu.com/api/users?page=2&per_page=100'

注意,分布起始值为1,若请求中不提供page参数,默认返回第一页的数据。

Link头

分页信息包含在响应头的Link字段中。建议使用此字段的值作为资源导航,而不是自己构建URL。某些情况下,分页信息会使用SHA1并且不提供页码。

Link: <https://ehr.yihu.com/api/user/repos?page=3&per_page=100>; rel="next",
  <https://ehr.yihu.com/api/repos?page=50&per_page=100>; rel="last"

Link字段包含一个或多个超媒体链接关系。*rel*可能值如下:

名称 描述
next 下一页结果
last 最后一页结果
first 第一页结果
previous 前一页结果

请求频率与限制

对认证过的请求,客户端每个小时的请求频率限制为5000次,对于未认证过的请求,客户端的请求频率限制为60次/小时。未认证的请求与IP绑定而不是与用户名绑定。

此情况对于搜索类型的API不适用,其频率限制有自身的规则。

通过检查HTTP响应头可以得到当前的请求频率限制:

curl -i https://ehr.yihu.com/api/users/whatever
HTTP/1.1 200 OK
Date: Mon, 01 Jul 2013 17:27:06 GMT
Status: 200 OK
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 56
X-RateLimit-Reset: 1372700873

从请求头中你可以得知你所需要的信息:

字段名称 描述
X-RateLimit-Limit 每小时最大请求次数
X-RateLimit-Remaining 当前时间内剩余请求次数
X-RateLimit-Reset 下一次请求次数重置时间,UTC epoch秒数

若想要将X-RateLimit-Reset时间转换为可读的,可以使用以下代码转换:

new Date(1372700873 * 1000)
// => Mon Jul 01 2013 13:47:53 GMT-0400 (EDT)

一旦请求次数用完,你将得到以下错误消息:

HTTP/1.1 403 Forbidden
Date: Tue, 20 Aug 2013 14:50:41 GMT
Status: 403 Forbidden
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1377013266

{
   "message": "xxx.xxx.xxx.xxx的API请求超过限制(好消息:认证提限优惠大酬宾,活动参见链接)",
   "documentation_url": "https://ehr.yihu.com/docs/api/v1/#rate-limiting"
}
  • 增加未授权的应用请求次数

若想增加未授权的应用请求次数,你需要在查询字段串中传递客户端的Client Id与Secret:

curl -i 'https://ehr.yihu.com/api/v1/users/whatever?client_id=xxxx&client_secret=yyyy'
HTTP/1.1 200 OK
Date: Mon, 01 Jul 2013 17:27:06 GMT
Status: 200 OK
X-RateLimit-Limit: 5000
X-RateLimit-Remaining: 4966
X-RateLimit-Reset: 1372700873

这个方法应该仅用于服务端-服务端的请求,请务必不要泄漏App Secret。

使用User Agent

所有的API都需要在请求头中包含User-Agent。没有User-Agent的请求将被拒绝。请在此字段中使用您的用户名或Client名称。 这样做可以在出现问题的时候方便联系你们。

例如:

User-Agent: client 1FtXTrSL8D

User-Agent: user developer

如果提供的User-Agent是无效值,你将收到以下错误信息:

curl -iH 'User-Agent: ' https://ehr.yihu.com/api/v1/meta

HTTP/1.0 403 Forbidden
Connection: close
Content-Type: text/html

Request forbidden by administrative rules.
Please make sure your request has a User-Agent header.
Check https://ehr.yihu.com/api for other possible causes.

条件式请求

未实现。

跨域资源共享(Cross-Origin Resource Sharing,CORS)

未实现。

JSON-P回调

你可以在GET请求参数中添加?callback参数以将请求结果包装成JSON函数,这种方式经常用在向第三方Web页面中嵌入健康档案平台的内容(跨域)。 平台将会把回调结果中的HTTP头作为API返回值的一部分:

curl https://ehr.yihu.com/api/v1?callback=foo

foo({
  "meta": {
    "status": 200,
    "X-RateLimit-Limit": "5000",
    "X-RateLimit-Remaining": "4966",
    "X-RateLimit-Reset": "1372700873",
    "Link": [ // pagination headers and other links
      ["https://ehr.yihu.com/api/v1?page=2", {"rel": "next"}]
    ]
  },
  "data": {
    // 回调的实际响应值
  }
})

你可以编写JavaScript代码处理这种回调结果。以下是一个简单的示例:

<html>
<head>
<script type="text/javascript">
function foo(response) {
  var meta = response.meta;
  var data = response.data;
  console.log(meta);
  console.log(data);
}

var script = document.createElement('script');
script.src = 'https://ehr.yihu.com?callback=foo';

document.getElementsByTagName('head')[0].appendChild(script);
</script>
</head>

<body>
  <p>Open up your browser's console.</p>
</body>
</html>

所有的响应头内容均与原HTTP响应头一致,除了LInk字段。此处的Link字段已经提前为你解析好,并包装为*[url, options]*数组。

例如,正常的HTTP响应头中Link结构如下:

Link: <url1>; rel="next", <url2>; rel="foo"; bar="baz"

JSON-P中的Link结构如下:

{
  "Link": [
    [
      "url1",
      {
        "rel": "next"
      }
    ],
    [
      "url2",
      {
        "rel": "foo",
        "bar": "baz"
      }
    ]
  ]
}

时区

部分API需要提供时间戳参数,部分API会返回含时区的时间戳。我们根据以下规则(优先级从大到小)决定API的时区信息:

显示使用ISO 8601格式的时间戳,即包含时区信息的时间戳

对于允许显示指定时间戳的API,我们将使用指定的时区。这种时间戳看起像这样:*2016-04-26T08:25:07.504+0800*。

在HTTP请求头中使用Time-Zone参数

在HTTP请求头中附加Time-Zone参数,其值使用时区数据库中的名称

curl -H "Time-Zone: Asia/Shanghai" -X POST https://ehr.yihu.com/linguist/contents/new_file.md

使用上一次的已知时区信息

如果找不到Time-Zone参数,并且你生成了一个授权请求,我们将使用上次授权用户所在的时区。每次你浏览健康档案的时候都会更新这个值。

UTC

如果上述几种情况中均不包含任何时区信息,我们将使用UTC。