OpenResty::Spec::REST_cn - OpenResty REST 协议白皮书(草案)
Agent Zhang (章亦春) <agentzh@yahoo.cn>
CREATED: Nov 19, 2007 LAST MODIFIED: Dec 28, 2007 VERSION: 0.17
Copyright (c) 2007 Yahoo! China (中国雅虎公司). Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license can be found at http://www.gnu.org/licenses/fdl.html
本文定义了 OpenResty 基于 REST 风格的 web service API 协议组。
确保对于简单的需求,API 足够简单;同时又能满足很复杂的高级需求。
"Make simple things easy, hard things possible" -- Larry Wall
API 应能映射到绝大部分常见的 SQL 请求语句 ,但应能阻止 SQL injection 的发生。
API 应该足够直观和友好,应尽量做到 DWIM (Do What I mean).
确保 API 能够在绝大多数支持 HTTP 1.1 和 cookie 的环境中使用。包括但不限于网页中的 Javascript, 应用程序中的 Perl, C/C++, Java, C#, VB, 以及纯命令行工具 wget 和 curl 等等。
普通网页中的 Javascript 代码应能通过 AJAX 跨域方式访问 100% 的 API.
来自客户的 API 访问请求应保持无状态。单个请求中应包含所有信息(除了用户身分认证可以存储在 cookie 中供反复使用以外)。
来自服务器的数据格式(XML,JSON,YAML)应能由客户自由控制,同时客户能指定编码(charset).
API 在形式上必须保持统一和一致。同时单次返回的结果中应包含足够的导航信息,帮助用户进行后续请求,以取得与之相关的其他数据。
出于安全性方面的考虑,数据存储应采用类似 wiki 和 Subversion 版本控制方式,应提供接口允许用户拄撤销和恢复临近一段时间的操作或数据.
API 应支持自省能力。用户可以通过 API 获得有关 Models, Actions, 和 API 本身的帮助信息.
本 API 的设计基于美国 Best Practical 公司的 Jifty 框架中所包含的 REST Web Service 接口设计 ( http://search.cpan.org/perldoc?Jifty::Manual::TutorialRest )。
我们的 API 将基于最基本的 HTTP 1.1 协议。特别地,我们将充分利用 HEAD, GET, POST, PUT, DELETE 这几种基本的 HTTP 方法 来简化我们的 API。
在本文中,除非特别说明,将总是假设使用 http:// 模式。
http://
本接口使用下列 HTTP 1.1 方法:
一般用于查询和读取操作,类似 SQL 语言中的 select 语句。
select
一般用于新建对象和插入数据,类似 SQL 语言中的 create 语句和 insert into 语句.
create
insert into
一般用于修改对象的属性和已有的数据记录。类似 SQL 语言中的 update 和 alter 语句.
update
alter
一般用于删除对象和已有的数据记录. 类似 SQL 语言中的 delete 和 drop 语句。
delete
drop
与 GET 相近,但对于返回列表型数据的读取操作而言,返回列表中元素的个数。类似 SQL 语言中的 select count(*).
GET
select count(*)
由于一些防火墙阻止 PUT 和 DELETE 请求,而且对于跨域 AJAX 调用而言, DELETE 和 PUT 也较难实现,OpenResty 为 PUT 和 DELETE 提供了相应的变形接口。 例如
PUT /=/model/Foo <content>
等价于下面这个 POST 请求:
POST /=/put/model/Foo <content>
类似地,下面这个 DELETE 请求
DELETE /=/model/~
等价于下面这个 GET 请求:
GET /=/delete/model/~
为方便起见,POST 请求也具有对应的 GET 变形:
GET /=/post/...?data=<content>
等价于
POST /=/... <content>
所有的 URL 都是大小写敏感的。
/=/
为使我们的 API 能够在 URL 上复用已有的域名,如 www.yahoo.cn,同时避免污染已有的 URL 名字空间,所有的 URL 都以 /=/ 起始。
www.yahoo.cn
如果域名不存在与常规网页 URL 冲突的风险,前导 /=/ 可省略,或替换为其他形式,比如 /webservice/ 或者 /~/ 之类。
/webservice/
/~/
在本文中,将总是使用 /=/ 前导,以强调这些 URL 是特殊的(从某种意义上说),并保持一致性。
GET /=/help
[猜想:该命令可以返回一个无结构的纯文本帮助,或者是有结构的帮助目录列表。]
POST /=/login
登录验证,目前有两种选择:
使用明文传输口令
此种方法使用简单,尤其对于 wget, curl 这样的工具友好。缺点是安全性不高。
一种可能的方式是: GET /=/login/<user_name>/<password>
使用加密传输口令
此种方法使用复杂,需要握手过程,以及加密库的支持。
POST 请求的内容需要经过加密处理。比如:
POST /=/login/<user_name>
POST body
adsfkwk3223df23245rt3423dfds2d
这里的 POST body 中存放着经过加密的密码.
XXX 具体的加密算法
登录获取的 session ID 将被存储于 cookie 中,供后续请求使用。
[猜想: 在轻量级应用中使用明文,以降低接口使用门槛,适应普通用户。对于数据安全性高的企业级应用,使用加密传输的方法。]
OAuth 支持也应认真考虑:
http://oauth.net/
OpenResty 支持所谓的“角色”概念。一个 OpenResty 用户在注册时可以定义若干个“角色”,每个“角色”的权限各不相同,而都可以单独登录,并拥有不同的密码。
例如注册用户 marry 可以拥有 Admin, Reader, User 等多种角色, 每个角色都可以单独登录。当 marry 的 Reader 角色登录时,使用的帐户名为 marry.Reader. 事实上,marry 在作为帐户名使用时,就相当于以 marry.User 角色登录,因为 admin 角色拥有最高的权限。
Admin
Reader
User
marry.Reader
marry
marry.User
admin
一个例子是:
GET /=/login/marry.Reader/myPassword
有关角色的更多信息,请参见 "Roles" 一节。
模型是一种抽象的数据库表格。它可以映射到数据库的物理表,亦可以映射到虚拟的存储结构,如果电子信箱的 inbox. 模型按创建者,可以分为内置和用户自定义两种类型。模型拥有自己细化的权限模型,如可写权限分配,以及可读权限分配。
所有与 Model 相关的接口,其 URL 都满足下列几种形式:
/=/model 操纵模型列表 /=/model/<model_name> 操纵指定的模型,名为 <model_name> /=/model/<model_name>/<column_name> 操纵指定模型 <model_name> 的指定列<column_name> /=/model/<model_name>/<column_name>/<column_value> 操纵指定模型<model_name>中的数据记录, 并由 <column_name> 和 <column_value> 来定位
POST /=/model/MyNewModel
新创建的 Model 的 schema 在 POST 的 content body 中通过对应格式( 比如 JSON )的 schema 说明.
POST /=/model/Bookmark
{ name: "Bookmark", description: "我的书签", columns: [ { name: "url", type: "text", label: "书签网址" }, { name: "title", type: "text", label: "书签标题" }, { name: "description", type: "text", label: "书签描述" } ] }
一次 POST 请求只能指定创建单个 Model. Model 的声明包含两部分,一是模型名,即 name 字段,一是列声明,即 columns 字段。
name
columns
columns 字段可以为一空数组,或者完全省略,此时模型中没有任何可用列。用户可以稍后通过 POST /=/model/<model name>/<column name> 来添加新列。
当 columns 字段为空时,服务器会返回一条警告信息,例如:
{"success":1,"warning":"No 'columns' specified for model \"Foo\"."}
每个模型必须提供一个非空的 description 属性,同时模型各列的定义必须包含 label 这一属性,而且必须为非空。
description
label
请求的 POST 内容中可以不指定模型的 name 属性,因为它已经出现在了请求的 URL 中,如这里的 "Bookmark". 如果用户在 JSON 数据中也指定了 name,则
模型名必须以字母开头,后跟若干字母,下划线或数字。推荐模型名总以大写字母开头,比如 Bookmark, MyMusic 等等。
Bookmark
MyMusic
列名必须以字母开头,后跟若干字母,下划线或数字,与模型名的命名规则相似。但不同的是,推荐列名总是由小写字母开头,例如 book_name, gender, 等等。
book_name
gender
模型名和列名都是大小写敏感的,所以 Bookmark 和 bookmark 被认为是两个不同的 Model.
bookmark
任何模型都将拥有一个默认列,名为 id,用于在一个模型中唯一地标识某一条数据记录(或者说数据行)。若用户自己在模型中指定了 id 列,则服务器会将之忽略,并将给出一条警告信息。值得一提的是,Id, ID, 和 iD 也是保留的列名。
id
Id
ID
iD
默认情况下,URL 及请求数据中的非 ASCII 字符都按 UTF-8 编码处理。如若需要使用其他编码,如 GBK, Big5, 和 Latin1 的话,需要显式地通过 charset 参数指定,例如:
charset
POST /=/model/Bookmark?charset=GBK
任何情况下,URL 和请求数据中的编码必须一致,比如必须同为 UTF-8,或者同为 GBK.
具体的实现会对一个 Model 所含的字段数目和类型进行约束。
当模型已存在时,服务器返回出错信息,例如:
{ success: 0, error:"Model \"Bookmark\" already exists." }
详情请见 "ERROR HANDLING" 一节。
可以通过下面的接口修改已有模型的名字:
PUT /=/model/<old_name> { name: "<new_name" }
PUT /=/model/Bookmark { name: 'MyBookmark' }
如果新的模型名与已存在的另一个 Model 同名,则服务器会报错,例如:
{ success: 0, error: "Model \"MyBookmark\" already exists." }
可以通过下面的接口修改已有模型的描述:
PUT /=/model/<model_name> { descripton: "<new_description" }
修改模型的名字和描述可以放在单次 HTTP 请求中,例如下面这个例子:
PUT /=/model/Bookmark { name: "MyBookmark", description: "这可是我的书签哦!" }
可以通过下面的接口修改已有模型的某一列的名字,类型,或标签:
PUT /=/model/Bookmark/title { name: "bookmark_name", type: "varchar(20)", label: "书签名" }
一次可以只修改其中的一个或者两个属性。例如:
PUT /=/model/Bookmark/title { name: "bookmark_name", label: "书签名" }
如果我们并不想修改 title 列的类型(type)的话。
可以通过下面的接口添加模型的列:
POST /=/model/<model_name>/<new_column_name> { type: "<type>", label: "<label>" }
POST /=/model/Bookmark/comment { type: "text", label: "书签评论" }
可以通过下面的接口删除模型的列:
DELETE /=/model/<model_name>/<column_name>
DELETE /=/model/Bookmark/comment
删除所有的列可以使用下面的命令:
DELETE /=/model/Bookmark/~
值得提醒的是 id 列是保留的,故它不会被删除。
GET /=/model
返回的数据为一无序列表,其内容为用户所有可见的模型的名字,以及对应的 URL。
一个 JSON 格式的例子为:
GET /=/model [ { description: "My favorite bookmark", name: "Bookmark", src: "/=/model/Bookmark" }, { description: "My favorite music", type: "model", name: "Music", src: "/=/model/Music" }, { description: "My frequently accessed blog", name: "Blog", src: "/=/model/Blog" }, ]
有关结果格式的讨论,请见 "DATA FORMAT" 一节.
值得指出的是,模型的 schema 并不等同于真实的数据库物理表的 schema. 模型是一种抽象的概念。
在模型名的命名上,一般取为首字母大写的名词单词,如 Bookmark, Book, Music 等等, 而不像数据库表格一般取成 bookmarks, books, music 这样的形式。
GET /=/model/<model name>
该 URL 将返回模型名为 <model name> 的 schema 定义。
一个 JSON 的例子如下:
GET /=/model/Bookmark { name: "Bookmark", description: "My favorite bookmark", columns: [ { name: "id", type: "serial", src: "/=/model/Bookmark/id" }, { name: "title", type: "text", src: "/=/model/Bookmark/title" }, { name: "url", type: "text", src: "/=/model/Bookmark/url" }, { name: "description", type: "text", src: "/=/model/Bookmark/description" }, ] }
GET /=/model/<model name>/<column name>
GET /=/model/Bookmark/title
返回
{ name: "title", type: "text", src: "/=/model/Bookmark/title" },
DELETE /=/model/<model name>
该命令将删除名为 <model_name> 的模型。
在功能上相当于下面这条 SQL 语句:
drop table <model name>
DELETE /=/model
或者
GET /=/model/<model name>/<column name>/~
逻辑上相当于下面这行 SQL 语句:
select <column name> from <model name>
注意此处以及下文所给出的 SQL 语句也并非真实的 SQL,用来解释 API URL 的语义。
GET /=/model/<model name>/<column name>/<column value>
其功能上相当于下面这条 SQL 语句:
select * from <model name> where <column name>=<column value>
一个具体的体子是:
GET /=/model/Bookmark/id/1
相当于
select * from Bookmark where id = 1
服务器返回的结果类似于:
[ { id: 1, title: "Revision 34: /trunk", url: "http://svn.openfoundry.com/xulapp/trunk", description: "" } ]
如果 column value 中含有 URL 特殊字符,例如 ?, %, & 之类, 则应该进行转义。例如 ? 使用 %2E 代替。
column value
?
%
&
%2E
如果不希望用 = 执行精确匹配的话,可以通过指定 op 选项来指定其他运算符,包括 contains, gt, ge, lt, le, eq, 与 ne.
=
op
contains
gt
ge
lt
le
eq
ne
下面是一个例子:
GET /=/model/Bookmark/title/Yahoo?op=contains
近似于
select * from Bookmark where title like '%Yahoo%'
典型的返回结果如下:
[ { id: 56, title: "Yahoo News", url: "http://news.yahoo.com", description: "美国雅虎网站" }, { id: 57, title: "Yahoo中国", url: "http://cn.yahoo.com", description: "阿里巴巴中国雅虎首页" } ]
GET /=/model/<model name>/~/<column value>
这里的星号 ~ 是“通配符”(wildcard).
~
该查询相当于下面的 SQL 语句:
select * from <model name> where <column 1> = <column value> or <column 2> = <column value> or ...
GET /=/model/<model name>/~/~
值得一提的是,查询 GET /=/model/<model name/<column name>/~ > 的效果 也是显示所有记录。
GET /=/model/<model name
通过指定 extended=1 选项,可以启用扩展查询语法。下面是几个例子:
GET /=/model/Bookmark/id/1,3,52..72?extended=1
相当于下面这句 SQL 查询:
select * from Bookmark where id = 1 or id = 3 or id between 52 and 72
又如:
GET /=/model/Timetable/arrival_time/18:32..20:59,5:07..8:40?extended=1
select * from Timetable where arrival_time between '18:32' and '20:59'
可以使用通配符 * 来表示没有上限或下限,例如:
*
GET /=/model/Person/height/1.86..~?extended=1
表示选取身高在 1.86 以上的 Persion,相当于下面这句 SQL:
select * from Person where height > 1.86
如果要选取身高小于 1.65 的人,则可以这么写:
GET /=/model/Person/height/~..1.65?extended=1
更高级的检索需求 (比如 table join 和 group by) 应由 /=/action/Select 来完成.
/=/action/Select
POST /=/model/<model name>/~/~
该命令用于向指定模型上传若干新记录。一个例子是:
POST /=/model/Bookmark/~/~
[ { title: "Yahoo News", url: "http://news.yahoo.com", description: "美国雅虎网站" }, { title: "Yahoo中国", url: "http://cn.yahoo.com", description: "阿里巴巴中国雅虎首页" }, { title: "Revision: /trunk", url: "http://svn.openfoundry.org/xulapp/trunk", description: "我的 XUL::App 项目" } ]
Output
{ success: 1, rows_affected: 3, last_row: "/=/model/Bookmark/id/3" }
具体的实现会对一次插入的记录数目进行限制。
PUT /=/model/<model name>/<column name>/<column value>
修改指定记录的字段值。
PUT /=/model/Bookmark/url/yahoo?op=contains
{ title: "My Yahoo Home", description: "As title" }
对应的伪 SQL 为:
update bookmarks set title = 'My Yahoo Home', description = 'As title' where url like '%yahoo%'
[猜想:PUT 请求亦可专用于上传二进制的多媒体数据。]
用于删除指定的记录
DELETE /=/model/Bookmark/url/yahoo?op=contains
delete from bookmarks where url like '%yahoo%'
模型的名字可以写成名字空间修饰的形式,比如 Foo.Bar.Baz. 从逻辑上讲, Foo 相当于一个数据库,或者说是模型的集合。
Foo.Bar.Baz
Foo
[猜想: 模型检索时应提供名字空间级别上的搜索。]
"动作(Action)"是一种抽象的功能实体。Action 在概念上与编程语言中的函数相近。
每一个 Action 都有一个界面,界面一般由若干个 parameters 组成。就像每一个 model 都由若干个 columns 组成一样。
Action 亦可分为内建 Action (如 WebSearch 和 Select ) 和用户自定义 Action 两大类.
WebSearch
Select
用户自定义的 Action 在概念上类似于数据库中存储过程,并将通过 OpenResty 自己定义的 minisql 语言中的 update 和 delete 语句来表达其功能.
示例:
POST /=/action/RemoveBookmarks "delete from Bookmark where url like $pattern description like pattern"
GET /=/action/<action name>
可获得指定 action 的界面
GET /=/action/RemoveBooks
输出为它的 minisql 定义:
"delete from Bookmark where url like $pattern description like pattern"
GET /=/action/<action name>/<parameter1>/<value1>?<parameter2>=<value2>&<parameter3>=<value3>&...
以指定的参数调用 actions. 示例:
GET /=/action/RemoveBookmarks/pattern/Hello,world {"success":1}
对于无参数的 Action 调用时使用
GET /=/action/<action name>/~/~
的形式。
DELETE /=/action/<action name>
Select 提供了用 minisql 的 select 语句对 Models 进行高级查询的方式。下面是一个例子:
POST /=/action/Select/lang/minisql "select * from Music, Bookmark where Music.url = Bookmark.url"
minisql 与 SQL 的语法非常近似,不同的是它是大小写敏感的。 所有的关键字都必须为小写。在 minisql 中可以直接引用利用 /=/model 接口定义的 Model 名和列名.
/=/model
GET /=/action/WebSearch [ { name: "query", type: "text" } ] GET /=/action/WebSearch/query/Hello,world [ { title: "Hello world program - Wikipedia, the free encyclopedia", url: "en.wikipedia.org/wiki/Hello_world_program", summary: "..." }, { title: "Helloworld.com", url: "www.helloworld.com/", summary: "..." }, { title: "...", url: "...", summary: "..." }, ... ]
OpenResty 中的视图 (Views) 与数据库中的视图比较近似,但不同的是它是可以带参数的。比如:
POST /=/view/MyQuery { description: "This is my first view", definition: "select * from CComment where name = $name and parent_id = $parent_id" }
此时便创建了一个名为 MyQuery 的 OpenResty 视图对象。 该视图返回的结果是由一个带特殊变量的 minisql 语句来指定的。 由 $ 起始的变量都是 MyQuery 这个视图的参数。例如上面这个例子中, $name 和 $parent_id 都是参数变量。
$
$name
$parent_id
于是后面可以通过这样的语法调用 MyQuery:
GET /=/view/MyQuery/name/Foo?parent_id=Blah
或者等价地:
GET /=/view/MyQuery/parent_id/Blah?name=Foo
视图参量可以带缺省值,例如:
POST /=/view/MyQuery { description: "This is my second view", definition: "select * from CComment where name = $name|'Foo' and parent_id = $parent_id|'Blah'" }
这样调用 MyQuery 的时候如果不提供参量,也不会报错:
GET /=/view/MyQuery/~/~
这与前面的两种 MyQuery 调用方式是等效的。
虽然视图对象的功能都可以使用 Select 动作来完成,但它却拥有以下优点:
服务器可以只在创建视图的时候解析 minisql 语句一次,而在随后的视图调用中提高处理速度。
视图调用中会自动对参量进行 quote 处理,而客户无需自己在拼 minisql 串的时候自己去做。
方便通过 Role 的 URL 规则来限制客户端的权限,从而不必直接将 minisql 界面 (即 /=/action/Select/lang/minisql ) 暴露给客户端。
Role
/=/action/Select/lang/minisql
OpenResty 通过角色 (Role) 来实现子用户和权限分配功能。 一个 OpenResty 注册用户可以拥有多个角色,她可以通过 /=/role 这个 URL 对她的角色进行管理,其中包括添加和删除角色,对角色权限进行分配, 指定角色的登录方式(是通过口令,验证码图片,还是匿名)。
在 OpenResty 中,角色对象是一种特殊的 Model,/=/role 与 /=/model 在接口上有许多相似之处。
每一个角色实体都被视为一个规则 Model. 所有 Model 的查询,修改, 和删除操作都同样适用于 Role.
GET /=/role
服务器返回的是一个角色列表。每一个项目描述了对应角色的名称,描述,和 URL。 例如:
[ { name: "Admin", description: "管理员", src: "/=/role/Admin" }, { name: "User", description: "普通用户", src: "/=/role/User" }, { name: "Anonymous", description: "匿名用户", src: "/=/role/Anonymous" }, { name: "Commenter", description: "评论用户", src: "/=/role/Commenter" } ]
其中 Admin, User, 和 Anonymous 这三个角色是 OpenResty 用户在注册时 自动分配的,而且不能删除。用户直接使用用户名,比如 marry 登录时, 相当于使用 marry.User 帐户来登录。本文档中表的删除和修改操作都需要以 Admin 角色的权限。User 默认是没具有这些权限的。
GET /=/role/<role name>
返回一个哈希结构,其中包括角色名,角色描述,父亲角色,登录方式,等等字段。 一个典型的例子是:
GET /=/role/Admin { name: "Admin", description: "管理员", inherits: "User", columns: [ { name: "method", label: "HTTP方法"}, { name: "resource", lable: "资源名" } ] }
GET /=/role/<role name>/<column>/<value>
<column> 和 <value> 的含义与 Model 的 URL 记法中的一致。
<column>
<value>
例如当二者同时为 ~ 时返回所有的权限规则:
GET /=/role/Commenter/~/~ [ { method: "POST", resource: "/=/model/Comment/~/~" } ]
权限规则总是由两个属性构成,一是 method,用于指定允许的 HTTP 方法。 一是 resource,用于指定允许操纵的资源(或 URI)。
method
resource
POST /=/role/<role name>/~/~ [ rule1, rule2, ... ]
GET/POST/DELETE /=/admin/...
XXX needs work...
指定输入/输出数据和 URL 本身所使用的字符集。
GET /=/model/Bookmark/title/Yahoo?charset=GBK
该选项仅作用于 GET 请求返回列表的情形。它用于指定在返回的列表中跳过起始的记录的数目。例如
GET /=/model/Foo/~/~?offset=10
将返回第十一条及其以后的记录列表。offset 多与 count 选项一起使用,以实现分页功能。
offset
count
该选项仅作用于 GET 请求返回列表的情形。它用于指定返回的条目数,缺省为 500。最大值亦为 500。
GET /=/model/Postbook/body/People?count=20
count 的取值须为小于上限的正整数,否则服务器将报错。
limit 参数为 count 的一个别名。
limit
指定排序项(可指定多个,以逗号分隔). 例如:
GET /=/model/Bookmark/title/Yahoo?op=contains&order_by=title:asc,url:desc
在语义上近似于下面这条 SQL 语句:
select * from bookmarks where title like '%Yahoo%' order by title asc , url desc
升降序的缺省是 SQL 标准的缺省:ASC。当字段中无冒号分隔的升降序声明的时候,就是升序。
该选项仅适用于 JSON 格式,用于生成一个 JS 变量赋值语句。例如:
GET /=/model.json?var=foo
服务器将返回类似下面的数据:
var foo=...;
这里的 ... 是没有指定 var 选项时服务器返回的内容。
...
var
该选项用于 POST 和 PUT 方法的 GET 变形接口,例如
POST /=/model <content>
可以使用
GET /=/post/model?data=<content>
来代替(如果 <content> 含有 URL 特殊字符,需要进行 URL 编码)。
再比如下面这个 PUT 请求
改用 GET 就是
GET /=/put/model/Foo?data=<content>
该选项指定用户名
该选项指定密码
示例
GET /=/model/Bookmark/id.xml?user=foo&password=bar
注:以明文方式传递用户名和密码在安全性较高的场合是不允许使用的。
服务器返回的数据格式可以由用户通过 URL 后缀来控制。
支持的格式后缀为 .json, .xml, 和 .yaml. 它们分别对应 JSON 格式,XML/RDF 格式,和 YAML 格式。 它们的别名为 .js, .rdf, 和 .yml.
.json
.xml
.yaml
.js
.rdf
.yml
当后缀名未指定时,缺省为 .json。
HTTP 响应的头部中的 Content-Type 总是 text/plain.
text/plain
HTTP 请求头部中的 Content-Type 推荐是 text/plain. 如果 PUT 和 POST 请求的 Content-Type 被设为 application/x-www-form-urlencoded, 则 content 部分应该写作
Content-Type
application/x-www-form-urlencoded
data=<content>
XXX specify what happens when it is "multipart/form-data"
当操作成功时,服务器返回的数据如果用 JSON 格式表达,一般为
{"success":1}
如果操作虽然成功,但有警告的场合,会是:
{"success":1,"warning":"Some warning goes here..."}
对于插入 Model 记录的操作,服务器会返回一些额外的字段,比如:
{"success":1,"rows_affected":5,"last_row":"/=/model/Foo/id/3"}
如果操作失败,则是下面这个样子:
{"success":0,"error":"Error message goes here..."}
minisql 语言是标准 SQL 一个核心子集. 其基本语法如下:
minisql: statement statement: select_stmt | update_stmt | delete_stmt select_stmt: "select" pattern_list "from" models postfix_clause_list | "select" pattern_list "from" models pattern_list: pattern "," pattern_list | pattern pattern: aggregate alias | aggregate | column aggregate: func "(" column ")" func: "max" | "min" | "count" | "sum" column: qualified_symbol | symbol qualified_symbol: symbol "." symbol symbol: /[A-Za-z]+/ alias: symbol postfix_clause_list: postfix_clause postfix_clause_list | postfix_clause postfix_clause: where_clause | group_by_clause | order_by_clause where_clause: "where" condition condition: disjunction disjunction: conjunction "or" disjuntion | conjunction conjunction: comparison "and" conjunction | comparison comparison: column operator literal | column operator column | "(" condition ")" operator: ">" | ">=" | "<" | "<=" | "<>" | "=" | "like" literal: string | integer string: '"' symbol '"' | "'" symbol "'" integer: /\d+/ group_by_clause: "group by" column_list column_list: column "," column_list | column order_by_clause: "order by" column_list delete_stmt: "delete" "from" symbol where_clause update_stmt: "update" symbol set_clause where_clause | "update" symbol set_clause set_clause: "set" symbol "=" literal
To install OpenResty, copy and paste the appropriate command in to your terminal.
cpanm
cpanm OpenResty
CPAN shell
perl -MCPAN -e shell install OpenResty
For more information on module installation, please visit the detailed CPAN module installation guide.