REST简介
最近几年,Web程序有种趋势,就是业务逻辑被越来越多地移到了客户端一侧,开创出一种称为富互联网应用(Rich Internet Application, RIA)的架构,在RIA中,服务器的主要功能(有时是唯一功能)是为客户端提供数据存取服务,在这种模式中,服务器变成了Web服务或应用编程接口(Application Programming Interface,API)
RIA可采用多种协议与Web服务通信,远程过程调用(Romete Procedure Call,RPC)协议,例如XML-RPC,及由其衍生的简单对象访问协议,(Simplified Object Access Protocol, SOAP),在几年前比较受欢迎,最近,表现层状态转移(Representational State Transfer,REST)架构崭露头角,成为Web程序的新宠,因为这种架构建立在大家熟知的万维网基础之上
Flask是开发REST架构Web服务的理想框架,因为Flask天生轻量
Roy Fielding在其博士论文中介绍了Web服务的REST架构方式,并列出6个符合这一架构定义的特征:
客户端-服务器
客户端和服务器之间必须有明确的界限无状态
客户端发出的请求中必须包含所有必要的信息,服务器不能在两次请求之间保存客户端的任何状态缓存
服务器发出的响应可以标记为可缓存或不可缓存,这样出于优化目的,客户端(或客户端和服务器之间的中间服务)可以使用缓存接口统一
客户端访问服务器资源时使用的协议必须一致,定义良好,且已经标准化,REST Web服务最常使用的统一接口是HTTP协议系统分层
在客户端和服务器之间可以按需插入代理服务器,缓存或网关,以提高性能、稳定性和伸缩性按需代码
客户端可以选择从服务器上下载代码,在客户端的环境中执行
资源就是一切
资源是REST架构方式的核心概念,在REST架构中,资源时程序中你要着重关注的事务
例如在博客程序中,用户、博客文章和评论都是资源
每个资源都要使用唯一的URL表示,还是以博客程序为例,一篇博客文章可以使用URL/api/posts/12345
表示,其中12345是这篇文章的唯一标识符,使用文章在数据库中的主键表示,URL的格式或内容无关紧要,只要资源的URL只表示唯一的一个资源即可
某一类资源的集合也要有一个URL,博客文章集合的URL可以是/api/posts/
,评论集合的URL可以是/api/comments/
API还可以为某一类资源的逻辑子集定义集合URL,例如,编号为12345的博客文章,其中的所有评论可以使用URL/api/posts/12345/comments/
表示,表示资源集合的URL习惯在末尾加上一个斜线,代表一种“文件夹”结构
注意,Flask会特殊对待末尾带有斜线的路由,如果客户端请求的URL的末尾没有斜线,而唯一匹配的路由末端有斜线,Flask会自动响应一个重定向,转向末端带斜线的URL,反之则不会重定向
请求方法
客户端程序在建立起的资源URL上发送请求,使用请求方法表示期望的操作,若要从博客API中获取现有博客文章的列表,客户端可以向http://www.exam-ple.com/api/posts
发送GET请求,若要插入一篇新博客文章,客户端可以向同一地址发送POST请求,而且请求主体中要包含博客的内容,若要获取编号为12345的博客文章,客户端可以向http://www.exam-ple.com/api/posts/12345
发送GET请求,下表列出了REST架构API中常用的HTTP请求方法以及含义:
请求方法 | 目标 | 说明 | HTTP状态码 |
---|---|---|---|
GET | 单个资源的URL | 获取目标资源 | 200 |
GET | 资源集合的URL | 获取资源的集合(若有分页,则是一页中的资源) | 200 |
POST | 资源集合的URL | 创建新资源,并将其加入目标集合,服务器为新资源指派URL,并在相应的Location首部中返回 | 201 |
PUT | 单个资源的URL | 修改一个现有资源,如果客户端能为资源指派URL,还可用来创建新资源 | 200 |
DELETE | 单个资源的URL | 删除一个资源 | 200 |
DELETE | 资源集合的URL | 删除目标集合中的所有资源 | 200 |
REST架构不要求必须为一个资源实现所有的请求方法,如果资源不支持客户端使用的请求方法,响应的状态码为405,返回“不允许使用的方法”,Flask会自动处理这种错误
请求和响应主体
在请求和响应的主体中,资源在客户端和服务器之间来回传送,但REST没有指定编码资源的方式,请求和响应中的Content-Type首部用于指明主体中资源的编码方式,使用HTTP协议中的内容协商机制,可以找到一种客户端和服务器都支持的编码方式
REST Web服务常用的两种编码方式是JavaScript对象表示法(JavaScript Object Notation,JSON)和可扩展标记语言(Extensible Markup Language),对基于Web的RIA来说,JSON更具吸引力,因为JSON和JavaScript联系紧密,而JavaScript是Web浏览器使用的客户端脚本语言,继续以博客API为例,一篇博客文章对应的资源可以使用如下的JSON表示:
{
"url": "http://www.example/com/api/posts/12345"
"title": "Writing RESTful APIs in Python"
"author": "http://www.example.com/api/users/2"
"body": "... text of the article here ..."
"comments": "http://www.example.com/api/posts/12345/comments"
}
在这篇博客文章中,url、author和comments字段都是完整的资源URL,这是很重要的表示方式,因为客户端可以通过这些URL发掘新资源
在设计良好的REST API中,客户端只需知道几个顶级资源的URL,其他资源的URL则从响应中包含的链接上发掘,这就好比浏览网络时,你在自己知道的网页中点击链接发掘新网页
版本
在传统的以服务器为中心的Web程序中,服务器完全掌握程序,更新程序时,只需在服务器上部署新版本就可以更新所有的用户,因为运行在用户Web浏览器中的那部分程序也是从服务器上下载的
但升级RIA和Web服务要复杂的多,因为客户端程序和服务器上的程序是独立开发的有时甚至由不同的人进行开发,可以考虑一下这种情况,即一个程序的REST Web服务器 被很多客户端使用,其中包括Web浏览器和智能手机原生应用,服务器可以随时更新Web浏览器中的客户端,但无法强制更新智能手机中的应用,更新前先要获得机主的许可,即便机主想进行更新,也不能保证新版应用上传到所有应用商店的时机都完全吻合新服务器端版本的部署
基于以上原因,Web服务的容错能力要比一般的Web程序强,而且还要保证旧版客户端能继续使用,这一问题的常见解决办法是使用版本区分Web服务所处理的URL,例如,首次发布的博客Web服务可以通过/api/v1.0/posts
提供博客文章的集合
在URL中加入Web服务的版本有助于条理化管理新旧功能,让服务器能为新客户端提供新功能,同时继续支持旧版客户端,博客服务可能会修改博客文章使用的JSON格式,同时通过/api/v1.1/posts
提供修改后的博客文章,而客户端仍能通过/api/v1.0/posts
获取旧的JSON格式,在一段时间内,服务器要同时处理v1.1和v1.0这两个版本的URL
提供多版本支持会增加服务器的维护负担,但在某些情况下,这是不破坏现有部署且能让程序不断发展的唯一方式