1.5 PhalApi 2.x Api接口层

2018-07-28 21:23 更新

Api接口服务层

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

接口参数规则配置

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

一个简单的示例

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

// 文件 ./src/app/Api/User.php
<?php
namespace App\Api;


use PhalApi\Api;


class User extends Api {


    public function getRules() {
        return array(
            'login' => array(
                'username' => array('name' => 'username'),
                'password' => array('name' => 'password'),
            ),
        );
    }


    public function login() {
        return array('username' => $this->username, 'password' => $this->password);
    }                               
}

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

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

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

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

参数规则格式

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

  • 一维下标是接口类的方法名,对应接口服务的Action;
  • 二维下标是类属性名称,对应在服务端获取通过验证和转换化的最终客户端参数;
  • 三维下标name是接口参数名称,对应外部客户端请求时需要提供的参数名称。

小结如下:

    public function getRules() {
        return array(
            '接口类方法名' => array(
                '接口类属性' => array('name' => '接口参数名称', ... ... ),
            ),
        );
    }

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

三级参数规则配置

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

系统参数

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

应用参数

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

<?php
return array(
    /**
     * 应用接口层的统一参数
     */
    'apiCommonRules' => array(
        //签名
        'sign' => array(
            'name' => 'sign', 'require' => true,
        ),
        //客户端App版本号,默认为:1.4.0
        'version' => array(
            'name' => 'version', 'default' => '1.4.0', 
        ),
    ),
)

接口参数

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

  • 通用接口参数规则:使用*作为下标,对当前接口类全部的方法有效。
  • 指定接口参数规则:使用方法名作为下标,只对接口类的特定某个方法有效。

例如为了加强安全性,需要为全部的用户接口服务都加上长度为4位的验证码参数:

    public function getRules() {
        return array(
            '*' => array(
                'code' => array('name' => 'code', 'require' => true, 'min' => 4, 'max' => 4),
            ),
            'login' => array(
                'username' => array('name' => 'username', 'require' => true),
                'password' => array('name' => 'password', 'require' => true, 'min' => 6),
            ),
        );
    }

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

多个参数规则时的优先级

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

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

  • 1、指定接口参数规则
  • 2、通用接口参数规则
  • 3、应用参数规则
  • 4、系统参数规则

参数规则配置详细说明

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

类型 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参数可以轻松、更自由获取不同来源的参数。以下是一些常用的配置示例。

// 获取HTTP请求方法,判断是POST还是GET
'method' => array('name' => 'REQUEST_METHOD', 'source' => 'server'),


// 获取COOKIE中的标识
'is_new_user' => array('name' => 'is_new_user', 'source' => 'cookie'),


// 获取HTTP头部中的编码,判断是否为utf-8
'charset' => array('name' => 'Accept-Charset', 'source' => 'header'),

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

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

9种参数类型

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

  • 字符串 string

当一个参数规则未指定类型时,默认为string。如最简单的:

array('name' => 'username')

温馨提示:这一小节的参数规则配置示例,都省略了类属性,以关注配置本身的内容。

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

一个完整的写法可以为:

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

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

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

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

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

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

array('name' => 'email', 'regex' => "/^([0-9A-Za-z\\-_\\.]+)@([0-9a-z]+\\.[a-z]{2,3}(\\.[a-z]{2})?)$/i")

  • 整型 int

整型即自然数,包括正数、0和负数。如通常数据库中的id,即可配置成:

array('name' => 'id', 'type' => 'int', 'require' => true, 'min' => 1)

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

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

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

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。如通常的“是否记住我”参数,可配置成:

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

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

?is_remember_me=ok
?is_remember_me=true
?is_remember_me=success
?is_remember_me=on
?is_remember_me=yes
?is_remember_me=1

  • 日期 date

日期可以按自己约定的格式传递,默认是作为字符串,此时不支持范围检测。例如配置注册时间:

array('name' => 'register_date', 'type' => 'date')

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

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

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

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

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

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

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

