阿江 2017-10-15 08:31:12 3390次浏览 0条回复 1 0 0

说明

学习Yii Framework 2易2框架的过程是漫长的也是充满乐趣的以下是我学习Yii2框架时对官网英文资料(请参见原文网址)的翻译和代码实现提供了较完整的代码供你参考不妥之处请多多指正

原文网址:

http://www.yiiframework.com/doc-2.0/guide-rest-resources.html

本文主题: RESTful的资源(Resources)

RESTful API所有都是与获取和处理资源(resources)相关的,你可以在MVC范例中将资源视为模型(Model): 尽管对于如何定义一个资源没有严格的定义,但在Yii中通常以yii\base\Model或其子类(如yii\db\ActiveRecord)的形式表示资源,原因如下: 1)yii\base\Model实现了yii\base\Arrayable接口,这样允许你通过RESTful API自定义展现方式。 2)yii\base\Model支持输入验证,如果你的RESTful API需要支持数据输入,此功能是很有用的。 3)yii\base\ActiveRecord提供了强大的DB数据获取和操作支持,如果你的资源是存储在数据库中,使用它是最好的。

在本章节中,我们将主要描述yii\base\Model扩展的资源类如何定义通过RESTful API返回的数据。如果此资源类没有扩展自yii\base\Model,那么它的所有公共成员属性都将被返回。

1、Fields(字段)

当RESTful API中包含一个资源时,此资源需要序列化(serialize)为一个字符串。Yii将此过程分为两步:第一步,使用yii\rest\Serializer将此资源转为一个数组;第二步,此数组通过响应格式化程序以请求的格式(如JSON、XML)序列化成一个字符串。当开发一个资源类时需要你主要关注的是第一步。

当重写fields()或extraFields()方法时,你可以定义资源中的哪些数据(被称为字段fields)被加入到它所表示的数组中去。两个方法的不同之处是前者定义将要被包含进表示数组的默认字段集,后者定义包含进数组的附加字段(additional fields),附加字段需要用户使用expand查询参数来请求,例如:

//返回fields()中声明的所有字段
http://localhost/users

//仅返回fields()中声明的id和mail字段
http://localhost/users?fields=id,email

//返回fields()中的所有字段和extraFields()中定义的profile字段
http://localhost/users?expand=profile

//仅返回fields()中定义的id、email字段和extraFields()中定义的profile字段
http://localhost/users?fields=id,email&expand=profile

默认情况下,yii\base\Model::fields()返回模型的所有属性作为字段,而yii\db\ActiveRecord::fields()仅返回从DB中填充的属性。

你可以重写fields()方法去添加、删除、重命名、或重定义字段,fields()的返回值是一个数组。数组键名是字段名称,数组值是对应的字段定义(field definitions),此字段定义可以是属性(property/attribute)名称,也可以是返回对应字段值的匿名函数。特殊情况下,当一个字段名与属性名称相同时,你可以省略数组键名,例如:

//应明确列出每个字段,这样在更改DB表或模型属性时不用修改你的字段(API向后兼容)
public function fields()
{
    return [
		//字段名与属性名相同
        'id',
		//字段名是"email",对应的属性名称是"email_address"
        'email' => 'email_address',
		//字段名称是是"name",它的值由一个PHP回调函数定义
        'name' => function ($model) {
            return $model->first_name . ' ' . $model->last_name;
        },
    ];
}

//过滤一些字段,常用于继承父类的实现,并屏蔽一些敏感字段
public function fields()
{
    $fields = parent::fields();
	//移去包含敏感信息的字段
    unset($fields['auth_key'], $fields['password_hash'], $fields['password_reset_token']);
    return $fields;
}

警告:因为默认情况下模型的所有属性将被包含到API的结果集中,你应检查数据以确保它们不包含敏感信息。如果存在此类信息,你应重写field()将它们过滤掉,在上例中,我们过滤掉了auth_key、password_hash、password_reset_token属性。

实例:url中可以区分大小写:
http://localhost:8082/user/5?fields=id,email,createdAt
返回结果:
<response>
<id>5</id>
<email>allaa@163.net</email>
<createdAt>1461664500</createdAt>
</response>
//错误的写法:createdat没有正确列出相应字段值
http://localhost:8082/user/5?fields=id,email,createdat
返回结果:
<response>
<id>5</id>
<email>allaa@163.net</email>
</response>

D:\phpwork\advanced\frontend\models\User.php

    public function fields() {
        return [
            'id','username','email',
            'createdAt'=>'created_at',
            'updated_at',
        ];
    }
Overriding extraFields(),重写extraFields()方法

默认情况下,yii\base\Model::extraFields()将返回一个空数组,yii\db\ActiveRecord::extraFields()返回从DB填充的关联属性名称。

extraFields()返回的数据格式与fields()相同,通常extraFields()主要用于定义对象类型的字段,例如,给定如下的字段定义:

public function fields(){
    return ['id', 'email'];
}
public function extraFields(){
    return ['profile'];
}

请求http://localhost/users?fields=id,email&expand=profile可以返回如下的JSON数据:

[
    {
        "id": 100,
        "email": "100@example.com",
        "profile": {
            "id": 100,
            "age": 30,
        }
    },
    ...
]
实例,返回extraFields

D:\phpwork\advanced\frontend\models\User.php

    public function extraFields() {
        return [
            'goods'=>function(){
                return ['1',2,3];
            },
        ];
    }
测试结果:
http://localhost:8082/user/5?fields=id,email,createdAt,status&expand=goods
/*
<response>
	<id>5</id>
	<email>allaa@163.net</email>
	<createdAt>1461664500</createdAt>
	<status>4</status>
	<goods>
		<item>1</item>
		<item>2</item>
		<item>3</item>
	</goods>
</response>
*/
2、Links

