restful架构

参考1参考2

RESTful架构,一种互联网软件架构。它结构清晰、符合标准、易于理解、扩展方便,所以正得到越来越多网站的采用

起源 REST这个词,是Roy Thomas Fielding在他2000年的博士论文中提出的

Fielding将他对互联网软件的架构原则,定名为REST,即Representational State Transfer的缩写。这个词组的翻译是"表现层状态转化",如果一个架构符合rest原则,我们就称它为restful架构

资源(resources)

rest翻译为表现层状态转化,它没有指名是谁的表现层,缺乏主语,在这里,我们用资源当做主语,即为资源的表现层状态转化。

所谓资源,就是网络上的一个实体,它可以是一张图片,一段文本。。。我们可以用一个url指向它,用户通过url就能访问到这个资源。

普及两个概念 uri与url,uri(统一资源标识符 Uniform Resource Identifier),url(统一资源定位符 Uniform Resource Locator),其中url是uri的子集

表现层(representation)

资源是一种信息实体,它可以有多种外在表现,比如文本可以以txt格式表现,也可以用html,json,xml格式表现,我们把资源具体呈现出的形式叫做它的表现层。

uri指向资源,但不代表资源的形式,http头部中的accept和content-type等表达了资源的形式

状态转化(state transfer)

访问一个uri,就代表客户和服务器发生了一个交互,这个过程会涉及到数据和状态的变化。

互联网通信协议http协议,是无状态的,不能记录资源的各种状态,资源的状态保存在服务器端,如果要操作服务器上的资源,就要通过一些手段,让服务器知道,从而让服务器的资源发生状态转化,这个变化是建立在表现层之上的,就是表现层状态转化。

客户端使用的手段,只能是http协议,http协议里有很多操作方式的动词比如get,post,put,delete,它们对应了四种基本操作,get用来获取资源(查),post用来新建资源(增)或者修改资源(改),put用来更新资源(改),delete用来删除资源(删)。

综上

restfull架构即为

每个url指向一种资源,客户端和服务器之间传递资源的某种表现层,客户通过4个http动词,对服务器资源进行操作,实现服务器资源表现层状态转化

常见误区

url中含有动词,比如/users/update/1,其中update是动词(手段),这个url就不满足restful架构,正确的写法是/user/1,然后用put方式或post方式

url中带有版本号,不同版本,可以理解为同一资源的不同表现形式,可以把版本号放到http头信息中,从url剔除

 restful api设计

一、协议

api与用户的通信协议采用http协议

二、域名

尽量将api部署到专用域名下,比如 http://api.test.com。如果api不多且比较简单,不太会进一步扩展,也可以放到主域名下,比如http://test.com/api/...

三、版本(version)

将版本号放到http头信息中,也有说法说版本号放到url中更方便直接,github的是放在http头信息里的

四、路径(endpoint)

路径又称“终点”(endpoint),表示api具体网址,在restful架构中,每个网址代表一种资源(resource),所以网址中不能有动词,比如说一个api提供动物园zoo信息,其中包括各种动物以及雇员信息,符合restful架构的url可如下设计

  • http://api.test.com/zoos 
  • http://api.test.com/animals
  • http://api.test.com/employees
  • http://api.test.com/zoo/1

五、http动词

对于资源的具体操作类型,由http动词表示,常用的http动词如下

  • get(select),从服务器获取资源(一项或多项)
  • post(create),在服务器新建一个资源
  • put(update),在服务器更新资源(客户端提供改变后的完整资源)
  • patch(update),在服务器更新资源(客户端提供改变的属性)比如svn中针对修改打patch操作
  • delete(delete),从服务器删除资源

两个不常用http动词

  • head,获取资源元数据
  • options,获取信息,关于资源的哪些属性是客户端可以修改的

例子如下

  • get /zoos,获取所有动物园
  • post /zoos,新建一个动物园
  • get /zoo/id,获取某个指定动物园的信息
  • put /zoo/id,更新某个指定动物园信息(提供该动物园修改后的完整信息)
  • patch /zoo/id,更新某个指定动物园信息(提供该动物园部分信息)
  • delete /zoo/id, 删除某个动物园
  • get /zoos/id/animals,获取指定动物园的所有动物
  • delete /zoos/id/anamal/id,删除指定动物园的指定动物

