RESTful API 设计指南

RESTful API 设计指南 - 阮一峰的网络日志
        <div id="content">
            <div id="content-inner">


                <div id="alpha">
                    <div id="alpha-inner">


                        <div id="entry-1805" class="entry-asset asset hentry">
                            <div class="asset-header">
                                <div class="entry-categories">
                                    <p>分类<span class="delimiter">:</span></p>
                                    <ul>
                                        <li><a href="http://www.ruanyifeng.com/blog/developer/" rel="tag">开发者手册</a></li>
                                    </ul>
                                </div>




RESTful API 设计指南



                                        <p class="vcard author">作者: <a class="fn url" href="http://www.ruanyifeng.com">阮一峰</a></p>
                                <p>日期: <a href="http://www.ruanyifeng.com/blog/2014/05/"><abbr class="published" title="2014-05-22T20:54:31+08:00">2014年5月22日</abbr></a></p>


                                </div>

                            <div class="asset-content entry-content" id="main-content">

                                <!-- div class="asset-body" -->
                                    <p>网络应用程序,分为前端和后端两个部分。当前的发展趋势,就是前端设备层出不穷(手机、平板、桌面电脑、其他专用设备......)。</p>

                                <!-- /div -->


                                <!-- div id="more" class="asset-more" -->
                                    <p>因此,必须有一种统一的机制,方便不同的前端设备与后端进行通信。这导致API构架的流行,甚至出现<a href="http://www.google.com.hk/search?q=API+first">"API First"</a>的设计思想。<a href="http://en.wikipedia.org/wiki/Representational_state_transfer">RESTful API</a>是目前比较成熟的一套互联网应用程序的API设计理论。我以前写过一篇<a href="http://www.ruanyifeng.com/blog/2011/09/restful.html">《理解RESTful架构》</a>,探讨如何理解这个概念。</p>

今天,我将介绍RESTful API的设计细节,探讨如何设计一套合理、好用的API。我的主要参考了两篇文章(12)。

RESTful API

一、协议

API与用户的通信协议,总是使用HTTPs协议

二、域名

应该尽量将API部署在专用域名之下。


https://api.example.com

如果确定API很简单,不会有进一步扩展,可以考虑放在主域名下。


https://example.org/api/

三、版本(Versioning)

应该将API的版本号放入URL。


https://api.example.com/v1/

另一种做法是,将版本号放在HTTP头信息中,但不如放入URL方便和直观。Github采用这种做法。

四、路径(Endpoint)

路径又称”终点”(endpoint),表示API的具体网址。

在RESTful架构中,每个网址代表一种资源(resource),所以网址中不能有动词,只能有名词,而且所用的名词往往与数据库的表格名对应。一般来说,数据库中的表都是同种记录的”集合”(collection),所以API中的名词也应该使用复数。

举例来说,有一个API提供动物园(zoo)的信息,还包括各种动物和雇员的信息,则它的路径应该设计成下面这样。

  • https://api.example.com/v1/zoos
  • https://api.example.com/v1/animals
  • https://api.example.com/v1/employees

五、HTTP动词

对于资源的具体操作类型,由HTTP动词表示。

常用的HTTP动词有下面五个(括号里是对应的SQL命令)。

  • GET(SELECT):从服务器取出资源(一项或多项)。
  • POST(CREATE):在服务器新建一个资源。
  • PUT(UPDATE):在服务器更新资源(客户端提供改变后的完整资源)。
  • PATCH(UPDATE):在服务器更新资源(客户端提供改变的属性)。
  • DELETE(DELETE):从服务器删除资源。

还有两个不常用的HTTP动词。

  • HEAD:获取资源的元数据。
  • OPTIONS:获取信息,关于资源的哪些属性是客户端可以改变的。

下面是一些例子。

  • GET /zoos:列出所有动物园
  • POST /zoos:新建一个动物园
  • GET /zoos/ID:获取某个指定动物园的信息
  • PUT /zoos/ID:更新某个指定动物园的信息(提供该动物园的全部信息)
  • PATCH /zoos/ID:更新某个指定动物园的信息(提供该动物园的部分信息)
  • DELETE /zoos/ID:删除某个动物园
  • GET /zoos/ID/animals:列出某个指定动物园的所有动物
  • DELETE /zoos/ID/animals/ID:删除某个指定动物园的指定动物

六、过滤信息(Filtering)

如果记录数量很多,服务器不可能都将它们返回给用户。API应该提供参数,过滤返回结果。

下面是一些常见的参数。

  • ?limit=10:指定返回记录的数量
  • ?offset=10:指定返回记录的开始位置。
  • ?page=2&per_page=100:指定第几页,以及每页的记录数。
  • ?sortby=name&order=asc:指定返回结果按照哪个属性排序,以及排序顺序。
  • ?animal_type_id=1:指定筛选条件

