Api接口层称为接口服务层,负责对客户端的请求进行响应,处理接收客户端传递的参数,进行高层决策并对领域业务层进行调度,最后将处理结果返回给客户端。

接口参数规则配置

接口参数,对于接口服务本身来说,是非常重要的。对于外部调用的客户端来说,同等重要。对于接口参数,我们希望能够既减轻后台开发对接口参数获取、判断、验证、文档编写的痛苦;又能方便客户端快速调用,明确参数的意义。由此,我们引入了参数规则这一概念,即:通过配置参数的规则,自动实现对参数的获取和验证,同时自动生成在线接口文档。

一个简单的示例

假设我们现在需要提供一个用户登录的接口,接口参数有用户名和密码,那么新增的接口类和规则如下:

  1. // 文件 ./src/app/Api/User.php
  2. <?php
  3. namespace App\Api;
  4. use PhalApi\Api;
  5. class User extends Api {
  6. public function getRules() {
  7. return array(
  8. 'login' => array(
  9. 'username' => array('name' => 'username'),
  10. 'password' => array('name' => 'password'),
  11. ),
  12. );
  13. }
  14. public function login() {
  15. return array('username' => $this->username, 'password' => $this->password);
  16. }
  17. }

当请求此接口服务,并类似这样带上username和password参数时:

  1. /?s=User.Login&username=dogstar&password=123456

就可以得到这样的返回结果。

  1. {"ret":0,"data":{"username":"dogstar","password":"123456"},"msg":""}

参数规则格式

参数规则是针对各个接口服务而配置的多维规则数组,由接口类的getRules()方法返回。其中,

  • 一维下标是接口类的方法名,对应接口服务的Action;
  • 二维下标是类属性名称,对应在服务端获取通过验证和转换化的最终客户端参数;
  • 三维下标name是接口参数名称,对应外部客户端请求时需要提供的参数名称。
    小结如下:
  1. public function getRules() {
  2. return array(
  3. '接口类方法名' => array(
  4. '接口类属性' => array('name' => '接口参数名称', ... ... ),
  5. ),
  6. );
  7. }

在接口实现类里面getRules()成员方法配置参数规则后,便可以通过类属性的方式,根据配置指定的名称获取对应的接口参数,如上面的:$this->username$this->password

三级参数规则配置

参数规则主要有三种,分别是:系统参数规则、应用参数规则、接口参数规则。

系统参数

系统参数是指被框架保留使用的参数。目前已被PhalApi占用的系统参数只有一个,即:service参数(缩写为s参数),前面已有介绍。

应用参数

应用参数是指在一个接口系统中,全部项目的全部接口都需要的参数,或者通用的参数。假如我们的商城接口系统中全部的接口服务都需要必须的签名sign参数,以及非必须的版本号,则可以在./config/app.php中的apiCommonRules进行应用参数规则的配置:

  1. <?php
  2. return array(
  3. /**
  4. * 应用接口层的统一参数
  5. */
  6. 'apiCommonRules' => array(
  7. //签名
  8. 'sign' => array(
  9. 'name' => 'sign', 'require' => true,
  10. ),
  11. //客户端App版本号,默认为:1.4.0
  12. 'version' => array(
  13. 'name' => 'version', 'default' => '1.4.0',
  14. ),
  15. ),
  16. )

接口参数

接口参数是指各个具体的接口服务所需要的参数,为特定的接口服务所持有,独立配置。并且进一步在内部又细分为两种:

  • 通用接口参数规则:使用*作为下标,对当前接口类全部的方法有效。
  • 指定接口参数规则:使用方法名作为下标,只对接口类的特定某个方法有效。
    例如为了加强安全性,需要为全部的用户接口服务都加上长度为4位的验证码参数:
  1. public function getRules() {
  2. return array(
  3. '*' => array(
  4. 'code' => array('name' => 'code', 'require' => true, 'min' => 4, 'max' => 4),
  5. ),
  6. 'login' => array(
  7. 'username' => array('name' => 'username', 'require' => true),
  8. 'password' => array('name' => 'password', 'require' => true, 'min' => 6),
  9. ),
  10. );
  11. }

