收藏 分销(赏)

RESTfulAPI设计标准规范.docx

上传人:快乐****生活 文档编号:2772590 上传时间:2024-06-05 格式:DOCX 页数:16 大小:40.07KB 下载积分:8 金币
下载 相关 举报
RESTfulAPI设计标准规范.docx_第1页
第1页 / 共16页
RESTfulAPI设计标准规范.docx_第2页
第2页 / 共16页


点击查看更多>>
资源描述
背景 现在互联网上充斥着大量相关RESTful API(为方便,下文中“RESTful API ”简写为“API”)怎样设计文章,然而却没有一个”万能“设计标准:怎样鉴权?API 格式怎样?你API是否应该加入版本信息?当你开始写一个app时候,尤其是后端模型部分已经写完时候,你不得不殚精竭虑设计和实现自己apppublic API部分。因为一旦公布,对外公布API将会极难改变。 在给SupportedFu设计API时候,我试图以实用角度来处理上面提到问题。我期望能够设计出轻易使用,轻易布署,而且足够灵活API,本文所以而生。   API设计基础要求 网上很多相关API设计见解全部十分”学院派“,它们可能更有理论基础,不过有时却和现实世界脱轨(所以我是自由派)。所以我这篇文章目标是从实践角度出发,给出目前网络应用API设计最好实践(当然,是我认为最好了~),假如认为不适宜,我不会遵从标准。当然作为设计基础,多个必需标准还是要遵守: 1. 当标准合理时候遵守标准。 2. API应该对程序员友好,而且在浏览器地址栏轻易输入。 3. API应该简单,直观,轻易使用同时优雅。 4. API应该含有足够灵活性来支持上层ui。 5. API设计权衡上述多个标准。 需要强调是:API就是程序员UI,和其它UI一样,你必需仔细考虑它用户体验!   使用RESTful URLs 和action. 即使前面我说没有一个万能API设计标准。但确实有一个被普遍认可和遵守:RESTfu设计标准。它被Roy Felding提出(在她”基于网络软件架构“论文中第五章)。而REST关键标准是将你API拆分为逻辑上资源。这些资源经过http被操作(GET ,POST,PUT,DELETE)。   那么我应该怎样拆分出这些资源呢? 显然从API用户角度来看,”资源“应该是个名词。即使你内部数据模型和资源已经有了很好对应,API设计时候你仍然不需要把它们一对一全部暴露出来。这里关键是隐藏内部资源,暴露必需外部资源。 在SupportFu里,资源是 ticket、user、group。 一旦定义好了要暴露资源,你能够定义资源上许可操作,和这些操作和你API对应关系: · GET /tickets # 获取ticket列表 · GET /tickets/12 # 查看某个具体ticket · POST /tickets # 新建一个ticket · PUT /tickets/12 # 更新ticket 12. · DELETE /tickets/12 #删除ticekt 12 能够看出使用REST好处于于能够充足利用http强大实现对资源CURD功效。而这里你只需要一个endpoint:/tickets,再没有其它什么命名规则和url规则了,cool!   这个endpoint单数复数 一个能够遵从规则是:即使看起来使用复数来描述某一个资源实例看起来别扭,不过统一全部endpoint,使用复数使得你URL愈加规整。这让API使用者愈加轻易了解,对开发者来说也更轻易实现。 怎样处理关联?相关怎样处理资源之间管理REST标准也有相关描述: · GET /tickets/12/messages- Retrieves list of messages for ticket #12 · GET /tickets/12/messages/5- Retrieves message #5 for ticket #12 · POST /tickets/12/messages- Creates a new message in ticket #12 · PUT /tickets/12/messages/5- Updates message #5 for ticket #12 · PATCH /tickets/12/messages/5- Partially updates message #5 for ticket #12 · DELETE /tickets/12/messages/5- Deletes message #5 for ticket #12 其中,假如这种关联和资源独立,那么我们能够在资源输出表示中保留对应资源endpoint。然后API使用者就能够经过点击链接找到相关资源。假如关联和资源联络紧密。资源输出表示就应该直接保留对应资源信息。(比如这里假如message资源是独立存在,那么上面 GET /tickets/12/messages就会返回对应message链接;相反假如message不独立存在,她和ticket依附存在,则上面API调用返回直接返回message信息) 不符合CURD操作 对这个令人迷惑问题,下面是部分处理方法: 1. 重构你行为action。当你行为不需要参数时候,你能够把active对应到activated这个资源,(更新使用patch). 2. 以子资源对待。比如:github上,对一个gists加星操作:PUT /gists/:id/star 而且取消星操作:DELETE /gists/:id/star. 3. 有时候action实在没有难以和某个资源对应上比如search。那就这么办吧。我认为API使用者对于/search这种url也不会有太大意见(毕竟她很轻易了解)。只要注意在文档中写清楚就能够了。 4. 永远使用SSL 毫无例外,永远全部要使用SSL。你应用不知道要被谁,和什么情况访问。有些是安全,有些不是。使用SSL能够降低鉴权成本:你只需要一个简单令牌(token)就能够鉴权了,而不是每次让用户对每次请求署名。 值得注意是:不要让非SSLurl访问重定向到SSLurl。   文档 文档和API本身一样关键。文档应该轻易找到,而且公开(把它们藏到pdf里面或存到需要登录地方全部不太好)。文档应该有展示请求和输出例子:或以点击链接方法或经过curl方法(请见openstack文档)。假如有更新(尤其是公开API),应该立即更新文档。文档中应该有相关何时弃用某个API时间表和详情。使用邮件列表或博客统计是好方法。   版本化 在API上加入版本信息能够有效预防用户访问已经更新了API,同时也能让不一样关键版本之间平稳过渡。相关是否将版本信息放入url还是放入请求头有过争论:API version should be included in the URL or in a header. 学术界说它应该放到header里面去,不过假如放到url里面我们就能够跨版本访问资源了。。(参考openstack)。 strip使用方法就很好:它url里面有主版本信息,同时请求头俩面有子版本信息。这么在子版本改变过程中url稳定。改变有时是不可避免,关键是怎样管理改变。完整文档和合理时间表全部会使得API使用者使用愈加轻松。   结果过滤,排序,搜索: url最好越简短越好,和结果过滤,排序,搜索相关功效全部应该经过参数实现(而且也很轻易实现)。 过滤:为全部提供过滤功效接口提供统一参数。比如:你想限制get /tickets 返回结果:只返回那些open状态ticket–get /tickektsstate=open这里state就是过滤参数。 排序:和过滤一样,一个好排序参数应该能够描述排序规则,而不业务相关。复杂排序规则应该经过组合实现: · GET /ticketssort=-priority- Retrieves a list of tickets in descending order of priority · GET /ticketssort=-priority,created_at- Retrieves a list of tickets in descending order of priority. Within a specific priority, older tickets are ordered first 这里第二条查询中,排序规则有多个rule以逗号间隔组合而成。 搜索:有些时候简单排序是不够。我们能够使用搜索技术(ElasticSearch和Lucene)来实现(依旧能够作为url参数)。 · GET /ticketsq=return&state=open&sort=-priority,created_at- Retrieve the highest priority open tickets mentioning the word ‘return’ 对于常常使用搜索查询,我们能够为她们设置别名,这么会让API愈加优雅。比如: get /ticketsq=recently_closed -> get /tickets/recently_closed.   限制API返回值域 有时候API使用者不需要全部结果,在进行横向限制时候(比如值返回API结果前十项)还应该能够进行纵向限制。而且这个功效能有效提升网络带宽使用率和速度。能够使用fields查询参数来限制返回域比如: GET /ticketsfields=id,subject,customer_name,updated_at&state=open&sort=-updated_at   更新和创建操作应该返回资源 PUT、POST、PATCH 操作在对资源进行操作时候常常有部分副作用:比如created_at,updated_at 时间戳。为了预防用户数次API调用(为了进行此次更新操作),我们应该会返回更新资源(updated representation.)比如:在POST操作以后,返回201 created 状态码,而且包含一个指向新资源url作为返回头   是否需要 “HATEOAS“ 网上相关是否许可用户创建新url有很大异议(注意不是创建资源产生url)。为此REST制订了HATEOAS来描述了和endpoint进行交互时候,行为应该在资源metadata返回值里面进行定义。 (译注:作者这里认为HATEOAS还不算成熟,我也不怎么了解这段就算了,读者感爱好能够自己去原文查看)   只提供json作为返回格式 现在开始比较一下XML和json了。XML即冗长,难以阅读,又不适合多种编程语言解析。当然XML有扩展性优势,不过假如你只是将它来对内部资源串行化,那么她扩展优势也发挥不出来。很多应用(youtube,twitter,box)全部已经开始抛弃XML了,我也不想多费口舌。给了谷歌上趋势图吧: 当然假如你使用用户里面企业用户居多,那么可能需要支持XML。假如是这么话你还有另外一个问题:你http请求中media类型是应该和accept 头同时还是和url?为了方便(browser explorability),应该是在url中(用户只要自己拼url就好了)。假如这么话最好方法是使用.xml或.json后缀。   命名方法? 是蛇形命令(下划线和小写)还是驼峰命名?假如使用json那么最好应该是遵守JAVASCRIPT命名方法-也就是说骆驼命名法。假如你正在使用多个语言写一个库,那么最好根据那些语言所推荐,java,c#使用骆驼,python,ruby使用snake。 个人意见:我总认为蛇形命令愈加好使部分,当然这没有什么理论依据。有些人说蛇形命名读起来愈加快,能达成20%,也不知道真假   默认使用pretty print格式,使用gzip 只是使用空格返回结果从浏览器上看总是认为很恶心(一大坨有没有?~)。当然你能够提供url上参数来控制使用“pretty print”,不过默认开启这个选项还是愈加友好。格外传输上损失不会太大。相反你假如忘了使用gzip那么传输效率将会大大降低,损失大大增加。想象一个用户正在debug那么默认输出就是可读-而不用将结果拷贝到其它什么软件中在格式化-是想起来就很爽事,不是么? 下面是一个例子: $ curl > with-whitespace.txt $ ruby -r json -e 'puts JSON JSON.parse(STDIN.read)' < with-whitespace.txt > without-whitespace.txt $ gzip -c with-whitespace.txt > with-whitespace.txt.gz $ gzip -c without-whitespace.txt > without-whitespace.txt.gz 输出以下: · without-whitespace.txt- 1252 bytes · with-whitespace.txt- 1369 bytes · without-whitespace.txt.gz- 496 bytes · with-whitespace.txt.gz- 509 bytes 在上面例子中,多出空格使得结果大小多出了8.5%(没有使用gzip),相反只多出了2.6%。听说:twitter使用gzip以后它streaming API传输降低了80%(link:).   只在需要时候使用“envelope” 很多API象下面这么返回结果: 1 2 3 4 5 6 {   "data" : {     "id" : 123,     "name" : "John"   } } 理由很简单:这么做能够很轻易扩展返回结果,你能够加入部分分页信息,部分数据元信息等-这对于那些不轻易访问到返回头API使用者来说确实有用,不过伴随“标准”发展(cors和),我个人推荐不要那么做。 何时使用envelope? 有两种情况是应该使用envelope。假如API使用者确实无法访问返回头,或API需要支持交叉域请求(经过jsonp)。 jsonp请求在请求url中包含了一个callback函数参数。假如给出了这个参数,那么API应该返回200,而且把真正状态码放到返回值里面(包装在信封里),比如: 1 2 3 4 5 6 7 callback_function({   status_code: 200,   next_page: "https://..",   response: {     ... actual JSON response body ...   } }) 一样为了支持无法方法返回头API使用者,能够许可envelope=true这么参数。 在post,put,patch上使用json作为输入 假如你认同我上面说,那么你应该决定使用json作为全部API输出格式,那么我们接下来考虑考虑API输入数据格式。 很多API使用url编码格式:就像是url查询参数格式一样:单纯键值对。这种方法简单有效,不过也有自己问题:它没有数据类型概念。这使得程序不得不依据字符串解析出布尔和整数,而且还没有层次结构–即使有部分相关层次结构信息约定存在可是和本身就支持层次结构json比较一下还是不很好用。 当然假如API本身就很简单,那么使用url格式输入没什么问题。但对于复杂API你应该使用json。或干脆统一使用json。 注意使用json传输时候,要求请求头里面加入:Content-Type:application/json.,不然抛出415异常(unsupported media type)。   分页 分页数据能够放到“信封”里面,但伴随标准改善,现在我推荐将分页信息放到link header里面:。 使用link headerAPI应该返回一系列组合好了url而不是让用户自己再去拼。这点在基于游标分页中尤为关键。例以下面,来自github文档 1 2 Link: < rel="next", < rel="last" 自动加载相关资源 很多时候,自动加载相关资源很有用,能够很大提升效率。不过这却和RESTful标准相背。为了如此,我们能够在url中添加参数:embed(或expend)。embed能够是一个逗号分隔串,比如: 1 GET /ticket/12embed=customer.name,assigned_user 对应API返回值以下: 1 2 3 4 5 6 7 8 9 10 11 12 {   "id" : 12,   "subject" : "I have a question!",   "summary" : "Hi, ....",   "customer" : {     "name" : "Bob"   },   assigned_user: {    "id" : 42,    "name" : "Jim",   } } 值得提醒是,这个功效有时候会很复杂,而且可能造成N+1 SELECT 问题。   重写HTTP方法 有用户端只能发出简单GET 和POST请求。为了照料她们,我们能够重写HTTP请求。这里没有什么标准,不过一个普遍方法是接收X-HTTP-Method-Override请求头。   速度限制 为了避免请求泛滥,给API设置速度限制很关键。为此 RFC 6585 引入了HTTP状态码429(too many requests)。加入速度设置以后,应该提醒用户,至于怎样提醒标准上没有说明,不过流行方法是使用HTTP返回头。 下面是多个必需返回头(依据twitter命名规则): · X-Rate-Limit-Limit :目前时间段许可并发请求数 · X-Rate-Limit-Remaining:目前时间段保留请求数。 · X-Rate-Limit-Reset:目前时间段剩下秒数 为何使用目前时间段剩下秒数而不是时间戳? 时间戳保留信息很多,不过也包含了很多无须要信息,用户只需要知道还剩几秒就能够再发请求了这么也避免了clock skew问题。 有些API使用UNIX格式时间戳,我提议不要那么干。为何?HTTP 已经要求了使用 RFC 1123 时间格式   鉴权 Authentication restful API是无状态也就是说用户请求鉴权和cookie和session无关,每一次请求全部应该包含鉴权证实。 经过使用ssl我们能够不用每次全部提供用户名和密码:我们能够给用户返回一个随机产生token。这么能够极大方便使用浏览器访问API用户。这种方法适适用于用户能够首先经过一次用户名-密码验证并得到token,而且能够拷贝返回token到以后请求中。假如不方便,能够使用OAuth 2来进行token安全传输。 支持jsonpAPI需要额外鉴权方法,因为jsonp请求无法发送一般credential。这种情况下能够在查询url中添加参数:access_token。注意使用url参数问题是:现在大部分网络服务器全部会讲query参数保留到服务器日志中,这可能会成为大安全风险。 注意上面说到只是三种传输token方法,实际传输token可能是一样。   缓存 HTTP提供了自带缓存框架。你需要做是在返回时候加入部分返回头信息,在接收输入时候加入输入验证。基础两种方法: ETag:当生成请求时候,在HTTP头里面加入ETag,其中包含请求校验和和哈希值,这个值和在输入改变时候也应该改变。假如输入HTTP请求包含IF-NONE-MATCH头和一个ETag值,那么API应该返回304 not modified状态码,而不是常规输出结果。 Last-Modified:和etag一样,只是多了一个时间戳。返回头里Last-Modified:包含了 RFC 1123 时间戳,它和IF-MODIFIED-SINCE一致。HTTP规范里面有三种date格式,服务器应该全部能处理。 犯错处理 就像html错误页面能够显示错误信息一样,API 也应该能返回可读错误信息–它应该和通常资源格式一致。API应该一直返回对应状态码,以反应服务器或请求状态。API错误码能够分为两部分,400系列和500系列,400系列表明用户端错误:如错误请求格式等。500系列表示服务器错误。API应该最少将全部400系列错误以json形式返回。假如可能500系列错误也应该如此。json格式错误应该包含以下信息:一个有用错误信息,一个唯一错误码,和任何可能具体错误描述。以下: 1 2 3 4 5 {   "code" : 1234,   "message" : "Something bad happened :-(",   "description" : "More details about the error here" } 对PUT,POST,PATCH输入校验也应该返回对应错误信息,比如: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 {   "code" : 1024,   "message" : "Validation Failed",   "errors" : [     {       "code" : 5432,       "field" : "first_name",       "message" : "First name cannot have fancy characters"     },     {        "code" : 5622,        "field" : "password",        "message" : "Password cannot be blank"     }   ] }   HTTP 状态码 1 2 3 4 5 6 7 8 9 10 11 12 200 ok  - 成功返回状态,对应,GET,PUT,PATCH,DELETE.  201 created  - 成功创建。  304 not modified   - HTTP缓存有效。  400 bad request   - 请求格式错误。  401 unauthorized   - 未授权。  403 forbidden   - 鉴权成功,不过该用户没有权限。  404 not found - 请求资源不存在  405 method not allowed - 该http方法不被许可。  410 gone - 这个url对应资源现在不可用。  415 unsupported media type - 请求类型错误。  422 unprocessable entity - 校验错误时用。  429 too many request - 请求过多。
展开阅读全文

开通  VIP会员、SVIP会员  优惠大
下载10份以上建议开通VIP会员
下载20份以上建议开通SVIP会员


开通VIP      成为共赢上传

当前位置:首页 > 品牌综合 > 行业标准/行业规范

移动网页_全站_页脚广告1

关于我们      便捷服务       自信AI       AI导航        抽奖活动

©2010-2026 宁波自信网络信息技术有限公司  版权所有

客服电话:0574-28810668  投诉电话:18658249818

gongan.png浙公网安备33021202000488号   

icp.png浙ICP备2021020529号-1  |  浙B2-20240490  

关注我们 :微信公众号    抖音    微博    LOFTER 

客服