参数的设计允许存在冗余,即允许API路径和URL参数偶尔有重复。比如,GET /zoo/ID/animals 与 GET /animals?zoo_id=ID 的含义是相同的。

七、状态码(Status Codes)

服务器向用户返回的状态码和提示信息,常见的有以下一些(方括号中是该状态码对应的HTTP动词)。

  • 200 OK - [GET]:服务器成功返回用户请求的数据,该操作是幂等的(Idempotent)。
  • 201 CREATED - [POST/PUT/PATCH]:用户新建或修改数据成功。
  • 202 Accepted - [*]:表示一个请求已经进入后台排队(异步任务)
  • 204 NO CONTENT - [DELETE]:用户删除数据成功。
  • 400 INVALID REQUEST - [POST/PUT/PATCH]:用户发出的请求有错误,服务器没有进行新建或修改数据的操作,该操作是幂等的。
  • 401 Unauthorized - [*]:表示用户没有权限(令牌、用户名、密码错误)。
  • 403 Forbidden - [*] 表示用户得到授权(与401错误相对),但是访问是被禁止的。
  • 404 NOT FOUND - [*]:用户发出的请求针对的是不存在的记录,服务器没有进行操作,该操作是幂等的。
  • 406 Not Acceptable - [GET]:用户请求的格式不可得(比如用户请求JSON格式,但是只有XML格式)。
  • 410 Gone -[GET]:用户请求的资源被永久删除,且不会再得到的。
  • 422 Unprocesable entity - [POST/PUT/PATCH] 当创建一个对象时,发生一个验证错误。
  • 500 INTERNAL SERVER ERROR - [*]:服务器发生错误,用户将无法判断发出的请求是否成功。

状态码的完全列表参见这里

八、错误处理(Error handling)

如果状态码是4xx,就应该向用户返回出错信息。一般来说,返回的信息中将error作为键名,出错信息作为键值即可。


{
    error: "Invalid API key"
}

九、返回结果

针对不同操作,服务器向用户返回的结果应该符合以下规范。

  • GET /collection:返回资源对象的列表(数组)
  • GET /collection/resource:返回单个资源对象
  • POST /collection:返回新生成的资源对象
  • PUT /collection/resource:返回完整的资源对象
  • PATCH /collection/resource:返回完整的资源对象
  • DELETE /collection/resource:返回一个空文档

十、Hypermedia API

RESTful API最好做到Hypermedia,即返回结果中提供链接,连向其他API方法,使得用户不查文档,也知道下一步应该做什么。

比如,当用户向api.example.com的根目录发出请求,会得到这样一个文档。


{"link": {
  "rel":   "collection https://www.example.com/zoos",
  "href":  "https://api.example.com/zoos",
  "title": "List of zoos",
  "type":  "application/vnd.yourformat+json"
}}

上面代码表示,文档中有一个link属性,用户读取这个属性就知道下一步该调用什么API了。rel表示这个API与当前网址的关系(collection关系,并给出该collection的网址),href表示API的路径,title表示API的标题,type表示返回类型。

Hypermedia API的设计被称为HATEOAS。Github的API就是这种设计,访问api.github.com会得到一个所有可用API的网址列表。


{
  "current_user_url": "https://api.github.com/user",
  "authorizations_url": "https://api.github.com/authorizations",
  // ...
}

从上面可以看到,如果想获取当前用户的信息,应该去访问api.github.com/user,然后就得到了下面结果。


{
  "message": "Requires authentication",
  "documentation_url": "https://developer.github.com/v3"
}

上面代码表示,服务器给出了提示信息,以及文档的网址。

十一、其他

(1)API的身份认证应该使用OAuth 2.0框架。

(2)服务器返回的数据格式,应该尽量使用JSON,避免使用XML。

(完)

留言(58条)

        </div>
    </div>
    <div class="comment-content" id="comment-quote-324692">
        <p>最后一条: "服务器返回的数据格式,应该使用JSON,不应使用XML" 存疑,返回某种自描述类的文本类数据即可,貌似没有规定不应使用XML;这与SOAP的rpc风格没有关联,两码事。</p>
    </div>


    <div id="comment-324700" class="comment">
<div  class="inner">
    <div class="comment-header">
        <div class="asset-meta">

        </div>
    </div>
    <div class="comment-content" id="comment-quote-324700">
        <p>如果批量获取指定ID的一堆Zoo,按理应该用GET,但是由于ID列表可能会很长,这样会导致url过长;如果放在HTTP Content里面,但通常只有POST这些才支持,这样又不符合HTTP动词语义。请问如何破解?</p>
    </div>


    <div id="comment-324705" class="comment">
<div  class="inner">
    <div class="comment-header">
        <div class="asset-meta">

        </div>
    </div>
    <div class="comment-content" id="comment-quote-324705">
        <p>API还必须易于测试,能让用户在页面测试API,看到发送和接收的结果。</p>
    </div>


    <div id="comment-324707" class="comment">