现在,当再次请求用户登录接口,除了要提供用户名和密码外,我们还要提供验证码code参数。并且,对于Api\User类的其他方法也一样。

多个参数规则时的优先级

当同一个参数规则分别在应用参数、通用接口参数及指定接口参数出现时,后面的规则会覆盖前面的,即具体化的规则会替换通用的规则,以保证接口参数满足特定场合的定制要求。

简而言之,多个参数规则的优先级从高到下,分别是(正如你想到的那样):

  • 1、指定接口参数规则
  • 2、通用接口参数规则
  • 3、应用参数规则
  • 4、系统参数规则
温馨提示:如果对过滤器配置了白名单,必选参数最终会自动切换为可选参数,即require = false,详细请参考白名单配置

参数规则配置详细说明

具体的参数规则,根据不同的类型有不同的配置选项,以及一些公共的配置选项。目前,主要的类型有:字符串、整数、浮点数、布尔值、时间戳/日期、数组、枚举类型、文件上传和回调函数。

类型 type 参数名称 name 是否必须 require 默认值 default 最小值 min,最大值 max 更多配置选项(无特殊说明,均为可选)
字符串 string TRUE/FALSE,默认FALSE 应为字符串 可选 regex选项用于配置正则匹配的规则;format选项用于定义字符编码的类型,如utf8、gbk、gb2312等
整数 int TRUE/FALSE,默认FALSE 应为整数 可选 —-
浮点数 float TRUE/FALSE,默认FALSE 应为浮点数 可选 —-
布尔值 boolean TRUE/FALSE,默认FALSE true/false —- 以下值会转换为TRUE:ok,true,success,on,yes,1,以及其他PHP作为TRUE的值
时间戳/日期 date TRUE/FALSE,默认FALSE 日期字符串 可选,仅当为format配置为timestamp时才判断,且最值应为时间戳 format选项用于配置格式,为timestamp时会将字符串的日期转换为时间戳
数组 array TRUE/FALSE,默认FALSE 字符串或者数组,为非数组会自动转换/解析成数组 可选,判断数组元素个数 format选项用于配置数组和格式,为explode时根据separator选项将字符串分割成数组, 为json时进行JSON解析
枚举 enum TRUE/FALSE,默认FALSE 应为range选项中的某个元素 —- 必须的range选项,为一数组,用于指定枚举的集合
文件 file TRUE/FALSE,默认FALSE 数组类型 可选,用于表示文件大小范围,单位为B range选项用于指定可允许上传的文件类型;ext选项用于表示需要过滤的文件扩展名
回调 callable/callback TRUE/FALSE,默认FALSE —- —- callable/callback选项用于设置回调函数,params选项为回调函数的第三个参数(另外第一个为参数值,第二个为所配置的规则)

公共配置选项

公共的配置选项,除了上面的类型、参数名称、是否必须、默认值,还有说明描述、数据来源。下面分别简单说明。

  • 类型 type用于指定参数的类型,可以是string、int、float、boolean、date、array、enum、file、callable,或者自定义的类型。未指定时,默认为字符串。

  • 参数名称 name接口参数名称,即客户端需要传递的参数名称。与PHP变量规则一样,以下划线或字母开头。此选项必须提供,否则会提示错误。

  • 是否必须require为TRUE时,表示此参数为必须值;为FALSE时,表示此参数为可选。未指定时,默认为FALSE。

  • 默认值 default未提供接口参数时的默认值。未指定时,默认为NULL。

  • 最小值 min,最大值 max部分类型适用。用于指定接口参数的范围,比较时采用的是闭区间,即范围应该为:[min, max]。也可以只使用min或max,若只配置了min,则表示:[min, +∞);若只配置了maz,则表示:(-∞, max]。

  • 说明描述 desc用于自动生成在线接口详情文档,对参数的含义和要求进行扼要说明。未指定时,默认为空字符串。

  • 数据来源 source指定当前单个参数的数据来源,可以是post、get、cookie、server、request、header、或其他自定义来源。未指定时,默认为统一数据源。目前支持的source与对应的数据源映射关系如下:

source 对应的数据源
post $_POST
get $_GET
cookie $_COOKIE
server $_SERVER
request $_REQUEST
header $_SERVER['HTTP_X']

通过source参数可以轻松、更自由获取不同来源的参数。以下是一些常用的配置示例。

  1. // 获取HTTP请求方法,判断是POST还是GET
  2. 'method' => array('name' => 'REQUEST_METHOD', 'source' => 'server'),
  3. // 获取COOKIE中的标识
  4. 'is_new_user' => array('name' => 'is_new_user', 'source' => 'cookie'),
  5. // 获取HTTP头部中的编码,判断是否为utf-8
  6. 'charset' => array('name' => 'Accept-Charset', 'source' => 'header'),

若配置的source为无效或非法时,则会抛出异常。如配置了'source' => 'NOT_FOUND',会得到:

  1. "msg": "服务器运行错误: 参数规则中未知的数据源:NOT_FOUND"

9种参数类型

对于各种参数类型,结合示例说明如下。

  • 字符串 string
    当一个参数规则未指定类型时,默认为string。如最简单的:
  1. array('name' => 'username')
温馨提示:这一小节的参数规则配置示例,都省略了类属性,以关注配置本身的内容。

这样就配置了一个参数规则,接口参数名字叫username,类型为字符串。

一个完整的写法可以为:

  1. array('name' => 'username', 'type' => 'string', 'require' => true, 'default' => 'nobody', 'min' => 1, 'max' => 10)

这里指定了为必选参数,默认值为nobody,且最小长度为1个字符,最大长度为10个字符,若传递的参数长度过长,如&username=alonglonglonglongname,则会异常失败返回:

  1. "msg": "非法请求:username.len应该小于等于10, 但现在username.len = 21"

当需要验证的是中文的话,由于一个中文字符会占用3个字节。所以在min和max验证的时候会出现一些问题。为此,PhalApi提供了format配置选项,用于指定字符集。如:

  1. array('name' => 'username', 'type' => 'string', 'format' => 'utf8', 'min' => 1, 'max' => 10)

我们还可以使用regex下标来进行正则表达式的验证,一个邮箱的例子是:

  1. array('name' => 'email', 'regex' => "/^([0-9A-Za-z\\-_\\.]+)@([0-9a-z]+\\.[a-z]{2,3}(\\.[a-z]{2})?)$/i")
  • 整型 int
    整型即自然数,包括正数、0和负数。如通常数据库中的id,即可配置成:
  1. array('name' => 'id', 'type' => 'int', 'require' => true, 'min' => 1)

当传递的参数,不在其配置的范围内时,如&id=0,则会异常失败返回:

  1. "msg": "非法请求:id应该大于或等于1, 但现在id = 0"

另外,对于常见的分页参数,可以这样配置:

  1. array('name' => 'page_num', 'type' => 'int', 'min' => 1, 'max' => 20, 'default' => 20)

即每页数量最小1个,最大20个,默认20个。

  • 浮点 float
    浮点型,类似整型的配置,此处略。

  • 布尔值 boolean
    布尔值,主要是可以对一些字符串转换成布尔值,如ok,true,success,on,yes,以及会被PHP解析成true的字符串,都会转换成TRUE。如通常的“是否记住我”参数,可配置成:

  1. array('name' => 'is_remember_me', 'type' => 'boolean', 'default' => TRUE)

则以下参数,最终服务端会作为TRUE接收。

  1. ?is_remember_me=ok
  2. ?is_remember_me=true
  3. ?is_remember_me=success
  4. ?is_remember_me=on
  5. ?is_remember_me=yes
  6. ?is_remember_me=1
  • 日期 date
    日期可以按自己约定的格式传递,默认是作为字符串,此时不支持范围检测。例如配置注册时间:
  1. array('name' => 'register_date', 'type' => 'date')

对应地,register_date=2015-01-31 10:00:00则会被获取到为:"2015-01-31 100000"。