array('name' => 'register_date', ... ... 'min' => '2015-01-31 00:00:00', 'max' => '2015-01-31 23:59:59')

  • 数组 array

很多时候在接口进行批量获取时,都需要提供一组参数,如多个ID,多个选项。这时可以使用数组来进行配置。如:

array('name' => 'uids', 'type' => 'array', 'format' => 'explode', 'separator' => ',')

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

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

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

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

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

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

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

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

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

array ( 'username' => 'test', 'password' => '123456', )

温馨提示:使用JSON传递参数时,建议使用POST方式传递。若使用GET方式,须注意参数长度不应超过浏览器最大限制长度,以及URL编码问。

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

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

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

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

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

array ( 0 => 'test' )

  • 枚举 enum

在需要对接口参数进行范围限制时,可以使用此枚举型。如对于性别的参数,可以这样配置:

array('name' => 'sex', 'type' => 'enum', 'range' => array('female', 'male'))

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

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

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

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

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

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

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

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

  • 文件 file

在需要对上传的文件进行过滤、接收和处理时,可以使用文件类型,如:

array(
    'name' => 'upfile', 
    'type' => 'file', 
    'min' => 0, 
    'max' => 1024 * 1024, 
    'range' => array('image/jpeg', 'image/png') , 
    'ext' => array('jpeg', 'png')
)

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

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

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

对应的是:

  • $_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

当需要利用已有函数进行自定义验证时,可采用回调参数规则,如配置规则:

array('name' => 'version', 'type' => 'callable', 'callback' => 'App\\Common\\Request\\Version::formatVersion')

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

<?php
namespace App\Common\Request;


use PhalApi\Exception\BadRequestException;


class Version {


    public static function formatVersion($value, $rule) {
        if (count(explode('.', $value)) < 3) {
            throw new BadRequestException('版本号格式错误');
        }
        return $value;
    }
}

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

接口返回

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

{
    "ret": 200, // 状态码
    "data": {
        // 业务数据
    },
    "msg": "" // 错误提示信息
}

正常情况下的返回

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

class Hello extends Api {


    public function world() {
        return array('title' => 'Hello World!');
    }
}

对应:

{
    "ret": 200,
    "data": {
        "title": "Hello World!"
    },
    "msg": ""
}

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

失败情况下的返回

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

class Hello extends Api {


    public function fail() {
        throw new BadRequestException('签名失败', 1);
    }
}

会得到以下结果输出:

{
    "ret": 401,
    "data": [],
    "msg": "Bad Request: 签名失败"
}

注释与在线文档

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

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

接口服务名称

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

class Site extends Api {


    /**
     * 默认接口服务
     */
    public function index() {
    }
}

接口说明

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

class Site extends Api {


    /**
     * 默认接口服务
     * @desc 默认接口服务,当未指定接口服务时执行此接口服务
     */
    public function index() {
    }
}

接口参数

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

class Site extends Api {


    public function getRules() {
        return array(
            'index' => array(
                'username'  => array('name' => 'username', 'default' => 'PHPer', ),
            ),
        );
    }
}

返回结果

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

class Site extends Api {


    /**
     * 默认接口服务
     * @desc 默认接口服务,当未指定接口服务时执行此接口服务
     * @return string title 标题
     * @return string content 内容
     * @return string version 版本,格式:X.X.X
     * @return int time 当前时间戳
     */
    public function index() {
    }
}

异常情况

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

    /**
     * @exception 406 签名失败
     */
    public function index() {

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

公共注释

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

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

<?php
namespace App\Api;


use PhalApi\Api;


/**
 * @return int code 操作码,0表示成功
 * @exception 400 参数传递错误
 * @exception 500 服务器内部错误
 */


class User extends Api {

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

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

以上内容是否对您有帮助:
在线笔记
App下载
App下载

扫描二维码

下载编程狮App

公众号
微信公众号

编程狮公众号