使用CodeIgniter框架搭建RESTful API服务
在2011年8月的时候,我写了一篇博客《使用CodeIgniter框架搭建RESTful API服务》,介绍了RESTful的设计概念,以及使用CodeIgniter框架实现RESTful API的方法。转眼两年过去了,REST在这两年里有了很大的改进。我对于前一篇博客中的某些方面不是很满意,所以希望能利用这次机会写一个更加完善的版本。
我的项目基于Phil Sturgeon的CodeIgniter REST Server,遵循他自己的DBAD协议。Phil的这个项目很棒,干净利落,简单实用,并且极具特色,解决了我自己项目中两个问题:在请求中查询语句的使用,以及复杂的身份验证方法。正如我前面所说,我的项目基于Phil的项目开发,并且只有一个方面不同:我给每个资源分别分配一个控制器,而不是用一个独立庞大的控制器进行统一管理。使用多个独立控制器的好处是维护起来更加简单方便。
完整的项目源码可以在awhitney42/codeigniter-restserver-resources下载。
RESTful
在深入探讨之前,我们先来回顾一下TESTful的概念。REST全名Representational State Transfer,中文可以译为:表现层状态转化,是一种网络服务的架构工具,而不仅仅是一套接口规范。
使用名词而不是动词
你的RESTful接口应该提供访问而不是方法。
所以,这样是不可取的:
1 |
/createCustomer |
应该这样:
1 |
POST /customers |
所有东西都应该有ID
REST的优点之一就是简洁,一定程度上来讲,REST只是一个URI的集合,每个资源都需要一个独一无二的ID作为一个标识。
1 |
GET /customers/666 |
使用动词进行操作
在REST里,使用不同的HTTP动词进行增删改查的操作:
| 动词 | 操作 |
|---|---|
| POST | 新建一个资源 |
| GET | 读取一个资源 |
| PUT | 更新一个资源 |
| DELETE | 删除一个资源 |
使用这个REST服务的映射表来设计接口,在开发客户端的时候可以轻松上手,不用深入理解接口的含义。这套标准十分适用于REST,符合面向服务架构(SOA)的核心设计思想:服务抽离、松耦合、可复用、可发现、健全性。
通过这个一致的映射关系,客户端也知道哪个动词是幂等(Idempotent)的。幂等是指这个操作可以重复多次,并且每次都会得到相同的结果。参照HTTP规范,GET、PUT和DELETE操作是幂等的,POST操作不是幂等的。这也就是为什么在REST中使用POST来进行添加操作,而使用PUT来进行更新操作。
把东西链接起来
REST中的一个核心概念是HATEOAS(Hypermedia As The Engine Of Application State),即“超媒体即应用状态引擎”。这意味着应该始终使用链接(超媒体)来获取资源信息,然后客户端通过这个链接就可以获取到不同的资源。
所以,相关的资源应当返回一条链接,而不是返回整个资源的内容。
也就是,该这样:
1 |
<officer id="1"> |
而不该这样:
1 |
<officer id="1"> |
返回资源的链接应该用完整的URI地址而不是相对路径,这要求客户端请求这些资源的当前状态,维持HATEOAS原则。使用完整的URI地址的好处是客户端不需要额外的了解API相关的内容,只需要简单的访问这些链接就可以了。
提供多种资源表现方式
REST中的R代表Representational,即表现层,这意味着REST服务应该提供不同的表现方式,以全面支持不同的客户端请求。在一次HTTP请求中,服务器可以通过指定的表现方式返回资源,REST服务应该使用标准的表现方式,以便客户端之间的信息互通。
比如可以通过如下请求XML格式的数据:
1 |
GET /customers/666 |
或者vcard格式:
1 |
GET /customers/666 |
或者pdf格式:
1 |
GET /customers/666 |
或者自定义格式:
1 |
GET /customers/666 |
使用状态码作为回复
HTTP的状态码提供了一套标准化方案,用来反馈请求的状态。
| 含义 | 解释 | |
|---|---|---|
| 200 | OK | 确认GET、PUT和DELETE操作成功 |
| 201 | Created | 确认POST操作成功 |
| 304 | Not Modified | 用于条件GET访问,告诉客户端资源没有被修改 |
| 400 | Bad Request | 通常用于POST或者PUT请求,表明请求的内容是非法的 |
| 401 | Unauthorized | 需要授权 |
| 403 | Forbidden | 没有访问权限 |
| 404 | Not Found | 服务器上没有资源 |
| 405 | Method Not Allowed | 请求方法不能被用于请求相应的资源 |
| 409 | Conflict | 访问和当前状态存在冲突 |
CodeIgniter
CodeIgniter是一个流行的MVC框架,很适合用来进行RESTful API开发。控制器(Controller)处理客户端的请求并返回内容,模型(Model)进行增删改查(CRUD)的操作,视图(View)用来处理资源的表现格式。不过在这个例子里,我们没有用模型,而是直接用控制器进行格式处理,这样整个项目更干净更简单。
代码可以直接在Github中获取:codeigniter-restserver-resources。
在接下来的例子里,我们用到了REST_Controller这个库,继承自原生的CI_Controller。它可以完成绝大部分繁杂的工作:处理请求、调用模块、格式化内容、返回数据。你的每个资源控制器都应该继承自REST_Controller这个类。
下面我们来看一个例子,我们假设要提供一个Widgets资源的RESTful接口:
1 |
class Widgets extends REST_Controller |
get()
Widgets类中的第一个函数是get(),用来响应HTTP的GET请求。这个函数调用了父类的protected函数_get(),用来获取请求中的参数ID。然后这个函数根据是否有参数ID使用widgets_model调用getWidgets()或者getWidget($id)方法,模型的返回值将会通过父类的response()函数返回,返回的内容包含对应的状态码和符合格式的数据。
1 |
function get() |
cURL是一个很好的命令行工具,我们可以用来测试REST服务,返回一个JSON格式的数据:
1 |
$ curl -i -H "Accept: application/json" -X GET http://foo.com/index.php/api/widgets |
则会返回如下内容:
1 |
HTTP/1.1 200 OK |
请求特定的资源也很简单,这次我们去请求ID为2的widget并且通过XML格式返回:
1 |
$ curl -i -H "Accept: application/xml" -X GET http://foo.com/index.php/api/widgets/id/2 |
返回如下内容:
1 |
HTTP/1.1 200 OK |
如果请求一个不存在的资源就会返回404的错误码:
1 |
HTTP/1.1 404 Not Found |
post()
接下来的函数是post(),用来处理创建widget的POST请求。可以通过$this->_post_args获取请求的数据。父类通过Format.php对请求的数据进行处理并把它们放到了$this->_post_args里。接下来post()方法使用widgets_model模型调用createWidgets($data)函数,如果数据非法或者请求冲突,widgets_model模型会抛出异常并且返回异常的内容。如果调用成功,则会调用getWidget($id)函数获取最新的widget,在返回的时候会将返回值和201 (Created)的状态码一起返回。
1 |
function post() |
为了测试通过POST请求新建资源的操作,我们需要把我们的cURL包裹在PHP代码里,创建一个rest_client.php文件:
1 |
print "\n-----TESTING REST POST-----\n"; |
在命令行中通过PHP命令执行:
1 |
$ php rest_client.php |
返回内容如下:
1 |
-----TESTING REST POST----- |
put()
接下来的方法是put(),用来处理PUT请求,更新已经存在的资源数据。处理过程和POST的处理十分相似,最大的区别就在于使用$this->_put_args而不是$this->_post_args,以及返回200而不是201。
1 |
public function put() |
为了测试UPDATE更新资源的功能,我们依旧使用PHP进行cURL的操作,大多数的网络服务器默认没有开启PUT和DELETE,我们可以在header中使用X-HTTP-Method-Override,通过POST来发送PUT请求。这样的话,服务器会把它当做一个POST请求,而REST服务器会把它作为PUT操作处理。
1 |
print "\n-----TESTING REST PUT-----\n"; |
同样使用PHP命令测试:
1 |
$ php rest_client.php |
返回结果:
1 |
-----TESTING REST PUT----- |
delete()
最后的函数就是delete了,请求中必须包含ID的参数否则就会返回400(Bad Request)的状态码,因为我们不希望用户删除所有的资源内容。如果ID没有对应的资源则会返回404(Not Found) 错误码。如果对应的widget存在,则会尝试调用deleteWidget($id)删除对应资源。操作中的所有异常都会被捕获到并且返回。如果删除成功,返回200(Success)状态码。
1 |
function delete() |
我们可以用命令行工具cURL进行测试,参数ID像GET一样放在URL地址中,和PUT相同,我们通过X-HTTP-Method-Override使服务器把请求当做POST处理,REST服务则会把这次请求当做DELETE操作处理。
1 |
$ curl -i -H "Accept: application/xml" -H "X-HTTP-Method-Override: DELETE" -X POST http://foo.com/index.php/api/widgets/id/1 |
返回200 (Success)说明id为1的资源已经被成功删除:
1 |
HTTP/1.1 200 OK |
如果ID非法,则会返回404(Not Found)错误码,如果没有删除的权限,则会返回403 (Forbidden) 状态码。
Authentication
正如前面提到的,Phil的REST框架和我的原来的设计相比,在用户权限认证上有了很大的改进, 提供basic和digest两种认证方式。还有很多其他特性,比如可以用LDAP字典将授权融为一体,具体可以在config/rest.php中设置。
除了传统的认证方式,你还可以使用API keys或者IP地址的白名单进行用户权限管理。
DTO
在原先的设计中,我使用DTO(Data Transfer Object)在不同格式之间传输数据,Phil使用Format类来解决这个问题,从Widget的例子中我们可以看到,使用数据是多么的方便,只需要$this->_post_args或者$this->response()就可以解决问题。当然,这并不意味着DTO不好用,但是在很多场合下它会显得十分复杂和庞大。
在我原来的项目里,我对客户端需要使用和服务器端同样的DTO库来传输数据很不满意,因为它违反了KISS的原则。
总结
REST目前已经是比较成熟的网络服务的框架模型方案,是API产品目录网站programmableweb的基石,是各式各样应用和服务器的数据传输的基础,同时对于SOA来说也是十分重要,促进网络端和移动端的接口技术日趋成熟。
正如前面所看到的,用CodeIgniter框架实现RESTful接口十分简单,可以下载我的项目源码awhitney42/codeigniter-restserver-resources,从现在就开始开发吧!
后记
REST是一种遵从传统设计模式的架构风格,在过去的几年中一直都在改进,并且会一直坚持下去。所以如果对于REST或者是本文有什么意见,或者对于其中的概念有什么困惑,请在文章后面留言。
原文地址:
使用CodeIgniter框架搭建RESTful API服务的更多相关文章
- Go实战--通过gin-gonic框架搭建restful api服务(github.com/gin-gonic/gin)
生命不止,继续 go go go !!! 先插播一条广告,给你坚持学习golang的理由: <2017 软件开发薪酬调查:Go 和 Scala 是最赚钱的语言> 言归正传! 之前写过使用g ...
- 基于gin web框架搭建RESTful API服务
这篇主要学习go项目中的项目结构.项目规范等知识,ROM采用的database/sql的写法. 1.技术框架 利用的是ginweb框架,然后ROM层选用database/sql,安装mysql驱动.安 ...
- 【重学Node.js 第1&2篇】本地搭建Node环境并起RESTful Api服务
本地搭建Node环境并起RESTful Api服务 课程介绍看这里:https://www.cnblogs.com/zhangran/p/11963616.html 项目github地址:https: ...
- 使用 Beego 搭建 Restful API 项目
1 环境准备 首先你需要在你的环境安装以下软件: go:编程语言运行环境 git:版本控制工具 beego:go 语言流行的开发框架 bee:beego 配套的快速搭建工具 你喜欢的数据库:这里以 M ...
- 【PHP】基于ThinkPHP框架搭建OAuth2.0服务
[PHP]基于ThinkPHP框架搭建OAuth2.0服务 http://leyteris.iteye.com/blog/1483403
- 开放接口/RESTful/Api服务的设计和安全方案
总体思路 这个涉及到两个方面问题:一个是接口访问认证问题,主要解决谁可以使用接口(用户登录验证.来路验证)一个是数据数据传输安全,主要解决接口数据被监听(HTTPS安全传输.敏感内容加密.数字签名) ...
- 搭建RESTful API 之 实现WSGI服务的URL映射
javarestfull 搭建参考 http://blog.csdn.net/hejias/article/details/47424511 问题引出:对于一个稍具规模的网站来说,实现的功能不可能通过 ...
- 开放接口/RESTful/Api服务的设计和安全方案详解
一.总体思路 这个涉及到两个方面问题:一个是接口访问认证问题,主要解决谁可以使用接口(用户登录验证.来路验证)一个是数据数据传输安全,主要解决接口数据被监听(HTTPS安全传输.敏感内容加密.数字签名 ...
- node express+mysql搭建简易API服务—body-parser中间件
最近用express搭建了一个简单的RESTful风格的API服务,数据库使用mysql,主要用于获取数据库数据,模糊搜索等. 需要用到的模块: express:这个都很熟悉了: body-parse ...
随机推荐
- 提高IO性能
noatime - 不更新文件系统上 inode 访问记录,可以提升性能 [root@ok etc]# cat /etc/fstab |grep noatime /dev/mapper/vg_ok-l ...
- C#学习笔记-----基于AppDomain的"插件式"开发
很多时候,我们都想使用(开发)USB式(热插拔)的应用,例如,开发一个WinForm应用,并且这个WinForm应用能允许开发人员定制扩展插件,又例如,我们可能维护着一个WinService管理系统, ...
- IOS项目自动构建
# Sets the target folders and the final framework product. # 如果工程名称和Framework的Target名称不一样的话,要自定义FMKN ...
- ThinkPHP实现移动端访问自动切换主题模板
ThinkPHP的模板主题机制,如果只是在PC,只要需修改 DEFAULT_THEME (新版模板主题默认是空,表示不启用模板主题功能)配置项就可以方便的实现多模板主题切换. 但对于移动端和PC端,也 ...
- hdu 1542 扫描线求矩形面积的并
很久没做线段树了 求矩形面积的并分析:1.矩形比较多,坐标也很大,所以横坐标需要离散化(纵坐标不需要),熟悉离散化后这个步骤不难,所以这里不详细讲解了,不明白的还请百度2.重点:扫描线法:假想有一条扫 ...
- 【原创】CDM添加新磁盘,然后负载
hdfs快占满了,所以为节点中添加新的磁盘(这块是个教训,以后用新的节点时,磁盘需要一次性插满,省得后续再添加磁盘了) 注意: 添加磁盘时,不仅仅datanode在配置时添加节点,nodemanage ...
- (int &)a 和(int)a
[cpp] view plain copy float a = 1.0f; cout < < (int)a < < endl; cout < < ...
- ARP缓存表的构成ARP协议全面实战协议详解、攻击与防御
ARP缓存表的构成ARP协议全面实战协议详解.攻击与防御 1.4.3 ARP缓存表的构成 在局域网的任何一台主机中,都有一个ARP缓存表.该缓存表中保存中多个ARP条目.每个ARP条目都是由一个IP ...
- POJ1204 Word Puzzles(AC自动机)
给一个L*C字符矩阵和W个字符串,问那些字符串出现在矩阵的位置,横竖斜八个向. 就是个多模式匹配的问题,直接AC自动机搞了,枚举字符矩阵八个方向的所有字符串构成主串,然后在W个模式串构造的AC自动机上 ...
- BZOJ2310 : ParkII
单路径最大和问题,设f[i][j][S]表示到达(i,j),轮廓线状态为S的最优解. S用4进制m+1位数表示,0表示无插头,1表示左括号,2表示右括号,3表示独立插头. 在DP之前先进行一次预处理, ...