当需要将字符串的日期转换成时间戳时,可追加配置选项'format' => 'timestamp',则配置成:

  1. array('name' => 'register_date', 'type' => 'date', 'format' => 'timestamp')

则上面的参数再请求时,则会被转换成:1422669600。

此时作为时间戳,还可以添加范围检测,如限制时间范围在31号当天:

  1. array('name' => 'register_date', 'type' => 'date', 'format' => 'timestamp', 'min' => 1422633600, 'max' => 1422719999)

当配置的最小值或最大值为字符串的日期时,会自动先转换成时间戳再进行检测比较。如可以配置成:

  1. array('name' => 'register_date', ... ... 'min' => '2015-01-31 00:00:00', 'max' => '2015-01-31 23:59:59')
  • 数组 array
    很多时候在接口进行批量获取时,都需要提供一组参数,如多个ID,多个选项。这时可以使用数组来进行配置。如:
  1. array('name' => 'uids', 'type' => 'array', 'format' => 'explode', 'separator' => ',')

这时接口参数&uids=1,2,3则会被转换成:

  1. array ( 0 => '1', 1 => '2', 2 => '3', )

如果设置了默认值,那么默认值会从字符串,根据相应的format格式进行自动转换。如:

  1. array( ... ... 'default' => '4,5,6')

那么在未传参数的情况下,自动会得到:

  1. array ( 0 => '4', 1 => '5', 2 => '6', )

又如接口需要使用JSON来传递整块参数时,可以这样配置:

  1. array('name' => 'params', 'type' => 'array', 'format' => 'json')

对应地,接口参数&params={"username":"test","password":"123456"}则会被转换成:

  1. array ( 'username' => 'test', 'password' => '123456', )
温馨提示:使用JSON传递参数时,建议使用POST方式传递。若使用GET方式,须注意参数长度不应超过浏览器最大限制长度,以及URL编码问。

若使用JSON格式时,设置了默认值为:

  1. array( ... ... 'default' => '{"username":"dogstar","password":"xxxxxx"}')

那么在未传参数的情况下,会得到转换后的:

  1. array ( 'username' => 'dogstar', 'password' => 'xxxxxx', )

特别地,当配置成了数组却未指定格式format时,接口参数会转换成只有一个元素的数组,如接口参数:&name=test,会转换成:

  1. array ( 0 => 'test' )
  • 枚举 enum
    在需要对接口参数进行范围限制时,可以使用此枚举型。如对于性别的参数,可以这样配置:
  1. array('name' => 'sex', 'type' => 'enum', 'range' => array('female', 'male'))

当传递的参数不合法时,如&sex=unknow,则会被拦截,返回失败:

  1. "msg": "非法请求:参数sex应该为:female/male,但现在sex = unknow"

关于枚举类型的配置,这里需要特别注意配置时,应尽量使用字符串的值。 因为通常而言,接口通过GET/POST方式获取到的参数都是字符串的,而如果配置规则时指定范围用了整型,会导致底层规则验证时误判。例如接口参数为&type=N,而接口参数规则为:

  1. array('name' => 'type', 'type' => 'enum', 'range' => array(0, 1, 2))

则会出现以下这样的误判:

  1. var_dump(in_array('N', array(0, 1, 2))); // 结果为true,因为 'N' == 0

为了避免这类情况发生,应该使用使用字符串配置范围值,即可这样配置:

  1. array('name' => 'type', 'type' => 'enum', 'range' => array(`0`, `1`, `2`))
  • 文件 file
    在需要对上传的文件进行过滤、接收和处理时,可以使用文件类型,如:
  1. array(
  2. 'name' => 'upfile',
  3. 'type' => 'file',
  4. 'min' => 0,
  5. 'max' => 1024 * 1024,
  6. 'range' => array('image/jpeg', 'image/png') ,
  7. 'ext' => array('jpeg', 'png')
  8. )

其中,min和max分别对应文件大小的范围,单位为字节;range为允许的文件类型,使用数组配置,且不区分大小写。