<div  class="inner">
    <div class="comment-header">
        <div class="asset-meta">

        </div>
    </div>
    <div class="comment-content" id="comment-quote-324707">
        <p>@fallingwine:</p>

谢谢指出,改了一下。某些场合,确实不得不使用XML。



    <div id="comment-324708" class="comment">
<div  class="inner">
    <div class="comment-header">
        <div class="asset-meta">

        </div>
    </div>
    <div class="comment-content" id="comment-quote-324708">
        <blockquote>
引用allenwest的发言:

如果批量获取指定ID的一堆Zoo,按理应该用GET,但是由于ID列表可能会很长,这样会导致url过长;如果放在HTTP Content里面,但通常只有POST这些才支持,这样又不符合HTTP动词语义。请问如何破解?

url过长有什么问题?
不会是担心浏览器地址栏里的url长度吧?



    <div id="comment-324717" class="comment">
<div  class="inner">
    <div class="comment-header">
        <div class="asset-meta">

        </div>
    </div>
    <div class="comment-content" id="comment-quote-324717">
        <p>這個整理得很好,讚呀。</p>


我想問
如果 發生錯誤時,
只是拋出Status Code 真的好嗎?
如果想回應 錯誤的原因, 例如: 此日期不正確 ,之類



    <div id="comment-324721" class="comment">
<div  class="inner">
    <div class="comment-header">
        <div class="asset-meta">

        </div>
    </div>
    <div class="comment-content" id="comment-quote-324721">
        <p>不好意思,忘記了可以更改Response Body.<br />

{“status”:”XXX”,”message”:”XXX”}




    <div id="comment-324768" class="comment">
<div  class="inner">
    <div class="comment-header">
        <div class="asset-meta">

        </div>
    </div>
    <div class="comment-content" id="comment-quote-324768">
        <p>有关搜索的 API 设计没有提到,是 search?q=?吗?</p>
    </div>


    <div id="comment-324777" class="comment">
<div  class="inner">
    <div class="comment-header">
        <div class="asset-meta">

        </div>
    </div>
    <div class="comment-content" id="comment-quote-324777">
        <p>“理解RESTful架构”这篇文章提到“另一个设计误区,就是在URI中加入版本号”,不是跟上面提到的第三点矛盾了吗?</p>
    </div>


    <div id="comment-324778" class="comment">
<div  class="inner">
    <div class="comment-header">
        <div class="asset-meta">

        </div>
    </div>
    <div class="comment-content" id="comment-quote-324778">
        <p>?sortby=name&order=asc<br />

上面这种方式比较直观,如果用urlrewriter方式也不错,/sortby/name/order/asc,这样似乎又不符合一个url一个资源的规范




    <div id="comment-324800" class="comment">
<div  class="inner">
    <div class="comment-header">
        <div class="asset-meta">

        </div>
    </div>
    <div class="comment-content" id="comment-quote-324800">
        <p>POST 一个对象时,对象的内容是怎么放到POST参数里面呢?</p>

是把对象转成一个json,放在一个参数里面。
还是把对象的每个字段当初一个参数?



    <div id="comment-324806" class="comment">
<div  class="inner">
    <div class="comment-header">
        <div class="asset-meta">

        </div>
    </div>
    <div class="comment-content" id="comment-quote-324806">
        <blockquote>
引用Yhzhtk的发言:

POST 一个对象时,对象的内容是怎么放到POST参数里面呢?

是把对象转成一个json,放在一个参数里面。
还是把对象的每个字段当初一个参数?

转成json放到request body里面。



    <div id="comment-324812" class="comment">
<div  class="inner">
    <div class="comment-header">
        <div class="asset-meta">

        </div>
    </div>
    <div class="comment-content" id="comment-quote-324812">
        <p>目前公司也从WCF 向API 方面转移,定义与LZ雷同...</p>
    </div>


    <div id="comment-324818" class="comment">
<div  class="inner">
    <div class="comment-header">
        <div class="asset-meta">

        </div>
    </div>
    <div class="comment-content" id="comment-quote-324818">
        <p>我一般是给 web app使用,但是浏览器对 PUT 这类支持不好,这个操作放到 URL 里合适吗?怎么处理比较好呢?<br />




    <div id="comment-324819" class="comment">
<div  class="inner">
    <div class="comment-header">
        <div class="asset-meta">

        </div>
    </div>
    <div class="comment-content" id="comment-quote-324819">
        <blockquote>
引用廖雪峰的发言:

API还必须易于测试,能让用户在页面测试API,看到发送和接收的结果。

开发人员一般都是chrome 或 firefox加插件调试

    <div id="comment-324875" class="comment">