HATEOAS是Hypermedia as the Engine of Application State(应用状态引擎的超媒体)的缩写,RESTful API允许客户端查看取回资源的各种方法,这可以通过HATEOAS返回信息。 关键字HATEOAS将返回API提供的数据资源关联信息的一组超链接。

你的资源类可以通过实现yii\web\Linkable接口以支持HATEOAS,这个接口只包含一个方法getLinks(),它将返回连接的列表。尤其是你应该返回self连接,它表示资源对象自身的URL,例如:

use yii\base\Model;
use yii\web\Link; //代表一个使用JSON Hypermedia API语言定义的连接对象
use yii\web\Linkable;
use yii\helpers\Url;

class UserResource extends Model implements Linkable
{
    public $id;
    public $email;

    //...

    public function fields()
    {
        return ['id', 'email'];
    }

    public function extraFields()
    {
        return ['profile'];
    }

    public function getLinks()
    {
        return [
            Link::REL_SELF => Url::to(['user/view', 'id' => $this->id], true),
            'edit' => Url::to(['user/view', 'id' => $this->id], true),
            'profile' => Url::to(['user/profile/view', 'id' => $this->id], true),
            'index' => Url::to(['users'], true),
        ];
    }
}

当一个UserResource对象被返回时将包含一个_links元素,它里面包含了与用户相关的连接,例如:

{
    "id": 100,
    "email": "user@example.com",
    // ...
    "_links" => {
        "self": {
            "href": "https://example.com/users/100"
        },
        "edit": {
            "href": "https://example.com/users/100"
        },
        "profile": {
            "href": "https://example.com/users/profile/100"
        },
        "index": {
            "href": "https://example.com/users"
        }
    }
}
实例,实现Linkable接口:

D:\phpwork\advanced\frontend\models\User.php

namespace frontend\models;
use Yii;
use yii\web\Link;
use yii\web\Linkable;
use yii\helpers\Url;
class User extends \yii\db\ActiveRecord implements Linkable
{
    public function getLinks()
    {
        return [
            Link::REL_SELF => Url::to(['user/view', 'id' => $this->id], true),
            'edit' => Url::to(['user/view', 'id' => $this->id], true),
            'profile' => Url::to(['user/profile/view', 'id' => $this->id], true),
            'index' => Url::to(['users'], true),
        ];
    }
	......
}
测试结果:
http://localhost:8082/user/5?fields=id,email,createdAt,status&expand=goods
/*
<response>
	<id>5</id>
	<email>allaa@163.net</email>
	<createdAt>1461664500</createdAt>
	<status>4</status>
	<goods>
		<item>1</item>
		<item>2</item>
		<item>3</item>
	</goods>
	<_links>
		<self>
			<href>http://localhost:8082/user/5</href>
		</self>
		<edit>
			<href>http://localhost:8082/user/5</href>
		</edit>
		<profile>
			<href>http://localhost:8082/user/profile/view?id=5</href>
		</profile>
		<index>
			<href>http://localhost:8082/user/users</href>
		</index>
	</_links>
</response>
*/
3、Collections,集合

资源可以被分组为集合,每个集合包含一个相同类型的资源对象的列表。 虽然集合可以用数组来表示,但实际上它们更多的使用数据提供器(data provider)来表示,这是因为数据提供器支持对资源的排序和分页,这对于RESTful API返回集合来说是个常用特征。例如,以下动作返回一个post资源的数据提供器:

namespace app\controllers;
use yii\rest\Controller;
use yii\data\ActiveDataProvider;
use app\models\Post;
class PostController extends Controller
{
    public function actionIndex()
    {
        return new ActiveDataProvider([
            'query' => Post::find(),
        ]);
    }
}

当一个数据提供器被RESTful API响应发送时,yii\rest\Serializer将找出当前页的资源,并将这些资源对象序列化为一个数组,另外,yii\rest\Serializer将通过以下HTTP头包含分页信息: X-Pagination-Total-Count: 资源的总数 X-Pagination-Page-Count: 分页总数 X-Pagination-Current-Page: 当前页(从1起) X-Pagination-Per-Page: 每页中包含的资源数 Link: 一组导航连接,这样可以允许客户一页一页的浏览资源

一个例子可以在Quick Start部分找到: http://www.yiiframework.com/doc-2.0/guide-rest-quick-start.html#trying-it-out

实例,使用集合,更改每页返回的记录数量

//查看“更改数据提供器的方法”更为科学一些,因本例中将会丢失'index'的checkAccess属性

namespace app\controllers;
use yii\data\ActiveDataProvider;
use yii\rest\ActiveController;
use app\models\Users;
class UserController extends  ActiveController{
    public $modelClass='app\models\Users';
    public function actions(){
        $actions = parent::actions();
        unset($actions['index']);
        return $actions;
    }

    public function actionIndex(){
        $activeData = new ActiveDataProvider([
            'query' => Users::find(),
            'pagination' => [
                'defaultPageSize' => 2,
            ],
        ]);
        return $activeData;
    }
}

//实例,更改数据提供器的方法 D:\phpwork\api\controllers\UserController.php

namespace app\controllers;
use yii\data\ActiveDataProvider;
use yii\rest\ActiveController;
use app\models\Users;
class UserController extends  ActiveController{
    public $modelClass='app\models\Users';
    public function actions() {
        $actions=parent::actions();
        $actions['index']['prepareDataProvider']=[$this,'prepareDataProvider'];
        return $actions;
    }
    public function prepareDataProvider(){
        return new ActiveDataProvider([
            'query'=>Users::find(),
            'pagination'=>[
                'defaultPageSize'=>2,
            ],
        ]);
    }
}

(全文完)

    没有找到数据。
您需要登录后才可以回复。登录 | 立即注册