如果成功,返回的值对应的是$_FILES["upfile"],即会返回:

  1. array(
  2. 'name' => ..., // 被上传文件的名称
  3. 'type' => ..., // 被上传文件的类型
  4. 'size' => ..., // 被上传文件的大小,以字节计
  5. 'tmp_name' => ..., // 存储在服务器的文件的临时副本的名称
  6. )

对应的是:

  • $_FILES["upfile"]["name"] - 被上传文件的名称
  • $_FILES["upfile"]["type"] - 被上传文件的类型
  • $_FILES["upfile"]["size"] - 被上传文件的大小,以字节计
  • $_FILES["upfile"]["tmp_name"] - 存储在服务器的文件的临时副本的名称
  • $_FILES["upfile"]["error"] - 由文件上传导致的错误代码
参考:以上内容来自W3School,文件上传时请使用表单上传,并enctype 属性使用"multipart/form-data"。更多请参考PHP 文件上传

若需要配置默认值default选项,则也应为一数组,且其格式应类似如上。

其中,ext是对文件后缀名进行验证,当如果上传文件后缀名不匹配时将抛出异常。文件扩展名的过滤可以类似这样进行配置:

  • 单个后缀名 - 数组形式

'ext' => array('jpg')

  • 单个后缀名 - 字符串形式

'ext' => 'jpg'

  • 多个后缀名 - 数组形式

'ext' => array('jpg', 'jpeg', 'png', 'bmp')

  • 多个后缀名 - 字符串形式(以英文逗号分割)

'ext' => 'jpg,jpeg,png,bmp'

  • 回调 callable/callback
    当需要利用已有函数进行自定义验证时,可采用回调参数规则,如配置规则:
  1. array('name' => 'version', 'type' => 'callable', 'callback' => 'App\\Common\\Request\\Version::formatVersion')

然后,回调时将调用下面这个新增的类函数:

  1. <?php
  2. namespace App\Common\Request;
  3. use PhalApi\Exception\BadRequestException;
  4. class Version {
  5. public static function formatVersion($value, $rule) {
  6. if (count(explode('.', $value)) < 3) {
  7. throw new BadRequestException('版本号格式错误');
  8. }
  9. return $value;
  10. }
  11. }

回调函数的签名为:function format($value, $rule, $params),第一个为参数原始值,第二个为所配置的规则,第三个可选参数为配置规则中的params选项。最后应返回转换后的参数值。

接口返回

回顾一下,在PhalApi中,掊口返回的结果的结构为:

  1. {
  2. "ret": 200, // 状态码
  3. "data": {
  4. // 业务数据
  5. },
  6. "msg": "" // 错误提示信息
  7. }

正常情况下的返回

正常情况下,在Api层返回的数据结果,会在返回结果的data字段中体现。例如:

  1. class Hello extends Api {
  2. public function world() {
  3. return array('title' => 'Hello World!');
  4. }
  5. }

对应:

  1. {
  2. "ret": 200,
  3. "data": {
  4. "title": "Hello World!"
  5. },
  6. "msg": ""
  7. }

成功返回时,状态码ret为200,并且错误信息msg为空。

失败情况下的返回

对于异常情况,包括系统错误或者应用层的错误,可以通过抛出PhalApi\Exception系列的异常,中断请求并返回相关的错误信息。例如:

  1. class Hello extends Api {
  2. public function fail() {
  3. throw new BadRequestException('签名失败', 1);
  4. }
  5. }

会得到以下结果输出:

  1. {
  2. "ret": 401,
  3. "data": [],
  4. "msg": "Bad Request: 签名失败"
  5. }

返回JSONP、XML等其他格式

JSONP返回格式

如果需要支持JSONP返回格式,可以将 ./config/di.php 中的以下代码注释去掉:

  1. // 支持JsonP的返回
  2. if (!empty($_GET['callback'])) {
  3. $di->response = new \PhalApi\Response\JsonpResponse($_GET['callback']);
  4. }

然后在请求时,传入回调函数的名称callback,即可返回JSONP格式。例如请求:

  1. http://dev.phalapi.net/?s=Hello.World&callback=test

返回:

  1. test({"ret":200,"data":{"title":"Hello World!"},"msg":""})

XML返回格式