<div  class="inner">
    <div class="comment-header">
        <div class="asset-meta">

        </div>
    </div>
    <div class="comment-content" id="comment-quote-324875">
        <p>现在移动开发都是用api去调用数据,web也可以一起共用,节约开发成本。</p>
    </div>


    <div id="comment-324879" class="comment">
<div  class="inner">
    <div class="comment-header">
        <div class="asset-meta">

        </div>
    </div>
    <div class="comment-content" id="comment-quote-324879">
        <blockquote>
引用Fengz的发言:

我一般是给 web app使用,但是浏览器对 PUT 这类支持不好,这个操作放到 URL 里合适吗?怎么处理比较好呢?


对PUT这类的操作也一直是和你有着同样的困惑。



    <div id="comment-324913" class="comment">
<div  class="inner">
    <div class="comment-header">
        <div class="asset-meta">

        </div>
    </div>
    <div class="comment-content" id="comment-quote-324913">
        <p>其实这只是一个推荐规范, 为了解决一些浏览器的HTTP方法的兼容问题, 还有GET URL超长的问题, 感觉可以变通为, 把HTTP方法名称也放入URI中, 如:</p>

/GET/zoos
/PUT/zoos/ID
/DELETE/zoos/ID

过长的参数可以自动使用POST方法来发送



    <div id="comment-324916" class="comment">
<div  class="inner">
    <div class="comment-header">
        <div class="asset-meta">

        </div>
    </div>
    <div class="comment-content" id="comment-quote-324916">
        <p>用POST,《RESTful Web Services Cookbook》上有说过。</p>
    </div>


    <div id="comment-324918" class="comment">
<div  class="inner">
    <div class="comment-header">
        <div class="asset-meta">

        </div>
    </div>
    <div class="comment-content" id="comment-quote-324918">
        <p>对于PUT和POST的用法,这篇文章和《RESTful Web Services Cookbook》上推荐的做法不一致。<br />

Cookbook上的做法是如果用户可以决定新创建资源的URL,那么就用PUT,否则用POST。

以及,如果不仅仅是单纯的创建一个资源,类似复杂的工厂方法(会连带创建相关的其他复杂类属性),也用POST。




    <div id="comment-324919" class="comment">
<div  class="inner">
    <div class="comment-header">
        <div class="asset-meta">

        </div>
    </div>
    <div class="comment-content" id="comment-quote-324919">
        <blockquote>
引用j.w的发言:


开发人员一般都是chrome 或 firefox加插件调试

推荐Fiddler



    <div id="comment-325064" class="comment">
<div  class="inner">
    <div class="comment-header">
        <div class="asset-meta">

        </div>
    </div>
    <div class="comment-content" id="comment-quote-325064">
        <p><a href="https://speakerdeck.com/akuzemchak/simple-api-development-with-laravel" rel="nofollow">https://speakerdeck.com/akuzemchak/simple-api-development-with-laravel</a><br />

这里有一个讲 rest api 的ppt,时代不错,有兴趣的同学可以看一下




    <div id="comment-325086" class="comment">
<div  class="inner">
    <div class="comment-header">
        <div class="asset-meta">

        </div>
    </div>
    <div class="comment-content" id="comment-quote-325086">
        <p>restful 略蛋疼,跨域的更新操作怎么破?</p>
    </div>


    <div id="comment-325155" class="comment">
<div  class="inner">
    <div class="comment-header">
        <div class="asset-meta">

        </div>
    </div>
    <div class="comment-content" id="comment-quote-325155">
        <blockquote>
引用andrewyang的发言:

“理解RESTful架构”这篇文章提到“另一个设计误区,就是在URI中加入版本号”,不是跟上面提到的第三点矛盾了吗?

同问。。。



    <div id="comment-325256" class="comment">
<div  class="inner">
    <div class="comment-header">
        <div class="asset-meta">

        </div>
    </div>
    <div class="comment-content" id="comment-quote-325256">
        <blockquote>
引用fromoon的发言:

同问。。。

“理解RESTful架构”的评论里也有关于“版本”讨论,这也许和个人观点有关,我是觉得是可以的。有人在http://stackoverflow.com/questions/972226/how-to-version-rest-uris讨论哪种加版本号的方式更好。

    <div id="comment-325271" class="comment">
<div  class="inner">
    <div class="comment-header">
        <div class="asset-meta">

        </div>
    </div>
    <div class="comment-content" id="comment-quote-325271">
        <p>你好 谢谢您的文章 我在我们系统中也使用这种架构 想问一下您 如果返回的数据量比较大的结构化数据 使用哪种方式速度上有优势</p>
    </div>


    <div id="comment-325346" class="comment">
<div  class="inner">
    <div class="comment-header">
        <div class="asset-meta">

        </div>
    </div>
    <div class="comment-content" id="comment-quote-325346">
        <blockquote>
引用allenwest的发言:

