参数绑定

实现接口时,通常需要从 http 请求中提取数据,作为方法的输入参数,并将方法的返回值转换成 http 的输出。参数绑定功能即可以帮你完成上述工作。

1. 输入绑定

1.1. 根据方法定义绑定

默认情况下,框架会从http请求中提取和方法的参数名同名的变量,作为函数的参数。比如:

/**
 * @route GET /books/
 */
public function getBooks($offsit, $limit)

上述代码,对应的 http 请求形式为 GET /books/?offsit=0&limit=10。在此默认请求下:

  • 如果路由 uri 中定义了变量,参数将优先选取 uri 变量。如:
/**
 * @route GET /books/{id}
 */
public function getBook($id)

其中 $id 取自 uri。

  • 对于没有 BODY 的 http 请求(GET、HEAD、OPTION、DELETE),参数来自 querystring 。
  • 其他请求(POST、PUT、OPTION),参数先取 querystring,如果没有,再取 BODY。

1.2. @param

如果在方法的注释中,标注了 @param,就会有用 @param 的绑定信息覆盖默认来自函数定义的绑定信息。@param 可以指定变量的类型,而原函数定义中只能在参数是数组或者对象时才能指定类型。@param 的语法为标准 PHP Document 的语法。

/**
 * @route GET /books/
 * @param int $offsit
 * @param int $limit
 */
public function getBooks($offsit, $limit)

以上代码,除了绑定变量外,还指定了变量类型,即如果输入值无法转换成 int,将返回 400 BadRequest 错误。未指定@param 时,参数的类型默认为 mixed。

1.3. 输入对象参数

输入参数除了是原生类型外,还可以是对象(这里我们把只有属性和 get、set 方法的对象,称为实体(Entity))。如:

/**
 * @route POST /books/
 * @param Book $book {@bind request.request} 将$_POST 内容转换成Book实例
 */
public function createBook(Book $bok)

其中 Book 的的定义:

/**
 * 图书信息
 */
class Book
{
    /**
     * @var int
     * @v optional
     */
    public $id;
    /**
     * 书名
     * @var string
     */
    public $name='';

    /**
     * 简介
     * @var string
     * @v lengthMax:200
     */
    public $brief='';

    /**
     * 图片url
     * @var string[]
     */
    public $pictures=[];
}

框架对 http 请求到实体的转换,有一套自己的逻辑:

  • @var 指定属性的类型,如果类型不匹配,实例化将抛出 InvalidArgumentException 异常
  • 如果不标注 @var,则默认类型为mixed
  • 如果属性有默认值,表示此属性可选,否则认为此属性必选
  • 支持 @v 定义校验规则
  • 实体可以嵌套

1.4. 参数默认值

如果想指定某个输入参数可选,只需给方法参数设置一个默认值。比如:

/**
 * @route GET /books/
 * @param int $offsit
 * @param int $limit
 */
public function getBooks($offsit=0, $limit=10)

注意:php 方法的默认参数, 必须放在方法的最后

2. 输出绑定

2.1. 绑定return

默认情况下,函数的返回值将 jsonencode 后,作为 body 输出。如

/**
 * @route GET /books/{id}
 */
public function getBook($id)
{
    return ['name'=>'PhpBook', 'desc'=>'PhpBook Document'];
}

curl 请求将得到以下结果

$ curl "http://localhost/books/1"
{
    "name": "PhpBook",
    "desc": "PhpBook Document"
}

注意,这里为便于演示,直接在方法中返回了数组(其实这在其他语言里算对象),但你应该为这种返回定义一个类,首先,有很多改善代码质量的理由鼓励使用对象替代这类数组,其次在自动生成文档时,这类数组无发被结构化描述。

2.2. 绑定引用参数

如果方法的参数是引用类型,则这个参数将不会从请求中获取,而是作为输出。比如:

/**
 * @route GET /books/
 * @param int $offsit
 * @param int $limit
 * @return Books[]
 */
public function getBooks($offsit=0, $limit=10, &$total)
{
    $total = 1;
    return [new Books()];
}

curl 请求将得到以下结果

$ curl "http://localhost/books"
{
    "total": 1,
    "data": [
        {
            "name":null, 
            "desc":null
        }
    ]
}

可以看到,$total 输出到了 http body 中。 注意:当接口存在引用参数时,接口的返回值将会被默认绑定到response.content.data,效果和声明{@bind response.content.data}一致。

3. @bind

通过@bind,可以改变默认的绑定关系,将参数与其他输入项绑定,如:

/**
 * @route GET /books/
 * @return Books[] {@bind response.content.books}
 */
public function getBooks($offsit=0, $limit=10, &$total)

表示将返回绑定到响应 body 的 books 变量(响应默认是 json)。

3.1. 绑定输入

  • 请求Body: request.request
  • Query String: request.query
  • Cookie: request.cookies
  • **请求Header:**request.headers
  • **文件:**request.files

3.2. 绑定输出

  • 响应Body: response.content
  • Cookie: response.cookies
  • **请求Header:**response.headers