如果需要返回XML格式,需要将\PhalApi\DI()->response切换到XML响应类,如:

  1. $di->response = new \PhalApi\Response\XmlResponse();

然后,可看到请求的接口返回类似如下:

  1. <?xml version="1.0" encoding="utf-8"?><xml><ret><![CDATA[200]]></ret><data><title><![CDATA[Hello World!]]></title></data><msg><![CDATA[]]></msg></xml>

其他返回格式

常用的返回格式有如上的JSON、JSONP、XML返回格式。如果需要返回其他的格式,你可以:

  • 1、实现\PhalApi\Response抽象中的formatResult($result)格式化返回结果
  • 2、重新注册\PhalApi\DI()->response服务
    如果希望能由客户端指定返回格式,可通过参数来动态切换。

注释与在线文档

PhalApi提供了自动生成的在线接口文档,对于每一个接口服务,都有对应的在线接口详情文档。如默认接口服务Site.Index的在线接口详情文档为:

1.5 Api接口层 - 图2

此在线接口详情文档,从上到下,依次说明如下。

接口服务名称

接口服务名称是指用于请求时的名称,对应s参数(或service参数)。接口服务的中文名称,为不带任何注解的注释,通常为接口类成员函数的第一行注释。

  1. class Site extends Api {
  2. /**
  3. * 默认接口服务
  4. */
  5. public function index() {
  6. }
  7. }

接口说明

接口说明对应接口类成员函数的@desc注释。

  1. class Site extends Api {
  2. /**
  3. * 默认接口服务
  4. * @desc 默认接口服务,当未指定接口服务时执行此接口服务
  5. */
  6. public function index() {
  7. }
  8. }

接口参数

接口参数是根据接口类配置的参数规则自动生成,即对应当前接口类getRules()方法中的返回。其中最后的“说明” 字段对应参数规则中的desc选项。可以配置多个参数规则。此外,配置文件./config/app.php中的公共参数规则也会显示在此接口参数里。

  1. class Site extends Api {
  2. public function getRules() {
  3. return array(
  4. 'index' => array(
  5. 'username' => array('name' => 'username', 'default' => 'PHPer', ),
  6. ),
  7. );
  8. }
  9. }

返回结果

返回结果对应接口类成员函数的@return注释,可以有多组,格式为:@return 返回类型 返回字段 说明

  1. class Site extends Api {
  2. /**
  3. * 默认接口服务
  4. * @desc 默认接口服务,当未指定接口服务时执行此接口服务
  5. * @return string title 标题
  6. * @return string content 内容
  7. * @return string version 版本,格式:X.X.X
  8. * @return int time 当前时间戳
  9. */
  10. public function index() {
  11. }
  12. }

异常情况

异常情况对应@exception注释,可以有多组,格式为:@exception 错误码 错误描述信息。例如:

  1. /**
  2. * @exception 406 签名失败
  3. */
  4. public function index() {

刷新后,可以看到新增的异常情况说明。

公共注释

对于当前类的全部函数成员的公共@exception异常情况注释和@return返回结果注释,可在类注释中统一放置。而对于多个类公共的@exception@return```注释,则可以在父类的类注释中统一放置。

也就是说,通过把@exception注解和@return注解移到类注释,可以添加全部函数成员都适用的注解。例如,Api\User类的全部接口都返回code字段,且都返回400和500异常,则可以:

  1. <?php
  2. namespace App\Api;
  3. use PhalApi\Api;
  4. /**
  5. * @return int code 操作码,0表示成功
  6. * @exception 400 参数传递错误
  7. * @exception 500 服务器内部错误
  8. */
  9. class User extends Api {

这样就不需要在每个函数成员的注释中重复添加注解。此外,也可以在父类的注释中进行添加。对于相同异常码的@exception注解,子类的注释会覆盖父类的注释,方法的注释会覆盖类的注释;而对于相同的返回结果@return注释,也一样。

需要注意的是,注释必须是紧挨在类的前面,而不能是在namespace前面,否则会导致注释解析失败。

原文: http://docs.phalapi.net/#/v2.0/api