如果批量獲取指定ID的一堆Zoo,按理應該用GET,但是由於ID列表可能會很長,這樣會導致url過長;如果放在HTTP Content裡面,但通常只有POST這些才支持,這樣又不符合HTTP動詞語義。請問如何破解?

若你使用的是 PHP,可以透過 http://www.hashids.org/php/ 來解決



    <div id="comment-325753" class="comment">
<div  class="inner">
    <div class="comment-header">
        <div class="asset-meta">

        </div>
    </div>
    <div class="comment-content" id="comment-quote-325753">
        <blockquote>
引用allenwest的发言:

如果批量获取指定ID的一堆Zoo,按理应该用GET,但是由于ID列表可能会很长,这样会导致url过长;如果放在HTTP Content里面,但通常只有POST这些才支持,这样又不符合HTTP动词语义。请问如何破解?

获取一堆的Zoo,你会经常发一堆的ID去查找吗?
我觉得获取列表往往只是发几个过滤条件,或者通过不同的endpoint,就足够了



    <div id="comment-326554" class="comment">
<div  class="inner">
    <div class="comment-header">
        <div class="asset-meta">

        </div>
    </div>
    <div class="comment-content" id="comment-quote-326554">
        <p>版本应该在HTTP头中把.我一直认为Rest思想,其实就是HTTP协议的设计初衷.既然HTTP协议本身设计了版本,就应该走协议本身.而不是在协议上扩展.</p>
    </div>


    <div id="comment-326797" class="comment">
<div  class="inner">
    <div class="comment-header">
        <div class="asset-meta">

        </div>
    </div>
    <div class="comment-content" id="comment-quote-326797">
        <p>你还记得你2007写的绕过gfw吗?</p>

请教你:使用p2p技术能否把gfw打成一个千疮百孔的筛子,让其变成一个没有用的马其诺防线?

背景:
越来越受不了被人禁言和国内媒介的噤若寒蝉,特别是最近的事件,假期结束所有新闻平静沉默的吓人。

想法:
12年刚知道 BitCoin时被它的去中心技术所叹服。于是在想如果媒介或沟通也能去中心化,或者每个人都是一个中心,那么“擒贼先擒王”的办法再也无法控制每个单个个体。

方案:
A:去中心沟通工具
(1):做一个沟通工具,之间只能通过p2p链接。
(2):数据加密、源码开放。以BitCoin的方式认证源码。任何人都可以使用任何语言编写工具,但只能被认证通过的才可以进行沟通。防止间谍工具(代码)。
(3):p2p自我发现(嗅探)–每个终端开启后,可以自动嗅探到最近的其它终端,最终达到全网互联。
另:也可以使用初始其它p2p链接。即:一个p2p最终可以初始保存多个(如:1000+)其它p2p地址(如:至少有一个最近的,一个外省的,一个国内的,一个国外的)。相当于多服务器(多中心)。
B:去中心“服务器”
媒介(如:网易)只要服务器在国内或者公司在国内就不可能不受制约(断域名、断Ip、断人)。如果有一个服务器是存在全网(并不位于某一具体机器),那么如果在此服务器上部署的服务是不是就不受任何人控制。
(1):根据现在流行的大数据原理(所有结点构成整个分布式服务器,每个结点都可以充当中央结点)组建全网结点。
(2):每个具体用户贡献一部分cpu和内存、硬盘充当结点。
C:去中心“搜索引擎”
(1):同上,也是去中心“服务器”实现的基础。

    </div>


    <div id="comment-327202" class="comment">
<div  class="inner">
    <div class="comment-header">
        <div class="asset-meta">

        </div>
    </div>
    <div class="comment-content" id="comment-quote-327202">
        <blockquote>
引用allenwest的发言:

如果批量获取指定ID的一堆Zoo,按理应该用GET,但是由于ID列表可能会很长,这样会导致url过长;如果放在HTTP Content里面,但通常只有POST这些才支持,这样又不符合HTTP动词语义。请问如何破解?

url的长度在http协议中中没有限制,只是在浏览器中有限制



    <div id="comment-327374" class="comment">
<div  class="inner">
    <div class="comment-header">
        <div class="asset-meta">

        </div>
    </div>
    <div class="comment-content" id="comment-quote-327374">
        <blockquote>
引用AriesDevil的发言:

有关搜索的 API 设计没有提到,是 search?q=?吗?

同有点疑惑,如果按博主说的api中不能出现动词,那就不能用search了?



    <div id="comment-329049" class="comment">
<div  class="inner">
    <div class="comment-header">
        <div class="asset-meta">

        </div>
    </div>
    <div class="comment-content" id="comment-quote-329049">
        <p>《理解RESTful架构》中有一段:另一个设计误区,就是在URI中加入版本号。而本文第3条又说:应该将API的版本号放入URL。两者相互冲突了。我觉得版本号不让人URL更合适一些。</p>
    </div>


    <div id="comment-329467" class="comment">
