View on GitHub

Yinjie - GitHub.io

Welcome to the Yinjie's notes

REST 简介

REST (表征性状态传输,英文:Representational State Transfer) 以其简洁高效无状态的接口风格,为如今的 WebService 接口指引了新的方向

WebService 接口回顾

简单的说,WebService 就是通过 http、xmpp等协议,以规定格式 (WSDL) 进行信息传输的应用程序接口 (API) 系统。

图一

REST 概念介绍

REST 是 web 系统的架构风格。它对系统组件的设计和应用上进行了一些规范,提出了一个高效的、更易维护的软件系统设计风格。

以它为主体思想设计的系统就叫 RESTful,相应的接口就叫做 RESTful API。它在接口思想将传统的 基于 HTTP 的 URI 抽象成网格资源的实体,对资源的请求操作增删改查,正好对应 HTTP 协议的 POST、DELETE、PUT、GET方法

REST 架构特性

  • 性能:交互组件以用户预期的性能与网络效率作为主导因素
  • 高扩展性:以组件的形式,进行对外以及组件间的交互
  • 简单接口:接口功能的简单化,一个接口不包含较多的业务流程或复杂逻辑
  • 可维护性:组件的更改不会对其它组件产生影响,甚至在程序远行时都可以进行修改
  • 可见性:REST 接口本质上是 web 接口,不是基于代码或系统层级内部的调用。因此,其通信的数据都是可见的
  • 可移植性:包括组件的代码与数据都是可移植的
  • 可靠性:包括组件、链接、数据的可靠性,任一组件的失效都不会对整个系统的运行造成阻碍

REST 架构规范

  • Client–server 模式

客户端不关心服务器的数据是怎么来的,服务端也不关心客户端把这些数据拿去是怎么用的。完全分离、独立开发,只需要接口不变。

  • 无状态

客户端的每一个请求都是独立的,和上下文没有联系。服务器不保持客户端的状态(如 session),客户端的状态在请求中进行携带。

  • cache 机制

  • 此处指的是客户端的缓存机制,不管是终端还是中间层的客户端,都要做好接口的数据缓存,要以后台数据缓存的标准去对 RESTful API 接口数据进行管理。 这样可以取消一些不必要的与服务端之间的交互,进一步提高扩展性和性能。

  • 层次化的系统。中间代理、接入端、负载均衡、安全认证、业务层、逻辑层、数据层等等都要详情分开,每层的逻辑与对外接口具有独立性,与上层的业务无耦合。

  • 客服端代码要求,服务端可以定制和扩展客户端可使用的代码。

  • 统一接口,对整个系统提供的接口数据格式进行统一

图一

RESTful URI

RESTful URI 是将接口的 URL 抽象成一个个网络资源裸体,好比是在本地文件路径一样,一层层的去找到那个文件,文件的路径就是这个资源的 URI。

良好的 RESTful URI 有以下几个原则

  1. 路径各级使用名词,而不是动词
  2. 可读性好,使用正规的英文单词,让别人一看就能知道这个接口是操作什么的
  3. 使用 - 来连接单词
  4. 全部使用小写
  5. 区分一个还是多个(可用复数)
  6. 除非是同一资源的条件查询,不用在 URL 后面加参数
  7. 最好是支持回退的,比如 /my/cat/white 可以访问, /my/cat 也可以访问
  8. URI 可以以用户的角度,进行上下文关联。比如对用户 1001 ,他自己的信息就是 /user/my/info, 对管理员,看他的信息就是 /user/1001/info

特此给出 github 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}",
    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}"
}

RESTful HTTP method

图三

无状态下的安全机制

在 webservice 的应用中,一般都将其部署在独立的服务器上。 其作为一个专门的远程接口提供者,会被各类客户端调用,调用者的身份验证就是不可避免的了。

在目前已经施行 REST 接口的 webservice 提供商里,对安全认证的做法各有千秋,不少采用 OAuth 验证,每次请求都携带相应的 token ,这类方法比较通用和成熟些。

作为全球一线的云服务提供商的亚马逊网络服务系统(英语:Amazon Web Services,简称为AWS),采用了一套签名加密的认证方式,将 REST 的无状态性表现的更彻底。

来源:S3_Authentication2


* HTTP访问控制(CORS)与同源策略

跨站 HTTP 请求(Cross-site HTTP request)是指发起请求的资源所在域不同于该请求所指向资源所在的域的 HTTP 请求。

比如说,域名 A(http://aaa.example) 的某 Web 应用程序中通过<img>标签引入了域名 B(http://bbb.foo) 站点的某图片资源(http://domainb.foo/image.jpg),域名A的那 Web 应用就会导致浏览器发起一个跨站 HTTP 请求。

出于安全考虑,浏览器会限制脚本中发起的跨站请求。比如,使用 XMLHttpRequest 对象发起 HTTP 请求就必须遵守同源策略(same-origin policy)。同源策略是浏览器的一项重要安全控制,可以防止页面内的脚本对非同源数据进行篡改。

注意

跨域并非浏览器限制了发起跨站请求,而是跨站请求可以正常发起,但是返回结果被浏览器拦截了。最好的例子是crsf跨站攻击原理,请求是发送到了后端服务器无论是否跨域!注意:有些浏览器不允许从HTTPS的域跨域访问HTTP,比如Chrome和Firefox,这些浏览器在请求还未发出的时候就会拦截请求,这是一个特例。

XMLHttpRequest 同源策略可分为两种情况,此处先要简介一下什么是简单请求。

所谓的简单,是指:

  • 只使用 GET, HEAD 或者 POST 请求方法。如果使用 POST 向服务器端传送数据,则数据类型(Content-Type)只能是 application/x-www-form-urlencoded, multipart/form-data 或 text/plain中的一种。
  • 不会使用自定义请求头(类似于 X-Modified 这种)。

注意

此为 Gecko 2.0 标准,在这之前,POST 数据类型只有 text/plain 才算是简单请求,目前尽量还是以此为标准

简单请求下的 XHR 跨域

简单请发起的同源限制,只需要服务器端在 response Header 中加入 Access-Control-Allow-Origin: * 即可。

如果想仅允许来自 http://www.aaa.com 的跨域请求,可以设定成 Access-Control-Allow-Origin: http://www.aaa.com

非简单请求下的 XHR 跨域

非简单请求的跨域浏览器会有一个预请求的动作,先发出一个 OPTIONS 的请求。对所跨域的站点进行访问权限的试探。

OPTIONS /resources/post-here/ HTTP/1.1
Host: bbb.com
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Connection: keep-alive
Origin: http://aaa.com

此时,需要在服务器端进行更详情的跨域许可控制,在response 中加入以下 Header.

来源站点许可 Access-Control-Allow-Origin: http://aaa.com

HTTP 请求方式许可 Access-Control-Allow-Methods: POST, GET, OPTIONS

允许携带自定义 Access-Control-Allow-Headers: X-PINGOTHER

许可有效时间 Access-Control-Allow-Headers: X-PINGOTHER

凭证信息的请求的 XHR 跨域

跨域请求是不携带任何 cookie 的,即使符合 cookie 的携带规则,比中 http://aaa.domain.com 访问 http://aaa.domain.com,cookie 写在 domain.com 下。

此时要是想正常携带 cookie,需要对发起的 XMLHttpRequest 中一个标志位设置为true

var xhr = new XMLHttpRequest();
xhr.withCredentials = true;