RESTful API 规范 v1.0

javalhf 8年前
   <h2>RESTful API 规范 v1.0</h2>    <p>[toc]</p>    <h2>URI</h2>    <h3>URI规范</h3>    <ul>     <li>不要用大写</li>     <li>单词间使用下划线'_'</li>     <li>不使用动词,资源要使用名词复数形式,如:user、rooms、tickets</li>     <li>层级 >= 三层,则使用'?'带参数 <p>users/1/address/2/citys (bad) /citys?users=1&address=2; (good)</p> </li>    </ul>    <h2>Request</h2>    <h3>Method</h3>    <ul>     <li>GET:查询资源</li>     <li>POST:创建资源</li>     <li> <p>PUT/PATCH</p>      <ul>       <li>PUT:全量更新资源(提供改变后的完整资源)</li>       <li>PATCH:局部更新资源(仅提供改变的属性)</li>      </ul> </li>     <li> <p>DELETE:删除资源</p> </li>    </ul>    <h3>安全性与幂等性</h3>    <ul>     <li>安全性:任意多次对同一资源操作,都不会导致资源的状态变化</li>     <li>幂等性:任意次对同一资源操作,对资源的改变是一样的 |Method|安全性|幂等性| |------|:---:|:---:| |GET|√|√| |POST|×|×| |PUT|×|√| |PATCH|×|√| |DELETE|×|√|</li>    </ul>    <h3>兼容</h3>    <p>很多客户只支持GET/POST请求,一般有两种方式模拟PUT等请求</p>    <ul>     <li>添加_method参数 <pre>  <code class="language-bash">  /users/1?_method=put&name=111  </code></pre> </li>     <li>添加X-HTTP-Method-Override请求头 (我们使用这种方式) <pre>  <code class="language-json">  X-HTTP-Method-Override: PUT  </code></pre> </li>    </ul>    <h3>参数</h3>    <p>Method</p>    <p>GET</p>    <ul>     <li>非id的参数使用'?'方式传输 <pre>  <code class="language-bash">  /users/1?state=closed  </code></pre> <p>POST、PATCH、PUT、DELETE</p> </li>     <li>非id的参数使用body传输,并且应该encode</li>    </ul>    <p>过滤</p>    <p>?type=1&state=closed</p>    <p>排序</p>    <ul>     <li>+ 升序,如?sort=+create_time,根据id升序</li>     <li>- 降序,如?sort=-create_time,根据id降序</li>    </ul>    <p>分页</p>    <p>?limit=10&offset=10</p>    <ul>     <li>limit:返回记录数量</li>     <li>offset:返回记录的开始位置</li>    </ul>    <p>单参数多字段</p>    <p>使用 , 分隔,如</p>    <pre>  <code class="language-bash">    /users/1?fields=name,age,city  </code></pre>    <h2>版本控制</h2>    <p>三种方案:</p>    <ol>     <li>在uri中加入版本: /v1/room/1</li>     <li>Accept Header:Accept: v1</li>     <li>自定义 Header:X-Imweb-Media-Type: imweb.v1 (我们使用此方案)</li>    </ol>    <p>自定义Media-Type参考资料 <a href="/misc/goto?guid=4959670091505368416" rel="nofollow,noindex">github</a></p>    <h2>状态码</h2>    <h3>成功</h3>    <table>     <thead>      <tr>       <th>Code</th>       <th>Method</th>       <th>Describe</th>      </tr>     </thead>     <tbody>      <tr>       <td>200</td>       <td>ALL</td>       <td>请求成功并返回实体资源</td>      </tr>      <tr>       <td>201</td>       <td>POST</td>       <td>创建资源成功</td>      </tr>     </tbody>    </table>    <h3>客户端错误</h3>    <table>     <thead>      <tr>       <th>Code</th>       <th>Method</th>       <th>Describe</th>      </tr>     </thead>     <tbody>      <tr>       <td>400</td>       <td>ALL</td>       <td>一般是参数错误</td>      </tr>      <tr>       <td>401</td>       <td>ALL</td>       <td>一般用户验证失败(用户名、密码错误等)</td>      </tr>      <tr>       <td>403</td>       <td>ALL</td>       <td>一般用户权限校验失败</td>      </tr>      <tr>       <td>404</td>       <td>ALL</td>       <td>资源不存在(github在权限校验失败的情况下也会返回404,为了防止一些私有接口泄露出去)</td>      </tr>      <tr>       <td>422</td>       <td>ALL</td>       <td>一般是必要字段缺失或参数格式化问题</td>      </tr>     </tbody>    </table>    <h3>服务器错误</h3>    <table>     <thead>      <tr>       <th>CODE</th>       <th>METHOD</th>       <th>DESCRIBE</th>      </tr>     </thead>     <tbody>      <tr>       <td>500</td>       <td>ALL</td>       <td>服务器未知错误</td>      </tr>     </tbody>    </table>    <p>以上是常见的状态码,完整的状态码列表在这 <a href="/misc/goto?guid=4958974606586712906" rel="nofollow,noindex">状态码</a></p>    <h2>HATEOAS</h2>    <p>在介绍HATEOAS之前,先介绍一下REST的成熟度模型</p>    <p>在介绍 HATEOAS 之前,先介绍一下 Richardson 提出的 REST 成熟度模型。该模型把 REST 服务按照成熟度划分成 4 个层次:</p>    <ul>     <li>第一个层次(Level 0)的 Web 服务只是使用 HTTP 作为传输方式,实际上只是远程方法调用(RPC)的一种具体形式。</li>     <li>第二个层次(Level 1)的 Web 服务引入了资源的概念。每个资源有对应的标识符和表达。</li>     <li>第三个层次(Level 2)的 Web 服务使用不同的 HTTP 方法来进行不同的操作,并且使用 HTTP 状态码来表示不同的结果。如 HTTP GET 方法来获取资源,HTTP DELETE 方法来删除资源。</li>     <li>第四个层次(Level 3)的 Web 服务使用 HATEOAS。在资源的表达中包含了链接信息。客户端可以根据链接来发现可以执行的动作。</li>    </ul>    <h3>简述</h3>    <p>HATEOAS(Hypermedia as the engine of application state)是 REST 架构风格中最复杂的约束,也是构建成熟 REST 服务的核心。它的重要性在于客户端和服务器之间的解耦。</p>    <h3>例子</h3>    <p>分页</p>    <p>request请求,查询user,每页显示10条,从第10条开始显示(第二页)</p>    <pre>  <code class="language-bash">    /users?limit=10&offset=10  </code></pre>    <p>response</p>    <pre>  <code class="language-json">    {          data: {              xxxx          },          meta: {              _link: [                  {rel: 'self', href: 'xxx/users?limit=10&offset=10'},                  {rel: 'first', href: 'xxx/users?limit=10&offset=0', title: 'first page'},                  {rel: 'last', href: 'xxx/users?limit=10&offset=50', title: 'last page'},                  {rel: 'prev', href: 'xxx/users?limit=10&offset=0', title: 'prev page'},                  {rel: 'next', href: 'xxx/users?limit=10&offset=20', title: 'next page'}              ]          }          }  </code></pre>    <p>_link 返回了5个资源</p>    <ul>     <li>rel: 'self',资源本身</li>     <li>rel: 'first',第一页资源</li>     <li>rel: 'last',最后一页资源</li>     <li>rel: 'prev',上一页资源</li>     <li>rel: 'next',下一页资源</li>    </ul>    <h3>权限相关</h3>    <p>如用户查询一个订单</p>    <p>普通用户</p>    <p>request</p>    <pre>  <code class="language-bash">    /orders/1  </code></pre>    <p>response</p>    <pre>  <code class="language-json">    {          data: {              xxx          },          meta: {              _link: [                  {rel: 'self', href: 'xxx/orders/1'},                  {rel: 'related', href: 'xxx/orders/1/payment', title: 'pay the order'}              ]          }      }  </code></pre>    <p>_link 返回两个资源</p>    <ul>     <li>rel: 'self',资源本身</li>     <li>rel: 'related',与当前资源相关的资源, /order/1/payment 用户可以使用此资源进行支付</li>    </ul>    <p>权限用户</p>    <p>request</p>    <pre>  <code class="language-json">    /orders/1  </code></pre>    <p>response</p>    <pre>  <code class="language-json">    {          data: {              xxx          },          meta: {              _link: [                  {rel: 'self', href: 'xxx/orders/1'},                  {rel: 'edit', href: 'xxx/orders/1', title: 'edit the order'},                  {rel: 'delete', href: 'xxx/orders/1', title: 'delete the order'}              ]          }      }  </code></pre>    <p>此用户拥有修改与删除订单的权限,因此返回了3个资源</p>    <ul>     <li>rel: 'self',资源本身</li>     <li>rel: 'edit',此用户可修改该资源</li>     <li>rel: 'delete',此用户可删除该资源</li>    </ul>    <h3>常用rel</h3>    <table>     <thead>      <tr>       <th>rel</th>       <th>describe</th>      </tr>     </thead>     <tbody>      <tr>       <td>self</td>       <td>资源本身,每个资源表述都一个包含此关系</td>      </tr>      <tr>       <td>edit</td>       <td>指向一个可以编辑当前资源的链接</td>      </tr>      <tr>       <td>delete</td>       <td>指向一个可以删除当前资源的链接</td>      </tr>      <tr>       <td>item</td>       <td>如果当前资源表示的是一个集合,则用来指向该集合中的单个资源</td>      </tr>      <tr>       <td>collection</td>       <td>如果当前资源包含在某个集合中,则用来指向包含该资源的集合</td>      </tr>      <tr>       <td>related</td>       <td>指向一个与当前资源相关的资源</td>      </tr>      <tr>       <td>first、last、prev、next</td>       <td>分别用来指向第一个、最后一个、上一个和下一个资源</td>      </tr>     </tbody>    </table>    <h3>HATEOAS总结</h3>    <p>由以上例子可以看出 _link 就是以Hyperlink表述资源与资源之间的关系,这种方式使客户端与服务端能很好的分离开来,只要接口的定义不变,客户端与服务端就可以独立的开发和演变。</p>    <p>来自: <a href="/misc/goto?guid=4959670091625964100" rel="nofollow">http://imweb.io/topic/5707561f06f2400432c139a5</a></p>