<div  class="inner">
    <div class="comment-header">
        <div class="asset-meta">

        </div>
    </div>
    <div class="comment-content" id="comment-quote-329467">
        <p>.Net的WebAPI 还不能http跨域调用. AJAX使用JSONP访问是只有GET请求. 怎么扩展?</p>
    </div>


    <div id="comment-331280" class="comment">
<div  class="inner">
    <div class="comment-header">
        <div class="asset-meta">

        </div>
    </div>
    <div class="comment-content" id="comment-quote-331280">
        <p>推荐RESTful API调试插件工具<br />

Chrome:Postman/Advanced Rest Client

Firefox:RESTClient




    <div id="comment-333399" class="comment">
<div  class="inner">
    <div class="comment-header">
        <div class="asset-meta">

        </div>
    </div>
    <div class="comment-content" id="comment-quote-333399">
        <p>感觉还是不要在URL里加入版本号的比较好。个人浅见。</p>
    </div>


    <div id="comment-337832" class="comment">
<div  class="inner">
    <div class="comment-header">
        <div class="asset-meta">

        </div>
    </div>
    <div class="comment-content" id="comment-quote-337832">
        <blockquote>
引用WKPlus的发言:

同有点疑惑,如果按博主说的api中不能出现动词,那就不能用search了?

在参考了github的AIP后,对于搜索可以这样,你要搜索什么,如users, animals等,你可以在对应的后面加上一个搜索参数q={query},最后的组合是:
/users?q={query}这样的资源路径。



    <div id="comment-339363" class="comment">
<div  class="inner">
    <div class="comment-header">
        <div class="asset-meta">

        </div>
    </div>
    <div class="comment-content" id="comment-quote-339363">
        <p>uri 中还是带有版本号好。<br />




    <div id="comment-340964" class="comment">
<div  class="inner">
    <div class="comment-header">
        <div class="asset-meta">

        </div>
    </div>
    <div class="comment-content" id="comment-quote-340964">
        <blockquote>
引用j.w的发言:


开发人员一般都是chrome 或 firefox加插件调试

配合curl很顺畅的。

    </div>


    <div id="comment-342194" class="comment">
<div  class="inner">
    <div class="comment-header">
        <div class="asset-meta">

        </div>
    </div>
    <div class="comment-content" id="comment-quote-342194">
        <p>应该将API的版本号放入URL。<br />

正中误区

http://www.ruanyifeng.com/blog/2011/09/restful.html

另一个设计误区,就是在URI中加入版本号:

新的设计趋势?



    <div id="comment-342329" class="comment">
<div  class="inner">
    <div class="comment-header">
        <div class="asset-meta">

        </div>
    </div>
    <div class="comment-content" id="comment-quote-342329">
        <blockquote>
引用fallingwine的发言:

最后一条: “服务器返回的数据格式,应该使用JSON,不应使用XML” 存疑,返回某种自描述类的文本类数据即可,貌似没有规定不应使用XML;这与SOAP的rpc风格没有关联,两码事。

基于JSON的优点,网络服务设计就是应该使用JSON而非陈旧的XML作为返回的数据格式~
这点没错,是时候抛弃XML了~

    </div>


    <div id="comment-342578" class="comment">
<div  class="inner">
    <div class="comment-header">
        <div class="asset-meta">

        </div>
    </div>
    <div class="comment-content" id="comment-quote-342578">
        <p>请问大师,如果顾客在控制台需要 “ 一次 ” 更改3个产品的数量,在后台数据库要如何处理事务回滚(transaction rollback )? 不知道是不是在对的地方问对的问题。。。</p>
    </div>


    <div id="comment-343296" class="comment">
<div  class="inner">
    <div class="comment-header">
        <div class="asset-meta">

        </div>
    </div>
    <div class="comment-content" id="comment-quote-343296">
        <blockquote>
引用Mark的发言:

请问大师,如果顾客在控制台需要 “ 一次 ” 更改3个产品的数量,在后台数据库要如何处理事务回滚(transaction rollback )? 不知道是不是在对的地方问对的问题。。。

同问



    <div id="comment-345127" class="comment">
<div  class="inner">
    <div class="comment-header">
        <div class="asset-meta">

        </div>
    </div>
    <div class="comment-content" id="comment-quote-345127">
        <p>openstack作为比较火的开源项目,里面有很多Restful API的设计,我觉得其应该经受得住考验,任何问题都可以参考它!</p>
    </div>


    <div id="comment-346539" class="comment">
<div  class="inner">
    <div class="comment-header">
        <div class="asset-meta">

        </div>
    </div>
    <div class="comment-content" id="comment-quote-346539">
        <blockquote>
引用andrewyang的发言:

“理解RESTful架构”这篇文章提到“另一个设计误区,就是在URI中加入版本号”,不是跟上面提到的第三点矛盾了吗?

我也是看了上一个,一直记得不加版本号这个说法。



    <div id="comment-346943" class="comment">