六、信息过滤(filter)

如果资源数量过多,服务器不好一次性全部返回给用户,api应该提供参数,过滤返回结果,比如指定筛选条件,排序,分页,限制返回数量等

  • ?animalType=1,指定筛选条件
  • ?sort=age&order=desc,排序
  • ?page=2&perPage=10,分页
  • ?limit=10,限制返回数量

参数设计允许冗余,即允许api路径与url参数偶尔重复,比如 get /zoos/id/animals与 get /animals?zooId=id含义相同

七、状态码(status code)

服务器向用户返回的状态码和提示信息,常见的列表以下

  • 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],用户请求的资源被永久删除,且不会再得到
  • 500 INTERNAL SERVER ERROR - [*],服务器发生错误,用户将无法判断发出的请求是否成功

八、错误处理(error handling)

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

九、返回结果

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

  • get /collection 返回资源对象的列表(数组)
  • get /collection/resource 返回单个资源对象
  • post /collection 返回新生成的资源对象
  • put /collection/resource 返回完整的资源对象
  • patch /collection/resource 返回完整的资源对象
  • delete /collection/resource 返回一个空文档

十、hypermedia api

restful api最好做到hypermedia,即返回结果中信息丰富一点,提供链接,连向其他api方法,使得用户不用去查询文档,也知道下一步应该做什么。比如当用户向api.test.com的根目录发出请求时,得到的文档包含以下结果

{
"link":{ "rel": "collection http://api.test.com/zoos",
     "href": "http://api/test.com",
     "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就是这种设计,请求github的api会得到一个所有可用api网址列表

{
  "current_user_url": "https://api.github.com/user",
  "current_user_authorizations_html_url": "https://github.com/settings/connections/applications{/client_id}",
  "authorizations_url": "https://api.github.com/authorizations",
  "code_search_url": "https://api.github.com/search/code?q={query}{&page,per_page,sort,order}",
  "commit_search_url": "https://api.github.com/search/commits?q={query}{&page,per_page,sort,order}",
  "emails_url": "https://api.github.com/user/emails",
  "emojis_url": "https://api.github.com/emojis",
  "events_url": "https://api.github.com/events",
  "feeds_url": "https://api.github.com/feeds",
  "followers_url": "https://api.github.com/user/followers",
  "following_url": "https://api.github.com/user/following{/target}",
  "gists_url": "https://api.github.com/gists{/gist_id}",
  "hub_url": "https://api.github.com/hub",
  "issue_search_url": "https://api.github.com/search/issues?q={query}{&page,per_page,sort,order}",
  "issues_url": "https://api.github.com/issues",
  "keys_url": "https://api.github.com/user/keys",
  "notifications_url": "https://api.github.com/notifications",
  "organization_repositories_url": "https://api.github.com/orgs/{org}/repos{?type,page,per_page,sort}",
  "organization_url": "https://api.github.com/orgs/{org}",
  "public_gists_url": "https://api.github.com/gists/public",
  "rate_limit_url": "https://api.github.com/rate_limit",
  "repository_url": "https://api.github.com/repos/{owner}/{repo}",
  "repository_search_url": "https://api.github.com/search/repositories?q={query}{&page,per_page,sort,order}",
  "current_user_repositories_url": "https://api.github.com/user/repos{?type,page,per_page,sort}",
  "starred_url": "https://api.github.com/user/starred{/owner}{/repo}",
  "starred_gists_url": "https://api.github.com/gists/starred",
  "team_url": "https://api.github.com/teams",
  "user_url": "https://api.github.com/users/{user}",
  "user_organizations_url": "https://api.github.com/user/orgs",
  "user_repositories_url": "https://api.github.com/users/{user}/repos{?type,page,per_page,sort}",
  "user_search_url": "https://api.github.com/search/users?q={query}{&page,per_page,sort,order}"
}

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

{
  "message": "Requires authentication",
  "documentation_url": "https://developer.github.com/v3/users/#get-the-authenticated-user"
}

返回结果给出了服务器的提示信息,以及相应文档地址

十一、其他

api的身份认证可以使用Oauth框架

服务器返回的数据格式,尽量使用json,xml

自动化测试工具java版参考

原文地址:https://www.cnblogs.com/peter-yan/p/8376440.html