<div  class="inner">
    <div class="comment-header">
        <div class="asset-meta">

        </div>
    </div>
    <div class="comment-content" id="comment-quote-346943">
        <p>六、过滤信息(Filtering)<br />

有时我们会有比较复杂的过滤比如 : >= , between , || , (color=red && size=s)

这些要怎样用uri来表示呢?




    <div id="comment-347232" class="comment">
<div  class="inner">
    <div class="comment-header">
        <div class="asset-meta">

        </div>
    </div>
    <div class="comment-content" id="comment-quote-347232">
        <blockquote>
引用nebula的发言:

url过长有什么问题?
不会是担心浏览器地址栏里的url长度吧?

get请求,url是有长度限制的



    <div id="comment-347369" class="comment">
<div  class="inner">
    <div class="comment-header">
        <div class="asset-meta">

        </div>
    </div>
    <div class="comment-content" id="comment-quote-347369">
        <p>现在正在做一个基于RESTful API 架构的接口。但是遇到了一些令自己很困扰的问题:<br />

假如每一个设备(devices)是拥有多张设备图片(images)的,那么按照RESTful,我们的URI 设计如下:

现在的设计:
GET /devices/ID/images:获取某个设备的所有图片
GET /devices/ID/images/ID:获取某个设备的某张图片(我们不提供改方法,有人认为没有意义的)
POST /devices/ID/images:给指定设备添加一张图片
PUT /Images/ID:修改某张图片(有疑问)
DELETE /images/ID : 删除某张图片(有疑问)

我觉得合理的设计是:
PUT /devices/ID/Images/ID:修改某个设备的某张图片
DELETE /devices/ID/images/ID : 删除某个设备的某张图片


看到,对于修改和删除某张图片,我是有疑问的,现在的删除是直接通过图片ID进行删除(不管这张图片属于哪一个设备的)。其实真的符合RESTful标准吗? 他们这样设计的理由是:由于图片本身就有自己的图片ID,为什么直接通过ID来删除呢,还非得指定某个设备的图片,然后再删除。

RESTful 强调资源的唯一性,images/ID其实就是唯一的图片资源,而/devices/ID/images/ID也是设备的唯一图片,那么,我都不知道应该采用哪一个URI的设计。



    <div id="comment-347440" class="comment">
<div  class="inner">
    <div class="comment-header">
        <div class="asset-meta">

        </div>
    </div>
    <div class="comment-content" id="comment-quote-347440">
        <p>看到很多人提到关于版本号的问题,其实在“理解RESTful架构”一文两个误区中,提到的是资源不要使用URI来区分版本,但是这篇文章中所说的不同版本是指API 的版本,两个其实是不一样的。<br />




    <div id="comment-348383" class="comment">
<div  class="inner">
    <div class="comment-header">
        <div class="asset-meta">

        </div>
    </div>
    <div class="comment-content" id="comment-quote-348383">
        <p>个人认为版本放在headers 里更合适。放在url 里 确实有点膈应~<br />

另外如果能讲讲 api 的设计过程。

实践出真知吗。期待 实战 api 设计




    <div id="comment-348391" class="comment">
<div  class="inner">
    <div class="comment-header">
        <div class="asset-meta">

        </div>
    </div>
    <div class="comment-content" id="comment-quote-348391">
        <blockquote>
引用nebula的发言:

url过长有什么问题?
不会是担心浏览器地址栏里的url长度吧?

不美观?



    <div id="comment-348775" class="comment">
<div  class="inner">
    <div class="comment-header">
        <div class="asset-meta">

        </div>
    </div>
    <div class="comment-content" id="comment-quote-348775">
        <p>注册、登录、登出这种api该怎么设计?<br />

endpoint怎么定义,还有请求方法是用什么方法?




    <div id="comment-348805" class="comment">
<div  class="inner">
    <div class="comment-header">
        <div class="asset-meta">

        </div>
    </div>
    <div class="comment-content" id="comment-quote-348805">
        <p>看来对REST API 还不够了解。<br />

心是火热的,但路还很漫长,可惜项目不等人。

各类问题都很现实,趋于完善还需要时间。




    <div id="comment-348834" class="comment">
<div  class="inner">
    <div class="comment-header">
        <div class="asset-meta">

        </div>
    </div>
    <div class="comment-content" id="comment-quote-348834">
        <blockquote>
引用nebula的发言:

url过长有什么问题?
不会是担心浏览器地址栏里的url长度吧?

浏览器url长度取决于 浏览器/服务器中较小的那一个.
这个问题我也是遇到过.在一些复杂查询(GET)时候,常常需要向后台传入多个查询参数,我自己大多数都是走post…



    <div id="comment-348896" class="comment">
<div  class="inner">
    <div class="comment-header">
        <div class="asset-meta">

        </div>
    </div>
    <div class="comment-content" id="comment-quote-348896">
        <p>第三点介绍的:应该将API的版本号放入URL。但是在另一篇文章里http://www.ruanyifeng.com/blog/2011/09/restful.html介绍:另一个设计误区,就是在URI中加入版本号:          </p>
    </div>


    <div id="comment-349141" class="comment">
<div  class="inner">
    <div class="comment-header">
        <div class="asset-meta">

        </div>
    </div>
    <div class="comment-content" id="comment-quote-349141">
        <p>如果不用RESTFUL API 设计, 现有的HTTPs API 设计, 利用好HTTP协议, 设计上应该 不差到哪儿吧。</p>

有哪位大侠能举例说明一下RESTFUL API 比现有的设计 有站得住脚的优势?



    <div id="comment-349280" class="comment">
<div  class="inner">
    <div class="comment-header">
        <div class="asset-meta">

        </div>
    </div>
    <div class="comment-content" id="comment-quote-349280">
        <p>你上一篇博文有说“另一个设计误区,就是在URI中加入版本号”<br />

博文地址:http://www.ruanyifeng.com/blog/2011/09/restful.html

和你当前这篇博文第三条有矛盾




    <div id="comment-349430" class="comment">
<div id="comment-last" class="inner">
    <div class="comment-header">
        <div class="asset-meta">

        </div>
    </div>
    <div class="comment-content" id="comment-quote-349430">
        <blockquote>
引用kwk1024的发言:

url的长度在http协议中中没有限制,只是在浏览器中有限制

web服务器是有限制的啊!



</div>






<div class="comments-open" id="comments-open">
    <h2 class="comments-open-header">我要发表看法</h2>
    <div class="comments-open-content">


        <div id="comment-greeting"></div>

        <form method="post" action="http://www.ruanyifeng.com/cgi-bin/mtos/mt-comments.cgi" name="comments_form" id="comments-form" onsubmit="return pleaseWait();">
            <input type="hidden" name="static" value="1" />
            <input type="hidden" name="entry_id" value="1805" />
            <input type="hidden" name="__lang" value="en" />
            <input type="hidden" name="parent_id" value="" id="comment-parent-id" />
            <input type="hidden" name="armor" value="1" />
            <input type="hidden" name="preview" value="" />
            <input type="hidden" name="sid" value="" />
            <div id="comments-open-data">
        <div id="comments-open-text">
                <p><label for="comment-text">您的留言
                (HTML标签部分可用)</label></p>
                <p><textarea id="comment-text" name="text" rows="10" cols="50"></textarea></p>
            </div>
                <div id="comment-form-name">
                    <p><label for="comment-author">您的大名:</label></p>
                    <p><input id="comment-author" name="author" size="30" value="" />  <span class="hint"> &laquo;-必填</span></p>
                </div>
                <div id="comment-form-email">
                    <p><label for="comment-email">电子邮件:</label></p>
                    <p><input id="comment-email" name="email" size="30" value="" />  <span class="hint"> &laquo;-必填,不公开</span></p>
                </div>
                <div id="comment-form-url">
                    <p><label for="comment-url">个人网址:</label></p>
                    <p><input id="comment-url" name="url" size="30" value="" />  <span class="hint"> &laquo;-我信任你,不会填写广告链接</span></p>
                </div>
                <div id="comment-form-remember-me">
                    <p>
                    <label for="comment-bake-cookie">记住个人信息?</label><input type="checkbox" id="comment-bake-cookie" name="bakecookie" onclick="!this.checked?forgetMe(document.comments_form):rememberMe(document.comments_form)" value="1" accesskey="r" /></p>
                </div>
            </div>
                <div id="comment-form-reply" style="display:none">
                <input type="checkbox" id="comment-reply" name="comment_reply" value="" onclick="mtSetCommentParentID()" />
                <label for="comment-reply" id="comment-reply-label"></label>
            </div>
            <div id="comments-open-captcha"></div>
            <div id="comments-open-footer">

正在发表您的评论,请稍候

                <p><input type="submit" accesskey="s" name="post" id="comment-submit" value="发表" />  <span class="hint"> &laquo;- 点击按钮</span></p>
            </div>
        </form>


    </div>
</div>

                    </div>
                </div>

            </div>
        </div>
    <script type="text/javascript" src="http://www.ruanyifeng.com/blog/js/prism.js"></script>

        <div id="footer">
<div id="footer-inner">
    <div id="footer-content">

联系方式 | ruanyifeng.com 2003 - 2015 (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); ga('create', 'UA-46829782-1', 'ruanyifeng.com'); ga('send', 'pageview'); Site Meter

    </div>
</div>

分享按钮 bShare.addEntry({ title: document.getElementById("page-title").innerHTML, url:window.location.href });
document.getElementById("share_button").innerHTML=document.getElementById("share_button_proto").innerHTML;
</div>


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值