diff --git a/apps/advanced/common/models/User.php b/apps/advanced/common/models/User.php
index fc74532..d2c80c0 100644
--- a/apps/advanced/common/models/User.php
+++ b/apps/advanced/common/models/User.php
@@ -1,6 +1,7 @@
 <?php
 namespace common\models;
 
+use yii\base\NotSupportedException;
 use yii\db\ActiveRecord;
 use yii\helpers\Security;
 use yii\web\IdentityInterface;
@@ -72,6 +73,14 @@ class User extends ActiveRecord implements IdentityInterface
 	}
 
 	/**
+	 * @inheritdoc
+	 */
+	public static function findIdentityByAccessToken($token)
+	{
+		throw new NotSupportedException('"findIdentityByAccessToken" is not implemented.');
+	}
+
+	/**
 	 * Finds user by username
 	 *
 	 * @param string $username
diff --git a/apps/basic/models/User.php b/apps/basic/models/User.php
index b890e69..7a6fcc0 100644
--- a/apps/basic/models/User.php
+++ b/apps/basic/models/User.php
@@ -8,6 +8,7 @@ class User extends \yii\base\Object implements \yii\web\IdentityInterface
 	public $username;
 	public $password;
 	public $authKey;
+	public $accessToken;
 
 	private static $users = [
 		'100' => [
@@ -15,12 +16,14 @@ class User extends \yii\base\Object implements \yii\web\IdentityInterface
 			'username' => 'admin',
 			'password' => 'admin',
 			'authKey' => 'test100key',
+			'accessToken' => '100-token',
 		],
 		'101' => [
 			'id' => '101',
 			'username' => 'demo',
 			'password' => 'demo',
 			'authKey' => 'test101key',
+			'accessToken' => '101-token',
 		],
 	];
 
@@ -33,6 +36,19 @@ class User extends \yii\base\Object implements \yii\web\IdentityInterface
 	}
 
 	/**
+	 * @inheritdoc
+	 */
+	public static function findIdentityByAccessToken($token)
+	{
+		foreach (self::$users as $user) {
+			if ($user['accessToken'] === $token) {
+				return new static($user);
+			}
+		}
+		return null;
+	}
+
+	/**
 	 * Finds user by username
 	 *
 	 * @param string $username
diff --git a/docs/guide/authentication.md b/docs/guide/authentication.md
index e7c1d94..f43ff0d 100644
--- a/docs/guide/authentication.md
+++ b/docs/guide/authentication.md
@@ -5,7 +5,7 @@ Authentication is the act of verifying who a user is, and is the basis of the lo
 
 In Yii, this entire process is performed semi-automatically, leaving the developer to merely implement [[yii\web\IdentityInterface]], the most important class in the authentication system. Typically, implementation of `IdentityInterface` is accomplished using the `User` model.
 
-You can find a full featured example of authentication in the
+You can find a fully featured example of authentication in the
 [advanced application template](installation.md). Below, only the interface methods are listed:
 
 ```php
@@ -25,6 +25,17 @@ class User extends ActiveRecord implements IdentityInterface
 	}
 
 	/**
+	 * Finds an identity by the given token.
+	 *
+	 * @param string $token the token to be looked for
+	 * @return IdentityInterface|null the identity object that matches the given token.
+	 */
+	public static function findIdentityByAccessToken($token)
+	{
+		return static::find(['access_token' => $token]);
+	}
+
+	/**
 	 * @return int|string current user ID
 	 */
 	public function getId()
diff --git a/docs/guide/rest.md b/docs/guide/rest.md
new file mode 100644
index 0000000..6fd215f
--- /dev/null
+++ b/docs/guide/rest.md
@@ -0,0 +1,878 @@
+Implementing RESTful Web Service APIs
+=====================================
+
+Yii provides a whole set of tools to greatly simplify the task of implementing RESTful Web Service APIs.
+In particular, Yii provides support for the following aspects regarding RESTful APIs:
+
+* Quick prototyping with support for common APIs for ActiveRecord;
+* Response format (supporting JSON and XML by default) and API version negotiation;
+* Customizable object serialization with support for selectable output fields;
+* Proper formatting of collection data and validation errors;
+* Efficient routing with proper HTTP verb check;
+* Support `OPTIONS` and `HEAD` verbs;
+* Authentication;
+* Authorization;
+* Support for HATEOAS;
+* Caching via `yii\web\HttpCache`;
+* Rate limiting;
+* Searching and filtering: TBD
+* Testing: TBD
+* Automatic generation of API documentation: TBD
+
+
+A Quick Example
+---------------
+
+Let's use a quick example to show how to build a set of RESTful APIs using Yii.
+Assume you want to expose the user data via RESTful APIs. The user data are stored in the user DB table,
+and you have already created the ActiveRecord class `app\models\User` to access the user data.
+
+First, create a controller class `app\controllers\UserController` as follows,
+
+```php
+namespace app\controllers;
+
+use yii\rest\ActiveController;
+
+class UserController extends ActiveController
+{
+    public $modelClass = 'app\models\User';
+}
+```
+
+Then, modify the configuration about the `urlManager` component in your application configuration:
+
+```php
+'urlManager' => [
+    'enablePrettyUrl' => true,
+    'enableStrictParsing' => true,
+    'showScriptName' => false,
+    'rules' => [
+        ['class' => 'yii\rest\UrlRule', 'controller' => 'user'],
+    ],
+]
+```
+
+With the above minimal amount of effort, you have already finished your task of creating the RESTful APIs
+for accessing the user data. The APIs you have created include:
+
+* `GET /users`: list all users page by page;
+* `HEAD /users`: show the overview information of user listing;
+* `POST /users`: create a new user;
+* `GET /users/123`: return the details of the user 123;
+* `HEAD /users/123`: show the overview information of user 123;
+* `PATCH /users/123` and `PUT /users/123`: update the user 123;
+* `DELETE /users/123`: delete the user 123;
+* `OPTIONS /users`: show the supported verbs regarding endpoint `/users`;
+* `OPTIONS /users/123`: show the supported verbs regarding endpoint `/users/123`.
+
+You may access your APIs with the `curl` command like the following,
+
+```
+curl -i -H "Accept:application/json" "http://localhost/users"
+```
+
+which may give the following output:
+
+```
+HTTP/1.1 200 OK
+Date: Sun, 02 Mar 2014 05:31:43 GMT
+Server: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y
+X-Powered-By: PHP/5.4.20
+X-Pagination-Total-Count: 1000
+X-Pagination-Page-Count: 50
+X-Pagination-Current-Page: 1
+X-Pagination-Per-Page: 20
+Link: <http://localhost/users?page=1>; rel=self, 
+      <http://localhost/users?page=2>; rel=next, 
+      <http://localhost/users?page=50>; rel=last
+Transfer-Encoding: chunked
+Content-Type: application/json; charset=UTF-8
+
+[
+    {
+        "id": 1,
+        ...
+    },
+    {
+        "id": 2,
+        ...
+    },
+    ...
+]
+```
+
+Try changing the acceptable content type to be `application/xml`, and you will see the result
+is returned in XML format:
+
+```
+curl -i -H "Accept:application/xml" "http://localhost/users"
+```
+
+```
+HTTP/1.1 200 OK
+Date: Sun, 02 Mar 2014 05:31:43 GMT
+Server: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y
+X-Powered-By: PHP/5.4.20
+X-Pagination-Total-Count: 1000
+X-Pagination-Page-Count: 50
+X-Pagination-Current-Page: 1
+X-Pagination-Per-Page: 20
+Link: <http://localhost/users?page=1>; rel=self, 
+      <http://localhost/users?page=2>; rel=next, 
+      <http://localhost/users?page=50>; rel=last
+Transfer-Encoding: chunked
+Content-Type: application/xml
+
+<?xml version="1.0" encoding="UTF-8"?>
+<response>
+    <item>
+        <id>1</id>
+        ...
+    </item>
+    <item>
+        <id>2</id>
+        ...
+    </item>
+    ...
+</response>
+```
+
+> Tip: You may also access your APIs via Web browser by entering the URL `http://localhost/users`.
+
+As you can see, in the response headers, there are information about the total count, page count, etc.
+There are also links that allow you to navigate to other pages of data. For example, `http://localhost/users?page=2`
+would give you the next page of the user data.
+
+Using the `fields` and `expand` parameters, you may also request to return a subset of the fields in the result.
+For example, the URL `http://localhost/users?fields=id,email` will only return the `id` and `email` fields in the result:
+
+
+> Info: You may have noticed that the result of `http://localhost/users` includes some sensitive fields,
+> such as `password_hash`, `auth_key`. You certainly do not want these to appear in your API result.
+> You can/should filter out these fields as described in the following sections.
+
+
+In the following sections, we will explain in more details about implementing RESTful APIs.
+
+
+General Architecture
+--------------------
+
+Using the Yii RESTful API framework, you implement an API endpoint in terms of a controller action, and you use
+a controller to organize the actions that implement the endpoints for a single type of resource.
+
+Resources are represented as data models which extend from the [[yii\base\Model]] class.
+If you are working with databases (relational or NoSQL), it is recommended you use ActiveRecord to represent resources.
+
+You may use [[yii\rest\UrlRule]] to simplify the routing to your API endpoints.
+
+While not required, it is recommended that you develop your RESTful APIs as an application, separated from
+your Web front end and back end.
+
+
+Creating Resource Classes
+-------------------------
+
+RESTful APIs are all about accessing and manipulating resources. In Yii, a resource can be an object of any class.
+However, if your resource classes extend from [[yii\base\Model]] or its child classes (e.g. [[yii\db\ActiveRecord]]),
+you may enjoy the following benefits:
+
+* Input data validation;
+* Query, create, update and delete data, if extending from [[yii\db\ActiveRecord]];
+* Customizable data formatting (to be explained in the next section).
+
+
+Formatting Response Data
+------------------------
+
+By default, Yii supports two response formats for RESTful APIs: JSON and XML. If you want to support
+other formats, you should configure [[yii\rest\Controller::supportedFormats]] and also [[yii\web\Response::formatters]].
+
+Formatting response data in general involves two steps:
+
+1. The objects (including embedded objects) in the response data are converted into arrays by [[yii\rest\Serializer]];
+2. The array data are converted into different formats (e.g. JSON, XML) by [[yii\web\ResponseFormatterInterface|response formatters]].
+
+Step 2 is usually a very mechanical data conversion process and can be well handled by the built-in response formatters.
+Step 1 involves some major development effort as explained below.
+
+When the [[yii\rest\Serializer|serializer]] converts an object into an array, it will call the `toArray()` method
+of the object if it implements [[yii\base\ArrayableInterface]]. If an object does not implement this interface,
+its public properties will be returned instead.
+
+For classes extending from [[yii\base\Model]] or [[yii\db\ActiveRecord]], besides directly overriding `toArray()`,
+you may also override the `fields()` method and/or the `extraFields()` method to customize the data being returned.
+
+The method [[yii\base\Model::fields()]] declares a set of *fields* that should be included in the result.
+A field is simply a named data item. In a result array, the array keys are the field names, and the array values
+are the corresponding field values. The default implementation of [[yii\base\Model::fields()]] is to return
+all attributes of a model as the output fields; for [[yii\db\ActiveRecord::fields()]], by default it will return
+the names of the attributes whose values have been populated into the object.
+
+You can override the `fields()` method to add, remove, rename or redefine fields. For example,
+
+```php
+// explicitly list every field, best used when you want to make sure the changes
+// in your DB table or model attributes do not cause your field changes (to keep API backward compatibility).
+public function fields()
+{
+    return [
+        // field name is the same as the attribute name
+        'id',
+        // field name is "email", the corresponding attribute name is "email_address"
+        'email' => 'email_address',
+        // field name is "name", its value is defined by a PHP callback
+        'name' => function () {
+            return $this->first_name . ' ' . $this->last_name;
+        },
+    ];
+}
+
+// filter out some fields, best used when you want to inherit the parent implementation
+// and blacklist some sensitive fields.
+public function fields()
+{
+    $fields = parent::fields();
+
+    // remove fields that contain sensitive information
+    unset($fields['auth_key'], $fields['password_hash'], $fields['password_reset_token']);
+
+    return $fields;
+}
+```
+
+The return value of `fields()` should be an array. The array keys are the field names, and the array values
+are the corresponding field definitions which can be either property/attribute names or anonymous functions
+returning the corresponding field values.
+
+> Warning: Because by default all attributes of a model will be included in the API result, you should
+> examine your data to make sure they do not contain sensitive information. If there is such information,
+> you should override `fields()` or `toArray()` to filter them out. In the above example, we choose
+> to filter out `auth_key`, `password_hash` and `password_reset_token`.
+
+You may use the `fields` query parameter to specify which fields in `fields()` should be included in the result.
+If this parameter is not specified, all fields returned by `fields()` will be returned.
+
+The method [[yii\base\Model::extraFields()]] is very similar to [[yii\base\Model::fields()]].
+The difference between these methods is that the latter declares the fields that should be returned by default,
+while the former declares the fields that should only be returned when the user specifies them in the `expand` query parameter.
+
+For example, `http://localhost/users?fields=id,email&expand=profile` may return the following JSON data:
+
+```php
+[
+    {
+        "id": 100,
+        "email": "100@example.com",
+        "profile": {
+            "id": 100,
+            "age": 30,
+        }
+    },
+    ...
+]
+```
+
+You may wonder who triggers the conversion from objects to arrays when an action returns an object or object collection.
+The answer is that this is done by [[yii\rest\Controller::serializer]] in the [[yii\base\Controller::afterAction()|afterAction()]]
+method. By default, [[yii\rest\Serializer]] is used as the serializer that can recognize resource objects extending from
+[[yii\base\Model]] and collection objects implementing [[yii\data\DataProviderInterface]]. The serializer
+will call the `toArray()` method of these objects and pass the `fields` and `expand` user parameters to the method.
+If there are any embedded objects, they will also be converted into arrays recursively.
+
+If all your resource objects are of [[yii\base\Model]] or its child classes, such as [[yii\db\ActiveRecord]],
+and you only use [[yii\data\DataProviderInterface]] as resource collections, the default data formatting
+implementation should work very well. However, if you want to introduce some new resource classes that do not
+extend from [[yii\base\Model]], or if you want to use some new collection classes, you will need to
+customize the serializer class and configure [[yii\rest\Controller::serializer]] to use it.
+You new resource classes may use the trait [[yii\base\ArrayableTrait]] to support selective field output
+as explained above.
+
+
+### Pagination
+
+For API endpoints about resource collections, pagination is supported out-of-box if you use 
+[[yii\data\DataProviderInterface|data provider]] to serve the response data. In particular,
+through query parameters `page` and `per-page`, an API consumer may specify which page of data
+to return and how many data items should be included in each page. The corresponding response
+will include the pagination information by the following HTTP headers (please also refer to the first example
+in this chapter):
+
+* `X-Pagination-Total-Count`: The total number of data items;
+* `X-Pagination-Page-Count`: The number of pages;
+* `X-Pagination-Current-Page`: The current page (1-based);
+* `X-Pagination-Per-Page`: The number of data items in each page;
+* `Link`: A set of navigational links allowing client to traverse the data page by page.
+
+The response body will contain a list of data items in the requested page.
+
+Sometimes, you may want to help simplify the client development work by including pagination information
+directly in the response body. To do so, configure the [[yii\rest\Serializer::collectionEnvelope]] property
+as follows:
+
+```php
+use yii\rest\ActiveController;
+
+class UserController extends ActiveController
+{
+    public $modelClass = 'app\models\User';
+    public $serializer = [
+        'class' => 'yii\rest\Serializer',
+        'collectionEnvelope' => 'items',
+    ];
+}
+```
+
+You may then get the following response for request `http://localhost/users`:
+
+```
+HTTP/1.1 200 OK
+Date: Sun, 02 Mar 2014 05:31:43 GMT
+Server: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y
+X-Powered-By: PHP/5.4.20
+X-Pagination-Total-Count: 1000
+X-Pagination-Page-Count: 50
+X-Pagination-Current-Page: 1
+X-Pagination-Per-Page: 20
+Link: <http://localhost/users?page=1>; rel=self, 
+      <http://localhost/users?page=2>; rel=next, 
+      <http://localhost/users?page=50>; rel=last
+Transfer-Encoding: chunked
+Content-Type: application/json; charset=UTF-8
+
+{
+    "items": [
+        {
+            "id": 1,
+            ...
+        },
+        {
+            "id": 2,
+            ...
+        },
+        ...
+    ],
+    "_links": {
+        "self": "http://localhost/users?page=1", 
+        "next": "http://localhost/users?page=2", 
+        "last": "http://localhost/users?page=50"
+    },
+    "_meta": {
+        "totalCount": 1000,
+        "pageCount": 50,
+        "currentPage": 1,
+        "perPage": 20
+    }
+}
+```
+
+
+### HATEOAS Support
+
+[HATEOAS](http://en.wikipedia.org/wiki/HATEOAS), an abbreviation for Hypermedia as the Engine of Application State,
+promotes that RESTful APIs should return information that allow clients to discover actions supported for the returned
+resources. The key of HATEOAS is to return a set of hyperlinks with relation information when resource data are served
+by APIs.
+
+You may let your model classes to implement the [[yii\web\Linkable]] interface to support HATEOAS. By implementing
+this interface, a class is required to return a list of [[yii\web\Link|links]]. Typically, you should return at least
+the `self` link, for example:
+
+```php
+use yii\db\ActiveRecord;
+use yii\web\Linkable;
+use yii\helpers\Url;
+
+class User extends ActiveRecord implements Linkable
+{
+    public function getLinks()
+    {
+        return [
+            Link::REL_SELF => Url::action(['user', 'id' => $this->id], true),
+        ];
+    }
+}
+```
+
+When a `User` object is returned in a response, it will contain a `_links` element representing the links related
+to the user, for example,
+
+```
+{
+    "id": 100,
+    "email": "user@example.com",
+    ...,
+    "_links" => [
+        "self": "https://example.com/users/100"
+    ]
+}
+```
+
+
+Creating Controllers and Actions
+--------------------------------
+
+So you have the resource data and you have specified how the resource data should be formatted, the next thing
+to do is to create controller actions to expose the resource data to end users.
+
+Yii provides two base controller classes to simplify your work of creating RESTful actions:
+[[yii\rest\Controller]] and [[yii\rest\ActiveController]]. The difference between these two controllers
+is that the latter provides a default set of actions that are specified designed to deal with
+resources represented as ActiveRecord. So if you are using ActiveRecord and you are comfortable with
+the provided built-in actions, you may consider creating your controller class by extending from
+the latter. Otherwise, extending from [[yii\rest\Controller]] will allow you to develop actions
+from scratch.
+
+Both [[yii\rest\Controller]] and [[yii\rest\ActiveController]] provide the following features which will
+be described in detail in the next few sections:
+
+* Response format negotiation;
+* API version negotiation;
+* HTTP method validation;
+* User authentication;
+* Rate limiting.
+
+[[yii\rest\ActiveController]] in addition provides the following features specifically for working
+with ActiveRecord:
+
+* A set of commonly used actions: `index`, `view`, `create`, `update`, `delete`, `options`;
+* User authorization in regard to the requested action and resource.
+
+When creating a new controller class, a convention in naming the controller class is to use
+the type name of the resource and use singular form. For example, to serve user information,
+the controller may be named as `UserController`.
+
+Creating a new action is similar to creating an action for a Web application. The only difference
+is that instead of rendering the result using a view by calling the `render()` method, for RESTful actions
+you directly return the data. The [[yii\rest\Controller::serializer|serializer]] and the
+[[yii\web\Response|response object]] will handle the conversion from the original data to the requested
+format. For example,
+
+```php
+public function actionSearch($keyword)
+{
+    $result = SolrService::search($keyword);
+    return $result;
+}
+```
+
+If your controller class extends from [[yii\rest\ActiveController]], you should set
+its [[yii\rest\ActiveController::modelClass||modelClass]] property to be the name of the resource class
+that you plan to serve through this controller. The class must implement [[yii\db\ActiveRecordInterface]].
+
+With [[yii\rest\ActiveController]], you may want to disable some of the built-in actions or customize them.
+To do so, override the `actions()` method like the following:
+
+```php
+public function actions()
+{
+    $actions = parent::actions();
+
+    // disable the "delete" and "create" actions
+    unset($actions['delete'], $actions['create']);
+
+    // customize the data provider preparation with the "prepareDataProvider()" method
+    $actions['index']['prepareDataProvider'] = [$this, 'prepareDataProvider'];
+
+    return $actions;
+}
+
+public function prepareDataProvider()
+{
+    // prepare and return a data provider for the "index" action
+}
+```
+
+The following list summarizes the built-in actions supported by [[yii\rest\ActiveController]]:
+
+* [[yii\rest\IndexAction|index]]: list resources page by page;
+* [[yii\rest\ViewAction|view]]: return the details of a specified resource;
+* [[yii\rest\CreateAction|create]]: create a new resource;
+* [[yii\rest\UpdateAction|update]]: update an existing resource;
+* [[yii\rest\DeleteAction|delete]]: delete the specified resource;
+* [[yii\rest\OptionsAction|options]]: return the supported HTTP methods.
+
+
+Routing
+-------
+
+With resource and controller classes ready, you can access the resources using the URL like
+`http://localhost/index.php?r=user/create`. As you can see, the format of the URL is the same as that
+for Web applications.
+
+In practice, you usually want to enable pretty URLs and take advantage of HTTP verbs.
+For example, a request `POST /users` would mean accessing the `user/create` action.
+This can be done easily by configuring the `urlManager` application component in the application
+configuration like the following:
+
+```php
+'urlManager' => [
+    'enablePrettyUrl' => true,
+    'enableStrictParsing' => true,
+    'showScriptName' => false,
+    'rules' => [
+        ['class' => 'yii\rest\UrlRule', 'controller' => 'user'],
+    ],
+]
+```
+
+Compared to the URL management for Web applications, the main new thing above is the use of
+[[yii\rest\UrlRule]] for routing RESTful API requests. This special URL rule class will
+create a whole set of child URL rules to support routing and URL creation for the specified controller(s).
+For example, the above code is roughly equivalent to the following rules:
+
+```php
+[
+    'PUT,PATCH users/<id>' => 'user/update',
+    'DELETE users/<id>' => 'user/delete',
+    'GET,HEAD users/<id>' => 'user/view',
+    'POST users' => 'user/create',
+    'GET,HEAD users' => 'user/index',
+    'users/<id>' => 'user/options',
+    'users' => 'user/options',
+]
+```
+
+And the following API endpoints are supported by this rule:
+
+* `GET /users`: list all users page by page;
+* `HEAD /users`: show the overview information of user listing;
+* `POST /users`: create a new user;
+* `GET /users/123`: return the details of the user 123;
+* `HEAD /users/123`: show the overview information of user 123;
+* `PATCH /users/123` and `PUT /users/123`: update the user 123;
+* `DELETE /users/123`: delete the user 123;
+* `OPTIONS /users`: show the supported verbs regarding endpoint `/users`;
+* `OPTIONS /users/123`: show the supported verbs regarding endpoint `/users/123`.
+
+You may configure the `only` and `except` options to explicitly list which actions to support or which
+actions should be disabled, respectively. For example,
+
+```php
+[
+    'class' => 'yii\rest\UrlRule',
+    'controller' => 'user',
+    'except' => ['delete', 'create', 'update'],
+],
+```
+
+You may also configure `patterns` or `extra` to redefine existing patterns or add new patterns supported by this rule.
+For example, to support a new action `search` by the endpoint `GET /users/search`, configure the `extra` option as follows,
+
+```php
+[
+    'class' => 'yii\rest\UrlRule',
+    'controller' => 'user',
+    'extra' => [
+        'GET search' => 'search',
+    ],
+```
+
+You may have noticed that the controller ID `user` appears in plural form as `users` in the endpoints.
+This is because [[yii\rest\UrlRule]] automatically pluralizes controller IDs for them to use in endpoints.
+You may disable this behavior by setting [[yii\rest\UrlRule::pluralize]] to be false, or if you want
+to use some special names you may configure the [[yii\rest\UrlRule::controller]] property.
+
+
+Authentication
+--------------
+
+Unlike Web applications, RESTful APIs should be stateless, which means sessions or cookies should not
+be used. Therefore, each request should come with some sort of authentication credentials because
+the user authentication status may not be maintained by sessions or cookies. A common practice is
+to send a secret access token with each request to authenticate the user. Since an access token
+can be used to uniquely identify and authenticate a user, **the API requests should always be sent
+via HTTPS to prevent from man-in-the-middle (MitM) attacks**.
+
+There are different ways to send an access token:
+
+* [HTTP Basic Auth](http://en.wikipedia.org/wiki/Basic_access_authentication): the access token
+  is sent as the username. This is should only be used when an access token can be safely stored
+  on the API consumer side. For example, the API consumer is a program running on a server.
+* Query parameter: the access token is sent as a query parameter in the API URL, e.g.,
+  `https://example.com/users?access-token=xxxxxxxx`. Because most Web servers will keep query
+  parameters in server logs, this approach should be mainly used to serve `JSONP` requests which
+  cannot use HTTP headers to send access tokens.
+* [OAuth 2](http://oauth.net/2/): the access token is obtained by the consumer from an authorization
+  server and sent to the API server via [HTTP Bearer Tokens](http://tools.ietf.org/html/rfc6750),
+  according to the OAuth2 protocol.
+
+Yii supports all of the above authentication methods and can be further extended to support other methods.
+
+To enable authentication for your APIs, do the following two steps:
+
+1. Configure [[yii\rest\Controller::authMethods]] with the authentication methods you plan to use.
+2. Implement [[yii\web\IdentityInterface::findIdentityByAccessToken()]] in your [[yii\web\User::identityClass|user identity class]].
+
+For example, to enable all three authentication methods explained above, you would configure `authMethods`
+as follows,
+
+```php
+class UserController extends ActiveController
+{
+    public $authMethods = [
+        'yii\rest\HttpBasicAuth',
+        'yii\rest\QueryParamAuth',
+        'yii\rest\HttpBearerAuth',
+    ];
+}
+```
+
+Each element in `authMethods` should be an auth class name or a configuration array. An auth class
+must implement [[yii\rest\AuthInterface]].
+
+Implementation of `findIdentityByAccessToken()` is application specific. For example, in simple scenarios
+when each user can only have one access token, you may store the access token in an `access_token` column
+in the user table. The method can then be readily implemented in the `User` class as follows,
+
+```php
+use yii\db\ActiveRecord;
+use yii\web\IdentityInterface;
+
+class User extends ActiveRecord implements IdentityInterface
+{
+    public static function findIdentityByAccessToken($token)
+    {
+        return static::find(['access_token' => $token]);
+    }
+}
+```
+
+After authentication is enabled as described above, for every API request, the requested controller
+will try to authenticate the user in its `beforeAction()` step.
+
+If authentication succeeds, the controller will perform other checks (such as rate limiting, authorization)
+and then run the action. The authenticated user identity information can be retrieved via `Yii::$app->user->identity`.
+
+If authentication fails, a response with HTTP status 401 will be sent back together with other appropriate headers
+(such as a `WWW-Authenticate` header for HTTP Basic Auth).
+
+
+Authorization
+-------------
+
+After a user is authenticated, you probably want to check if he has the permission to perform the requested
+action for the requested resource. This process is called *authorization* which is covered in detail in
+the [Authorization chapter](authorization.md).
+
+You may use the [[yii\web\AccessControl]] filter and/or the Role-Based Access Control (RBAC) component
+to implementation authorization.
+
+To simplify the authorization check, you may also override the [[yii\rest\Controller::checkAccess()]] method
+and then call this method in places where authorization is needed. By default, the built-in actions provided
+by [[yii\rest\ActiveController]] will call this method when they are about to run.
+
+```php
+/**
+ * Checks the privilege of the current user.
+ *
+ * This method should be overridden to check whether the current user has the privilege
+ * to run the specified action against the specified data model.
+ * If the user does not have access, a [[ForbiddenHttpException]] should be thrown.
+ *
+ * @param string $action the ID of the action to be executed
+ * @param \yii\base\Model $model the model to be accessed. If null, it means no specific model is being accessed.
+ * @param array $params additional parameters
+ * @throws ForbiddenHttpException if the user does not have access
+ */
+public function checkAccess($action, $model = null, $params = [])
+{
+}
+```
+
+
+Rate Limiting
+-------------
+
+To prevent abuse, you should consider adding rate limiting to your APIs. For example, you may limit the API usage
+of each user to be at most 100 API calls within a period of 10 minutes. If too many requests are received from a user
+within the period of the time, a response with status code 429 (meaning Too Many Requests) should be returned.
+
+To enable rate limiting, the [[yii\web\User::identityClass|user identity class]] should implement [[yii\rest\RateLimitInterface]].
+This interface requires implementation of the following three methods:
+
+* `getRateLimit()`: returns the maximum number of allowed requests and the time period, e.g., `[100, 600]` means
+  at most 100 API calls within 600 seconds.
+* `loadAllowance()`: returns the number of remaining requests allowed and the corresponding UNIX timestamp
+  when the rate limit is checked last time.
+* `saveAllowance()`: saves the number of remaining requests allowed and the current UNIX timestamp.
+
+You may use two columns in the user table to record the allowance and timestamp information.
+And `loadAllowance()` and `saveAllowance()` can then be implementation by reading and saving the values
+of the two columns corresponding to the current authenticated user. To improve performance, you may also
+consider storing these information in cache or some NoSQL storage.
+
+Once the identity class implements the required interface, Yii will automatically use the rate limiter
+as specified by [[yii\rest\Controller::rateLimiter]] to perform rate limiting check. The rate limiter
+will thrown a [[yii\web\TooManyRequestsHttpException]] if rate limit is exceeded.
+
+When rate limiting is enabled, every response will be sent with the following HTTP headers containing
+the current rate limiting information:
+
+* `X-Rate-Limit-Limit`: The maximum number of requests allowed with a time period;
+* `X-Rate-Limit-Remaining`: The number of remaining requests in the current time period;
+* `X-Rate-Limit-Reset`: The number of seconds to wait in order to get the maximum number of allowed requests.
+
+
+Error Handling
+--------------
+
+When handling a RESTful API request, if there is an error in the user request or if something unexpected
+happens on the server, you may simply throw an exception to notify the user something wrong happened. 
+If you can identify the cause of the error (e.g. the requested resource does not exist), you should 
+consider throwing an exception with a proper HTTP status code (e.g. [[yii\web\NotFoundHttpException]] 
+representing a 404 HTTP status code). Yii will send the response with the corresponding HTTP status
+code and text. It will also include in the response body the serialized representation of the 
+exception. For example,
+
+```
+HTTP/1.1 404 Not Found
+Date: Sun, 02 Mar 2014 05:31:43 GMT
+Server: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y
+Transfer-Encoding: chunked
+Content-Type: application/json; charset=UTF-8
+
+{
+    "type": "yii\\web\\NotFoundHttpException",
+    "name": "Not Found Exception",
+    "message": "The requested resource was not found.",
+    "code": 0,
+    "status": 404
+}
+```
+
+The following list summarizes the HTTP status code that are used by the Yii REST framework:
+
+* `200`: OK. Everything worked as expected.
+* `201`: A resource was successfully created in response to a `POST` request. The `Location` header
+   contains the URL pointing to the newly created resource.
+* `204`: The request is handled successfully and the response contains no body content (like a `DELETE` request).
+* `304`: Resource was not modified. You can use the cached version.
+* `400`: Bad request. This could be caused by various reasons from the user side, such as invalid JSON
+   data in the request body, invalid action parameters, etc.
+* `401`: Authentication failed.
+* `403`: The authenticated user is not allowed to access the specified API endpoint.
+* `404`: The requested resource does not exist.
+* `405`: Method not allowed. Please check the `Allow` header for allowed HTTP methods.
+* `415`: Unsupported media type. The requested content type or version number is invalid.
+* `422`: Data validation failed (in response to a `POST` request, for example). Please check the response body for detailed error messages.
+* `429`: Too many requests. The request is rejected due to rate limiting.
+* `500`: Internal server error. This could be caused by internal program errors.
+
+
+Versioning
+----------
+
+Your APIs should be versioned. Unlike Web applications which you have full control on both client side and server side
+code, for APIs you usually do not have control of the client code that consumes the APIs. Therefore, backward
+compatibility (BC) of the APIs should be maintained whenever possible, and if some BC-breaking changes must be
+introduced to the APIs, you should bump up the version number. You may refer to [Symantic Versioning](http://semver.org/)
+for more information about designing the version numbers of your APIs.
+
+Regarding how to implement API versioning, a common practice is to embed the version number in the API URLs.
+For example, `http://example.com/v1/users` stands for `/users` API of version 1. Another method of API versioning
+which gains momentum recently is to put version numbers in the HTTP request headers, typically through the `Accept` header,
+like the following:
+
+```
+// via a parameter
+Accept: application/json; version=v1
+// via a vendor content type
+Accept: application/vnd.company.myapp-v1+json
+```
+
+Both methods have pros and cons, and there are a lot of debates about them. Below we describe a practical strategy
+of API versioning that is a kind of mix of these two methods:
+
+* Put each major version of API implementation in a separate module whose ID is the major version number (e.g. `v1`, `v2`).
+  Naturally, the API URLs will contain major version numbers.
+* Within each major version (and thus within the corresponding module), use the `Accept` HTTP request header
+  to determine the minor version number and write conditional code to respond to the minor versions accordingly.
+
+For each module serving a major version, it should include the resource classes and the controller classes
+serving for that specific version. To better separate code responsibility, you may keep a common set of
+base resource and controller classes, and subclass them in each individual version module. Within the subclasses,
+implement the concrete code such as `Model::fields()`. As a result, your code may be organized like the following:
+
+```
+api/
+    common/
+        controllers/
+            UserController.php
+            PostController.php
+        models/
+            User.php
+            Post.php
+    modules/
+        v1/
+            controllers/
+                UserController.php
+                PostController.php
+            models/
+                User.php
+                Post.php
+        v2/
+            controllers/
+                UserController.php
+                PostController.php
+            models/
+                User.php
+                Post.php
+```
+
+Your application configuration would look like:
+
+```php
+return [
+    'modules' => [
+        'v1' => [
+            'basePath' => '@app/modules/v1',
+        ],
+        'v2' => [
+            'basePath' => '@app/modules/v2',
+        ],
+    ],
+    'components' => [
+        'urlManager' => [
+            'enablePrettyUrl' => true,
+            'enableStrictParsing' => true,
+            'showScriptName' => false,
+            'rules' => [
+                ['class' => 'yii\rest\UrlRule', 'controller' => ['v1/user', 'v1/post']],
+                ['class' => 'yii\rest\UrlRule', 'controller' => ['v2/user', 'v2/post']],
+            ],
+        ],
+    ],
+];
+```
+
+As a result, `http://example.com/v1/users` will return the list of users in version 1, while
+`http://example.com/v2/users` will return version 2 users.
+
+Using modules, code for different major versions can be well isolated. And it is still possible
+to reuse code across modules via common base classes and other shared classes.
+
+To deal with minor version numbers, you may take advantage of the content type negotiation
+feature provided by [[yii\rest\Controller]]:
+
+* Specify a list of supported minor versions (within the major version of the containing module)
+  via [[yii\rest\Controller::supportedVersions]].
+* Get the version number by reading [[yii\rest\Controller::version]].
+* In relevant code, such as actions, resource classes, serializers, etc., write conditional
+  code according to the requested minor version number.
+
+Since minor versions require maintaining backward compatibility, hopefully there are not much
+version checks in your code. Otherwise, chances are that you may need to create a new major version.
+
+
+Caching
+-------
+
+
+Documentation
+-------------
+
+Testing
+-------
+
diff --git a/framework/base/ArrayableTrait.php b/framework/base/ArrayableTrait.php
new file mode 100644
index 0000000..f5bc9d6
--- /dev/null
+++ b/framework/base/ArrayableTrait.php
@@ -0,0 +1,163 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\base;
+
+use Yii;
+use yii\helpers\ArrayHelper;
+use yii\web\Link;
+use yii\web\Linkable;
+
+/**
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @since 2.0
+ */
+trait ArrayableTrait
+{
+	/**
+	 * Returns the list of fields that should be returned by default by [[toArray()]] when no specific fields are specified.
+	 *
+	 * A field is a named element in the returned array by [[toArray()]].
+	 *
+	 * This method should return an array of field names or field definitions.
+	 * If the former, the field name will be treated as an object property name whose value will be used
+	 * as the field value. If the latter, the array key should be the field name while the array value should be
+	 * the corresponding field definition which can be either an object property name or a PHP callable
+	 * returning the corresponding field value. The signature of the callable should be:
+	 *
+	 * ```php
+	 * function ($field, $model) {
+	 *     // return field value
+	 * }
+	 * ```
+	 *
+	 * For example, the following code declares four fields:
+	 *
+	 * - `email`: the field name is the same as the property name `email`;
+	 * - `firstName` and `lastName`: the field names are `firstName` and `lastName`, and their
+	 *   values are obtained from the `first_name` and `last_name` properties;
+	 * - `fullName`: the field name is `fullName`. Its value is obtained by concatenating `first_name`
+	 *   and `last_name`.
+	 *
+	 * ```php
+	 * return [
+	 *     'email',
+	 *     'firstName' => 'first_name',
+	 *     'lastName' => 'last_name',
+	 *     'fullName' => function () {
+	 *         return $this->first_name . ' ' . $this->last_name;
+	 *     },
+	 * ];
+	 * ```
+	 *
+	 * In this method, you may also want to return different lists of fields based on some context
+	 * information. For example, depending on the privilege of the current application user,
+	 * you may return different sets of visible fields or filter out some fields.
+	 *
+	 * The default implementation of this method returns the public object member variables.
+	 *
+	 * @return array the list of field names or field definitions.
+	 * @see toArray()
+	 */
+	public function fields()
+	{
+		$fields = array_keys(Yii::getObjectVars($this));
+		return array_combine($fields, $fields);
+	}
+
+	/**
+	 * Returns the list of fields that can be expanded further and returned by [[toArray()]].
+	 *
+	 * This method is similar to [[fields()]] except that the list of fields returned
+	 * by this method are not returned by default by [[toArray()]]. Only when field names
+	 * to be expanded are explicitly specified when calling [[toArray()]], will their values
+	 * be exported.
+	 *
+	 * The default implementation returns an empty array.
+	 *
+	 * You may override this method to return a list of expandable fields based on some context information
+	 * (e.g. the current application user).
+	 *
+	 * @return array the list of expandable field names or field definitions. Please refer
+	 * to [[fields()]] on the format of the return value.
+	 * @see toArray()
+	 * @see fields()
+	 */
+	public function extraFields()
+	{
+		return [];
+	}
+
+	/**
+	 * Converts the model into an array.
+	 *
+	 * This method will first identify which fields to be included in the resulting array by calling [[resolveFields()]].
+	 * It will then turn the model into an array with these fields. If `$recursive` is true,
+	 * any embedded objects will also be converted into arrays.
+	 *
+	 * If the model implements the [[Linkable]] interface, the resulting array will also have a `_link` element
+	 * which refers to a list of links as specified by the interface.
+	 *
+	 * @param array $fields the fields being requested. If empty, all fields as specified by [[fields()]] will be returned.
+	 * @param array $expand the additional fields being requested for exporting. Only fields declared in [[extraFields()]]
+	 * will be considered.
+	 * @param boolean $recursive whether to recursively return array representation of embedded objects.
+	 * @return array the array representation of the object
+	 */
+	public function toArray(array $fields = [], array $expand = [], $recursive = true)
+	{
+		$data = [];
+		foreach ($this->resolveFields($fields, $expand) as $field => $definition) {
+			$data[$field] = is_string($definition) ? $this->$definition : call_user_func($definition, $field, $this);
+		}
+
+		if ($this instanceof Linkable) {
+			$data['_links'] = Link::serialize($this->getLinks());
+		}
+
+		return $recursive ? ArrayHelper::toArray($data) : $data;
+	}
+
+	/**
+	 * Determines which fields can be returned by [[toArray()]].
+	 * This method will check the requested fields against those declared in [[fields()]] and [[extraFields()]]
+	 * to determine which fields can be returned.
+	 * @param array $fields the fields being requested for exporting
+	 * @param array $expand the additional fields being requested for exporting
+	 * @return array the list of fields to be exported. The array keys are the field names, and the array values
+	 * are the corresponding object property names or PHP callables returning the field values.
+	 */
+	protected function resolveFields(array $fields, array $expand)
+	{
+		$result = [];
+
+		foreach ($this->fields() as $field => $definition) {
+			if (is_integer($field)) {
+				$field = $definition;
+			}
+			if (empty($fields) || in_array($field, $fields, true)) {
+				$result[$field] = $definition;
+			}
+		}
+
+		if (empty($expand)) {
+			return $result;
+		}
+
+		foreach ($this->extraFields() as $field => $definition) {
+			if (is_integer($field)) {
+				$field = $definition;
+			}
+			if (in_array($field, $expand, true)) {
+				$result[$field] = $definition;
+			}
+		}
+
+		return $result;
+	}
+}
diff --git a/framework/base/ErrorHandler.php b/framework/base/ErrorHandler.php
index bf1faad..606a795 100644
--- a/framework/base/ErrorHandler.php
+++ b/framework/base/ErrorHandler.php
@@ -93,10 +93,9 @@ class ErrorHandler extends Component
 			return;
 		}
 
-		$useErrorView = !YII_DEBUG || $exception instanceof UserException;
-
 		$response = Yii::$app->getResponse();
-		$response->getHeaders()->removeAll();
+
+		$useErrorView = $response->format === \yii\web\Response::FORMAT_HTML && (!YII_DEBUG || $exception instanceof UserException);
 
 		if ($useErrorView && $this->errorAction !== null) {
 			$result = Yii::$app->runAction($this->errorAction);
@@ -121,7 +120,7 @@ class ErrorHandler extends Component
 				]);
 			}
 		} elseif ($exception instanceof Arrayable) {
-			$response->data = $exception;
+			$response->data = $exception->toArray();
 		} else {
 			$response->data = [
 				'type' => get_class($exception),
diff --git a/framework/base/Model.php b/framework/base/Model.php
index 8ec6e40..8159bb9 100644
--- a/framework/base/Model.php
+++ b/framework/base/Model.php
@@ -13,9 +13,12 @@ use ArrayObject;
 use ArrayIterator;
 use ReflectionClass;
 use IteratorAggregate;
+use yii\helpers\ArrayHelper;
 use yii\helpers\Inflector;
 use yii\validators\RequiredValidator;
 use yii\validators\Validator;
+use yii\web\Link;
+use yii\web\Linkable;
 
 /**
  * Model is the base class for data models.
@@ -54,11 +57,12 @@ use yii\validators\Validator;
  */
 class Model extends Component implements IteratorAggregate, ArrayAccess, Arrayable
 {
+	use ArrayableTrait;
+
 	/**
 	 * The name of the default scenario.
 	 */
 	const SCENARIO_DEFAULT = 'default';
-
 	/**
 	 * @event ModelEvent an event raised at the beginning of [[validate()]]. You may set
 	 * [[ModelEvent::isValid]] to be false to stop the validation.
@@ -516,7 +520,8 @@ class Model extends Component implements IteratorAggregate, ArrayAccess, Arrayab
 
 	/**
 	 * Returns the first error of every attribute in the model.
-	 * @return array the first errors. An empty array will be returned if there is no error.
+	 * @return array the first errors. The array keys are the attribute names, and the array
+	 * values are the corresponding error messages. An empty array will be returned if there is no error.
 	 * @see getErrors()
 	 * @see getFirstError()
 	 */
@@ -526,13 +531,13 @@ class Model extends Component implements IteratorAggregate, ArrayAccess, Arrayab
 			return [];
 		} else {
 			$errors = [];
-			foreach ($this->_errors as $attributeErrors) {
-				if (isset($attributeErrors[0])) {
-					$errors[] = $attributeErrors[0];
+			foreach ($this->_errors as $name => $es) {
+				if (!empty($es)) {
+					$errors[$name] = reset($es);
 				}
 			}
+			return $errors;
 		}
-		return $errors;
 	}
 
 	/**
@@ -789,13 +794,92 @@ class Model extends Component implements IteratorAggregate, ArrayAccess, Arrayab
 	}
 
 	/**
-	 * Converts the object into an array.
-	 * The default implementation will return [[attributes]].
-	 * @return array the array representation of the object
+	 * Returns the list of fields that should be returned by default by [[toArray()]] when no specific fields are specified.
+	 *
+	 * A field is a named element in the returned array by [[toArray()]].
+	 *
+	 * This method should return an array of field names or field definitions.
+	 * If the former, the field name will be treated as an object property name whose value will be used
+	 * as the field value. If the latter, the array key should be the field name while the array value should be
+	 * the corresponding field definition which can be either an object property name or a PHP callable
+	 * returning the corresponding field value. The signature of the callable should be:
+	 *
+	 * ```php
+	 * function ($field, $model) {
+	 *     // return field value
+	 * }
+	 * ```
+	 *
+	 * For example, the following code declares four fields:
+	 *
+	 * - `email`: the field name is the same as the property name `email`;
+	 * - `firstName` and `lastName`: the field names are `firstName` and `lastName`, and their
+	 *   values are obtained from the `first_name` and `last_name` properties;
+	 * - `fullName`: the field name is `fullName`. Its value is obtained by concatenating `first_name`
+	 *   and `last_name`.
+	 *
+	 * ```php
+	 * return [
+	 *     'email',
+	 *     'firstName' => 'first_name',
+	 *     'lastName' => 'last_name',
+	 *     'fullName' => function () {
+	 *         return $this->first_name . ' ' . $this->last_name;
+	 *     },
+	 * ];
+	 * ```
+	 *
+	 * In this method, you may also want to return different lists of fields based on some context
+	 * information. For example, depending on [[scenario]] or the privilege of the current application user,
+	 * you may return different sets of visible fields or filter out some fields.
+	 *
+	 * The default implementation of this method returns [[attributes()]] indexed by the same attribute names.
+	 *
+	 * @return array the list of field names or field definitions.
+	 * @see toArray()
+	 */
+	public function fields()
+	{
+		$fields = $this->attributes();
+		return array_combine($fields, $fields);
+	}
+
+	/**
+	 * Determines which fields can be returned by [[toArray()]].
+	 * This method will check the requested fields against those declared in [[fields()]] and [[extraFields()]]
+	 * to determine which fields can be returned.
+	 * @param array $fields the fields being requested for exporting
+	 * @param array $expand the additional fields being requested for exporting
+	 * @return array the list of fields to be exported. The array keys are the field names, and the array values
+	 * are the corresponding object property names or PHP callables returning the field values.
 	 */
-	public function toArray()
+	protected function resolveFields(array $fields, array $expand)
 	{
-		return $this->getAttributes();
+		$result = [];
+
+		foreach ($this->fields() as $field => $definition) {
+			if (is_integer($field)) {
+				$field = $definition;
+			}
+			if (empty($fields) || in_array($field, $fields, true)) {
+				$result[$field] = $definition;
+			}
+		}
+
+		if (empty($expand)) {
+			return $result;
+		}
+
+		foreach ($this->extraFields() as $field => $definition) {
+			if (is_integer($field)) {
+				$field = $definition;
+			}
+			if (in_array($field, $expand, true)) {
+				$result[$field] = $definition;
+			}
+		}
+
+		return $result;
 	}
 
 	/**
diff --git a/framework/base/Module.php b/framework/base/Module.php
index ab5a516..cba919d 100644
--- a/framework/base/Module.php
+++ b/framework/base/Module.php
@@ -359,6 +359,9 @@ class Module extends Component
 				return $this->_modules[$id];
 			} elseif ($load) {
 				Yii::trace("Loading module: $id", __METHOD__);
+				if (is_array($this->_modules[$id]) && !isset($this->_modules[$id]['class'])) {
+					$this->_modules[$id]['class'] = 'yii\base\Module';
+				}
 				return $this->_modules[$id] = Yii::createObject($this->_modules[$id], $id, $this);
 			}
 		}
diff --git a/framework/data/Pagination.php b/framework/data/Pagination.php
index c2c06d1..29bedb7 100644
--- a/framework/data/Pagination.php
+++ b/framework/data/Pagination.php
@@ -8,7 +8,10 @@
 namespace yii\data;
 
 use Yii;
+use yii\base\Arrayable;
 use yii\base\Object;
+use yii\web\Link;
+use yii\web\Linkable;
 use yii\web\Request;
 
 /**
@@ -65,9 +68,8 @@ use yii\web\Request;
  * @author Qiang Xue <qiang.xue@gmail.com>
  * @since 2.0
  */
-class Pagination extends Object
+class Pagination extends Object implements Linkable, Arrayable
 {
-	const LINK_SELF = 'self';
 	const LINK_NEXT = 'next';
 	const LINK_PREV = 'prev';
 	const LINK_FIRST = 'first';
@@ -301,7 +303,7 @@ class Pagination extends Object
 		$currentPage = $this->getPage();
 		$pageCount = $this->getPageCount();
 		$links = [
-			self::LINK_SELF => $this->createUrl($currentPage, $absolute),
+			Link::REL_SELF => $this->createUrl($currentPage, $absolute),
 		];
 		if ($currentPage > 0) {
 			$links[self::LINK_FIRST] = $this->createUrl(0, $absolute);
@@ -315,6 +317,19 @@ class Pagination extends Object
 	}
 
 	/**
+	 * @inheritdoc
+	 */
+	public function toArray()
+	{
+		return [
+			'totalCount' => $this->totalCount,
+			'pageCount' => $this->getPageCount(),
+			'currentPage' => $this->getPage(),
+			'perPage' => $this->getPageSize(),
+		];
+	}
+
+	/**
 	 * Returns the value of the specified query parameter.
 	 * This method returns the named parameter value from [[params]]. Null is returned if the value does not exist.
 	 * @param string $name the parameter name
diff --git a/framework/db/BaseActiveRecord.php b/framework/db/BaseActiveRecord.php
index afca94c..69bc1da 100644
--- a/framework/db/BaseActiveRecord.php
+++ b/framework/db/BaseActiveRecord.php
@@ -1347,4 +1347,26 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface
 
 		return $this->generateAttributeLabel($attribute);
 	}
+
+	/**
+	 * @inheritdoc
+	 *
+	 * The default implementation returns the names of the columns whose values have been populated into this record.
+	 */
+	public function fields()
+	{
+		$fields = array_keys($this->_attributes);
+		return array_combine($fields, $fields);
+	}
+
+	/**
+	 * @inheritdoc
+	 *
+	 * The default implementation returns the names of the relations that have been populated into this record.
+	 */
+	public function extraFields()
+	{
+		$fields = array_keys($this->getRelatedRecords());
+		return array_combine($fields, $fields);
+	}
 }
diff --git a/framework/helpers/BaseArrayHelper.php b/framework/helpers/BaseArrayHelper.php
index 0177494..278eaf0 100644
--- a/framework/helpers/BaseArrayHelper.php
+++ b/framework/helpers/BaseArrayHelper.php
@@ -58,35 +58,42 @@ class BaseArrayHelper
 	 */
 	public static function toArray($object, $properties = [], $recursive = true)
 	{
-		if (!empty($properties) && is_object($object)) {
-			$className = get_class($object);
-			if (!empty($properties[$className])) {
-				$result = [];
-				foreach ($properties[$className] as $key => $name) {
-					if (is_int($key)) {
-						$result[$name] = $object->$name;
-					} else {
-						$result[$key] = static::getValue($object, $name);
+		if (is_array($object)) {
+			if ($recursive) {
+				foreach ($object as $key => $value) {
+					if (is_array($value) || is_object($value)) {
+						$object[$key] = static::toArray($value, true);
 					}
 				}
-				return $result;
 			}
-		}
-		if ($object instanceof Arrayable) {
-			$object = $object->toArray();
-			if (!$recursive) {
-				return $object;
+			return $object;
+		} elseif (is_object($object)) {
+			if (!empty($properties)) {
+				$className = get_class($object);
+				if (!empty($properties[$className])) {
+					$result = [];
+					foreach ($properties[$className] as $key => $name) {
+						if (is_int($key)) {
+							$result[$name] = $object->$name;
+						} else {
+							$result[$key] = static::getValue($object, $name);
+						}
+					}
+					return $recursive ? static::toArray($result) : $result;
+				}
 			}
-		}
-		$result = [];
-		foreach ($object as $key => $value) {
-			if ($recursive && (is_array($value) || is_object($value))) {
-				$result[$key] = static::toArray($value, $properties, true);
+			if ($object instanceof Arrayable) {
+				$result = $object->toArray();
 			} else {
-				$result[$key] = $value;
+				$result = [];
+				foreach ($object as $key => $value) {
+					$result[$key] = $value;
+				}
 			}
+			return $recursive ? static::toArray($result) : $result;
+		} else {
+			return [$object];
 		}
-		return $result;
 	}
 
 	/**
diff --git a/framework/rest/Action.php b/framework/rest/Action.php
new file mode 100644
index 0000000..1a98539
--- /dev/null
+++ b/framework/rest/Action.php
@@ -0,0 +1,106 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\rest;
+
+use Yii;
+use yii\base\InvalidConfigException;
+use yii\db\ActiveRecordInterface;
+use yii\web\NotFoundHttpException;
+
+/**
+ * Action is the base class for action classes that implement RESTful API.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @since 2.0
+ */
+class Action extends \yii\base\Action
+{
+	/**
+	 * @var string class name of the model which will be handled by this action.
+	 * The model class must implement [[ActiveRecordInterface]].
+	 * This property must be set.
+	 */
+	public $modelClass;
+	/**
+	 * @var callable a PHP callable that will be called to return the model corresponding
+	 * to the specified primary key value. If not set, [[findModel()]] will be used instead.
+	 * The signature of the callable should be:
+	 *
+	 * ```php
+	 * function ($id, $action) {
+	 *     // $id is the primary key value. If composite primary key, the key values
+	 *     // will be separated by comma.
+	 *     // $action is the action object currently running
+	 * }
+	 * ```
+	 *
+	 * The callable should return the model found, or throw an exception if not found.
+	 */
+	public $findModel;
+	/**
+	 * @var callable a PHP callable that will be called when running an action to determine
+	 * if the current user has the permission to execute the action. If not set, the access
+	 * check will not be performed. The signature of the callable should be as follows,
+	 *
+	 * ```php
+	 * function ($action, $model = null) {
+	 *     // $model is the requested model instance.
+	 *     // If null, it means no specific model (e.g. IndexAction)
+	 * }
+	 * ```
+	 */
+	public $checkAccess;
+
+
+	/**
+	 * @inheritdoc
+	 */
+	public function init()
+	{
+		if ($this->modelClass === null) {
+			throw new InvalidConfigException(get_class($this) . '::$modelClass must be set.');
+		}
+	}
+
+	/**
+	 * Returns the data model based on the primary key given.
+	 * If the data model is not found, a 404 HTTP exception will be raised.
+	 * @param string $id the ID of the model to be loaded. If the model has a composite primary key,
+	 * the ID must be a string of the primary key values separated by commas.
+	 * The order of the primary key values should follow that returned by the `primaryKey()` method
+	 * of the model.
+	 * @return ActiveRecordInterface the model found
+	 * @throws NotFoundHttpException if the model cannot be found
+	 */
+	public function findModel($id)
+	{
+		if ($this->findModel !== null) {
+			return call_user_func($this->findModel, $id, $this);
+		}
+
+		/**
+		 * @var ActiveRecordInterface $modelClass
+		 */
+		$modelClass = $this->modelClass;
+		$keys = $modelClass::primaryKey();
+		if (count($keys) > 1) {
+			$values = explode(',', $id);
+			if (count($keys) === count($values)) {
+				$model = $modelClass::find(array_combine($keys, $values));
+			}
+		} elseif ($id !== null) {
+			$model = $modelClass::find($id);
+		}
+
+		if (isset($model)) {
+			return $model;
+		} else {
+			throw new NotFoundHttpException("Object not found: $id");
+		}
+	}
+}
diff --git a/framework/rest/ActiveController.php b/framework/rest/ActiveController.php
new file mode 100644
index 0000000..75a4f55
--- /dev/null
+++ b/framework/rest/ActiveController.php
@@ -0,0 +1,126 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\rest;
+
+use yii\base\InvalidConfigException;
+use yii\base\Model;
+
+/**
+ * ActiveController implements a common set of actions for supporting RESTful access to ActiveRecord.
+ *
+ * The class of the ActiveRecord should be specified via [[modelClass]], which must implement [[\yii\db\ActiveRecordInterface]].
+ * By default, the following actions are supported:
+ *
+ * - `index`: list of models
+ * - `view`: return the details of a model
+ * - `create`: create a new model
+ * - `update`: update an existing model
+ * - `delete`: delete an existing model
+ * - `options`: return the allowed HTTP methods
+ *
+ * You may disable some of these actions by overriding [[actions()]] and unsetting the corresponding actions.
+ *
+ * To add a new action, either override [[actions()]] by appending a new action class or write a new action method.
+ * Make sure you also override [[verbs()]] to properly declare what HTTP methods are allowed by the new action.
+ *
+ * You should usually override [[checkAccess()]] to check whether the current user has the privilege to perform
+ * the specified action against the specified model.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @since 2.0
+ */
+class ActiveController extends Controller
+{
+	/**
+	 * @var string the model class name. This property must be set.
+	 */
+	public $modelClass;
+	/**
+	 * @var string the scenario used for updating a model.
+	 * @see \yii\base\Model::scenarios()
+	 */
+	public $updateScenario = Model::SCENARIO_DEFAULT;
+	/**
+	 * @var string the scenario used for creating a model.
+	 * @see \yii\base\Model::scenarios()
+	 */
+	public $createScenario = Model::SCENARIO_DEFAULT;
+	/**
+	 * @var boolean whether to use a DB transaction when creating, updating or deleting a model.
+	 * This property is only useful for relational database.
+	 */
+	public $transactional = true;
+
+
+	/**
+	 * @inheritdoc
+	 */
+	public function init()
+	{
+		parent::init();
+		if ($this->modelClass === null) {
+			throw new InvalidConfigException('The "modelClass" property must be set.');
+		}
+	}
+
+	/**
+	 * @inheritdoc
+	 */
+	public function actions()
+	{
+		return [
+			'index' => [
+				'class' => 'yii\rest\IndexAction',
+				'modelClass' => $this->modelClass,
+				'checkAccess' => [$this, 'checkAccess'],
+			],
+			'view' => [
+				'class' => 'yii\rest\ViewAction',
+				'modelClass' => $this->modelClass,
+				'checkAccess' => [$this, 'checkAccess'],
+			],
+			'create' => [
+				'class' => 'yii\rest\CreateAction',
+				'modelClass' => $this->modelClass,
+				'checkAccess' => [$this, 'checkAccess'],
+				'scenario' => $this->createScenario,
+				'transactional' => $this->transactional,
+			],
+			'update' => [
+				'class' => 'yii\rest\UpdateAction',
+				'modelClass' => $this->modelClass,
+				'checkAccess' => [$this, 'checkAccess'],
+				'scenario' => $this->updateScenario,
+				'transactional' => $this->transactional,
+			],
+			'delete' => [
+				'class' => 'yii\rest\DeleteAction',
+				'modelClass' => $this->modelClass,
+				'checkAccess' => [$this, 'checkAccess'],
+				'transactional' => $this->transactional,
+			],
+			'options' => [
+				'class' => 'yii\rest\OptionsAction',
+			],
+		];
+	}
+
+	/**
+	 * @inheritdoc
+	 */
+	protected function verbs()
+	{
+		return [
+			'index' => ['GET', 'HEAD'],
+			'view' => ['GET', 'HEAD'],
+			'create' => ['POST'],
+			'update' => ['PUT', 'PATCH'],
+			'delete' => ['DELETE'],
+		];
+	}
+}
diff --git a/framework/rest/AuthInterface.php b/framework/rest/AuthInterface.php
new file mode 100644
index 0000000..30ccc9f
--- /dev/null
+++ b/framework/rest/AuthInterface.php
@@ -0,0 +1,41 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\rest;
+
+use yii\web\User;
+use yii\web\Request;
+use yii\web\Response;
+use yii\web\IdentityInterface;
+use yii\web\UnauthorizedHttpException;
+
+/**
+ * AuthInterface is the interface required by classes that support user authentication.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @since 2.0
+ */
+interface AuthInterface
+{
+	/**
+	 * Authenticates the current user.
+	 *
+	 * @param User $user
+	 * @param Request $request
+	 * @param Response $response
+	 * @return IdentityInterface the authenticated user identity. If authentication information is not provided, null will be returned.
+	 * @throws UnauthorizedHttpException if authentication information is provided but is invalid.
+	 */
+	public function authenticate($user, $request, $response);
+	/**
+	 * Handles authentication failure.
+	 * The implementation should normally throw UnauthorizedHttpException to indicate authentication failure.
+	 * @param Response $response
+	 * @throws UnauthorizedHttpException
+	 */
+	public function handleFailure($response);
+}
diff --git a/framework/rest/Controller.php b/framework/rest/Controller.php
new file mode 100644
index 0000000..7900b15
--- /dev/null
+++ b/framework/rest/Controller.php
@@ -0,0 +1,247 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\rest;
+
+use Yii;
+use yii\base\InvalidConfigException;
+use yii\web\Response;
+use yii\web\UnauthorizedHttpException;
+use yii\web\UnsupportedMediaTypeHttpException;
+use yii\web\TooManyRequestsHttpException;
+use yii\web\VerbFilter;
+use yii\web\ForbiddenHttpException;
+
+/**
+ * Controller is the base class for RESTful API controller classes.
+ *
+ * Controller implements the following steps in a RESTful API request handling cycle:
+ *
+ * 1. Resolving response format and API version number (see [[supportedFormats]], [[supportedVersions]] and [[version]]);
+ * 2. Validating request method (see [[verbs()]]).
+ * 3. Authenticating user (see [[authenticate()]]);
+ * 4. Formatting response data (see [[serializeData()]]).
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @since 2.0
+ */
+class Controller extends \yii\web\Controller
+{
+	/**
+	 * @var string the name of the header parameter representing the API version number.
+	 */
+	public $versionHeaderParam = 'version';
+	/**
+	 * @var string|array the configuration for creating the serializer that formats the response data.
+	 */
+	public $serializer = 'yii\rest\Serializer';
+	/**
+	 * @inheritdoc
+	 */
+	public $enableCsrfValidation = false;
+	/**
+	 * @var array the supported authentication methods. This property should take a list of supported
+	 * authentication methods, each represented by an authentication class or configuration.
+	 * If this is not set or empty, it means authentication is disabled.
+	 */
+	public $authMethods;
+	/**
+	 * @var string|array the rate limiter class or configuration. If this is not set or empty,
+	 * the rate limiting will be disabled. Note that if the user is not authenticated, the rate limiting
+	 * will also NOT be performed.
+	 * @see checkRateLimit()
+	 * @see authMethods
+	 */
+	public $rateLimiter = 'yii\rest\RateLimiter';
+	/**
+	 * @var string the chosen API version number, or null if [[supportedVersions]] is empty.
+	 * @see supportedVersions
+	 */
+	public $version;
+	/**
+	 * @var array list of supported API version numbers. If the current request does not specify a version
+	 * number, the first element will be used as the [[version|chosen version number]]. For this reason, you should
+	 * put the latest version number at the first. If this property is empty, [[version]] will not be set.
+	 */
+	public $supportedVersions = [];
+	/**
+	 * @var array list of supported response formats. The array keys are the requested content MIME types,
+	 * and the array values are the corresponding response formats. The first element will be used
+	 * as the response format if the current request does not specify a content type.
+	 */
+	public $supportedFormats = [
+		'application/json' => Response::FORMAT_JSON,
+		'application/xml' => Response::FORMAT_XML,
+	];
+
+	/**
+	 * @inheritdoc
+	 */
+	public function behaviors()
+	{
+		return [
+			'verbFilter' => [
+				'class' => VerbFilter::className(),
+				'actions' => $this->verbs(),
+			],
+		];
+	}
+
+	/**
+	 * @inheritdoc
+	 */
+	public function init()
+	{
+		parent::init();
+		$this->resolveFormatAndVersion();
+	}
+
+	/**
+	 * @inheritdoc
+	 */
+	public function beforeAction($action)
+	{
+		if (parent::beforeAction($action)) {
+			$this->authenticate();
+			$this->checkRateLimit($action);
+			return true;
+		} else {
+			return false;
+		}
+	}
+
+	/**
+	 * @inheritdoc
+	 */
+	public function afterAction($action, $result)
+	{
+		$result = parent::afterAction($action, $result);
+		return $this->serializeData($result);
+	}
+
+	/**
+	 * Resolves the response format and the API version number.
+	 * @throws UnsupportedMediaTypeHttpException
+	 */
+	protected function resolveFormatAndVersion()
+	{
+		$this->version = empty($this->supportedVersions) ? null : reset($this->supportedVersions);
+		Yii::$app->getResponse()->format = reset($this->supportedFormats);
+		$types = Yii::$app->getRequest()->getAcceptableContentTypes();
+		if (empty($types)) {
+			$types['*/*'] = [];
+		}
+
+		foreach ($types as $type => $params) {
+			if (isset($this->supportedFormats[$type])) {
+				Yii::$app->getResponse()->format = $this->supportedFormats[$type];
+				if (isset($params[$this->versionHeaderParam])) {
+					if (in_array($params[$this->versionHeaderParam], $this->supportedVersions, true)) {
+						$this->version = $params[$this->versionHeaderParam];
+					} else {
+						throw new UnsupportedMediaTypeHttpException('You are requesting an invalid version number.');
+					}
+				}
+				return;
+			}
+		}
+
+		if (!isset($types['*/*'])) {
+			throw new UnsupportedMediaTypeHttpException('None of your requested content types is supported.');
+		}
+	}
+
+	/**
+	 * Declares the allowed HTTP verbs.
+	 * Please refer to [[VerbFilter::actions]] on how to declare the allowed verbs.
+	 * @return array the allowed HTTP verbs.
+	 */
+	protected function verbs()
+	{
+		return [];
+	}
+
+	/**
+	 * Authenticates the user.
+	 * This method implements the user authentication based on an access token sent through the `Authorization` HTTP header.
+	 * @throws UnauthorizedHttpException if the user is not authenticated successfully
+	 */
+	protected function authenticate()
+	{
+		if (empty($this->authMethods)) {
+			return;
+		}
+
+		$user = Yii::$app->getUser();
+		$request = Yii::$app->getRequest();
+		$response = Yii::$app->getResponse();
+		foreach ($this->authMethods as $i => $auth) {
+			$this->authMethods[$i] = $auth = Yii::createObject($auth);
+			if (!$auth instanceof AuthInterface) {
+				throw new InvalidConfigException(get_class($auth) . ' must implement yii\rest\AuthInterface');
+			} elseif ($auth->authenticate($user, $request, $response) !== null) {
+				return;
+			}
+		}
+
+		/** @var AuthInterface $auth */
+		$auth = reset($this->authMethods);
+		$auth->handleFailure($response);
+	}
+
+	/**
+	 * Ensures the rate limit is not exceeded.
+	 *
+	 * This method will use [[rateLimiter]] to check rate limit. In order to perform rate limiting check,
+	 * the user must be authenticated and the user identity object (`Yii::$app->user->identity`) must
+	 * implement [[RateLimitInterface]].
+	 *
+	 * @param \yii\base\Action $action the action to be executed
+	 * @throws TooManyRequestsHttpException if the rate limit is exceeded.
+	 */
+	protected function checkRateLimit($action)
+	{
+		if (empty($this->rateLimiter)) {
+			return;
+		}
+
+		$identity = Yii::$app->getUser()->getIdentity(false);
+		if ($identity instanceof RateLimitInterface) {
+			/** @var RateLimiter $rateLimiter */
+			$rateLimiter = Yii::createObject($this->rateLimiter);
+			$rateLimiter->check($identity, Yii::$app->getRequest(), Yii::$app->getResponse(), $action);
+		}
+	}
+
+	/**
+	 * Serializes the specified data.
+	 * The default implementation will create a serializer based on the configuration given by [[serializer]].
+	 * It then uses the serializer to serialize the given data.
+	 * @param mixed $data the data to be serialized
+	 * @return mixed the serialized data.
+	 */
+	protected function serializeData($data)
+	{
+		return Yii::createObject($this->serializer)->serialize($data);
+	}
+
+	/**
+	 * Checks the privilege of the current user.
+	 *
+	 * This method should be overridden to check whether the current user has the privilege
+	 * to run the specified action against the specified data model.
+	 * If the user does not have access, a [[ForbiddenHttpException]] should be thrown.
+	 *
+	 * @param string $action the ID of the action to be executed
+	 * @param object $model the model to be accessed. If null, it means no specific model is being accessed.
+	 * @param array $params additional parameters
+	 * @throws ForbiddenHttpException if the user does not have access
+	 */
+	public function checkAccess($action, $model = null, $params = [])
+	{
+	}
+}
diff --git a/framework/rest/CreateAction.php b/framework/rest/CreateAction.php
new file mode 100644
index 0000000..fa818c2
--- /dev/null
+++ b/framework/rest/CreateAction.php
@@ -0,0 +1,80 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\rest;
+
+use Yii;
+use yii\base\Model;
+use yii\db\ActiveRecord;
+
+/**
+ * CreateAction implements the API endpoint for creating a new model from the given data.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @since 2.0
+ */
+class CreateAction extends Action
+{
+	/**
+	 * @var string the scenario to be assigned to the new model before it is validated and saved.
+	 */
+	public $scenario = Model::SCENARIO_DEFAULT;
+	/**
+	 * @var boolean whether to start a DB transaction when saving the model.
+	 */
+	public $transactional = true;
+	/**
+	 * @var string the name of the view action. This property is need to create the URL when the mode is successfully created.
+	 */
+	public $viewAction = 'view';
+
+
+	/**
+	 * Creates a new model.
+	 * @return \yii\db\ActiveRecordInterface the model newly created
+	 * @throws \Exception if there is any error when creating the model
+	 */
+	public function run()
+	{
+		if ($this->checkAccess) {
+			call_user_func($this->checkAccess, $this->id);
+		}
+
+		/**
+		 * @var \yii\db\ActiveRecord $model
+		 */
+		$model = new $this->modelClass([
+			'scenario' => $this->scenario,
+		]);
+
+		$model->load(Yii::$app->getRequest()->getBodyParams(), '');
+
+		if ($this->transactional && $model instanceof ActiveRecord) {
+			if ($model->validate()) {
+				$transaction = $model->getDb()->beginTransaction();
+				try {
+					$model->insert(false);
+					$transaction->commit();
+				} catch (\Exception $e) {
+					$transaction->rollback();
+					throw $e;
+				}
+			}
+		} else {
+			$model->save();
+		}
+
+		if (!$model->hasErrors()) {
+			$response = Yii::$app->getResponse();
+			$response->setStatusCode(201);
+			$id = implode(',', array_values($model->getPrimaryKey(true)));
+			$response->getHeaders()->set('Location', $this->controller->createAbsoluteUrl([$this->viewAction, 'id' => $id]));
+		}
+
+		return $model;
+	}
+}
diff --git a/framework/rest/DeleteAction.php b/framework/rest/DeleteAction.php
new file mode 100644
index 0000000..a0355c8
--- /dev/null
+++ b/framework/rest/DeleteAction.php
@@ -0,0 +1,53 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\rest;
+
+use Yii;
+use yii\db\ActiveRecord;
+
+/**
+ * DeleteAction implements the API endpoint for deleting a model.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @since 2.0
+ */
+class DeleteAction extends Action
+{
+	/**
+	 * @var boolean whether to start a DB transaction when deleting the model.
+	 */
+	public $transactional = true;
+
+
+	/**
+	 * Deletes a model.
+	 */
+	public function run($id)
+	{
+		$model = $this->findModel($id);
+
+		if ($this->checkAccess) {
+			call_user_func($this->checkAccess, $this->id, $model);
+		}
+
+		if ($this->transactional && $model instanceof ActiveRecord) {
+			$transaction = $model->getDb()->beginTransaction();
+			try {
+				$model->delete();
+				$transaction->commit();
+			} catch (\Exception $e) {
+				$transaction->rollback();
+				throw $e;
+			}
+		} else {
+			$model->delete();
+		}
+
+		Yii::$app->getResponse()->setStatusCode(204);
+	}
+}
diff --git a/framework/rest/HttpBasicAuth.php b/framework/rest/HttpBasicAuth.php
new file mode 100644
index 0000000..7a69c15
--- /dev/null
+++ b/framework/rest/HttpBasicAuth.php
@@ -0,0 +1,50 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\rest;
+
+use Yii;
+use yii\base\Component;
+use yii\web\UnauthorizedHttpException;
+
+/**
+ * HttpBasicAuth implements the HTTP Basic authentication method.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @since 2.0
+ */
+class HttpBasicAuth extends Component implements AuthInterface
+{
+	/**
+	 * @var string the HTTP authentication realm
+	 */
+	public $realm = 'api';
+
+	/**
+	 * @inheritdoc
+	 */
+	public function authenticate($user, $request, $response)
+	{
+		if (($accessToken = $request->getAuthUser()) !== null) {
+			$identity = $user->loginByAccessToken($accessToken);
+			if ($identity !== null) {
+				return $identity;
+			}
+			$this->handleFailure($response);
+		}
+		return null;
+	}
+
+	/**
+	 * @inheritdoc
+	 */
+	public function handleFailure($response)
+	{
+		$response->getHeaders()->set('WWW-Authenticate', "Basic realm=\"{$this->realm}\"");
+		throw new UnauthorizedHttpException('You are requesting with an invalid access token.');
+	}
+}
diff --git a/framework/rest/HttpBearerAuth.php b/framework/rest/HttpBearerAuth.php
new file mode 100644
index 0000000..81033c9
--- /dev/null
+++ b/framework/rest/HttpBearerAuth.php
@@ -0,0 +1,52 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\rest;
+
+use Yii;
+use yii\base\Component;
+use yii\web\UnauthorizedHttpException;
+
+/**
+ * HttpBearerAuth implements the authentication method based on HTTP Bearer token.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @since 2.0
+ */
+class HttpBearerAuth extends Component implements AuthInterface
+{
+	/**
+	 * @var string the HTTP authentication realm
+	 */
+	public $realm = 'api';
+
+	/**
+	 * @inheritdoc
+	 */
+	public function authenticate($user, $request, $response)
+	{
+		$authHeader = $request->getHeaders()->get('Authorization');
+		if ($authHeader !== null && preg_match("/^Bearer\\s+(.*?)$/", $authHeader, $matches)) {
+			$identity = $user->loginByAccessToken($matches[1]);
+			if ($identity !== null) {
+				return $identity;
+			}
+
+			$this->handleFailure($response);
+		}
+		return null;
+	}
+
+	/**
+	 * @inheritdoc
+	 */
+	public function handleFailure($response)
+	{
+		$response->getHeaders()->set('WWW-Authenticate', "Basic realm=\"{$this->realm}\"");
+		throw new UnauthorizedHttpException('You are requesting with an invalid access token.');
+	}
+}
diff --git a/framework/rest/IndexAction.php b/framework/rest/IndexAction.php
new file mode 100644
index 0000000..ca30220
--- /dev/null
+++ b/framework/rest/IndexAction.php
@@ -0,0 +1,65 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\rest;
+
+use Yii;
+use yii\data\ActiveDataProvider;
+
+/**
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @since 2.0
+ */
+class IndexAction extends Action
+{
+	/**
+	 * @var callable a PHP callable that will be called to prepare a data provider that
+	 * should return a collection of the models. If not set, [[prepareDataProvider()]] will be used instead.
+	 * The signature of the callable should be:
+	 *
+	 * ```php
+	 * function ($action) {
+	 *     // $action is the action object currently running
+	 * }
+	 * ```
+	 *
+	 * The callable should return an instance of [[ActiveDataProvider]].
+	 */
+	public $prepareDataProvider;
+
+
+	/**
+	 * @return ActiveDataProvider
+	 */
+	public function run()
+	{
+		if ($this->checkAccess) {
+			call_user_func($this->checkAccess, $this->id);
+		}
+
+		return $this->prepareDataProvider();
+	}
+
+	/**
+	 * Prepares the data provider that should return the requested collection of the models.
+	 * @return ActiveDataProvider
+	 */
+	protected function prepareDataProvider()
+	{
+		if ($this->prepareDataProvider !== null) {
+			return call_user_func($this->prepareDataProvider, $this);
+		}
+
+		/**
+		 * @var \yii\db\BaseActiveRecord $modelClass
+		 */
+		$modelClass = $this->modelClass;
+		return new ActiveDataProvider([
+			'query' => $modelClass::find(),
+		]);
+	}
+}
diff --git a/framework/rest/OptionsAction.php b/framework/rest/OptionsAction.php
new file mode 100644
index 0000000..0f9561f
--- /dev/null
+++ b/framework/rest/OptionsAction.php
@@ -0,0 +1,42 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\rest;
+
+use Yii;
+
+/**
+ * OptionsAction responds to the OPTIONS request by sending back an `Allow` header.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @since 2.0
+ */
+class OptionsAction extends \yii\base\Action
+{
+	/**
+	 * @var array the HTTP verbs that are supported by the collection URL
+	 */
+	public $collectionOptions = ['GET', 'POST', 'HEAD', 'OPTIONS'];
+	/**
+	 * @var array the HTTP verbs that are supported by the resource URL
+	 */
+	public $resourceOptions = ['GET', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS'];
+
+
+	/**
+	 * Responds to the OPTIONS request.
+	 * @param string $id
+	 */
+	public function run($id = null)
+	{
+		if (Yii::$app->getRequest()->getMethod() !== 'OPTIONS') {
+			Yii::$app->getResponse()->setStatusCode(405);
+		}
+		$options = $id === null ? $this->collectionOptions : $this->resourceOptions;
+		Yii::$app->getResponse()->getHeaders()->set('Allow', implode(', ', $options));
+	}
+}
diff --git a/framework/rest/QueryParamAuth.php b/framework/rest/QueryParamAuth.php
new file mode 100644
index 0000000..f45e4c8
--- /dev/null
+++ b/framework/rest/QueryParamAuth.php
@@ -0,0 +1,52 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\rest;
+
+use Yii;
+use yii\base\Component;
+use yii\web\UnauthorizedHttpException;
+
+/**
+ * QueryParamAuth implements the authentication method based on the access token passed through a query parameter.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @since 2.0
+ */
+class QueryParamAuth extends Component implements AuthInterface
+{
+	/**
+	 * @var string the parameter name for passing the access token
+	 */
+	public $tokenParam = 'access-token';
+
+	/**
+	 * @inheritdoc
+	 */
+	public function authenticate($user, $request, $response)
+	{
+		$accessToken = $request->get($this->tokenParam);
+		if (is_string($accessToken)) {
+			$identity = $user->loginByAccessToken($accessToken);
+			if ($identity !== null) {
+				return $identity;
+			}
+		}
+		if ($accessToken !== null) {
+			$this->handleFailure($response);
+		}
+		return null;
+	}
+
+	/**
+	 * @inheritdoc
+	 */
+	public function handleFailure($response)
+	{
+		throw new UnauthorizedHttpException('You are requesting with an invalid access token.');
+	}
+}
diff --git a/framework/rest/RateLimitInterface.php b/framework/rest/RateLimitInterface.php
new file mode 100644
index 0000000..07f60e0
--- /dev/null
+++ b/framework/rest/RateLimitInterface.php
@@ -0,0 +1,39 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\rest;
+
+/**
+ * RateLimitInterface is the interface that may be implemented by an identity object to enforce rate limiting.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @since 2.0
+ */
+interface RateLimitInterface
+{
+	/**
+	 * Returns the maximum number of allowed requests and the window size.
+	 * @param array $params the additional parameters associated with the rate limit.
+	 * @return array an array of two elements. The first element is the maximum number of allowed requests,
+	 * and the second element is the size of the window in seconds.
+	 */
+	public function getRateLimit($params = []);
+	/**
+	 * Loads the number of allowed requests and the corresponding timestamp from a persistent storage.
+	 * @param array $params the additional parameters associated with the rate limit.
+	 * @return array an array of two elements. The first element is the number of allowed requests,
+	 * and the second element is the corresponding UNIX timestamp.
+	 */
+	public function loadAllowance($params = []);
+	/**
+	 * Saves the number of allowed requests and the corresponding timestamp to a persistent storage.
+	 * @param integer $allowance the number of allowed requests remaining.
+	 * @param integer $timestamp the current timestamp.
+	 * @param array $params the additional parameters associated with the rate limit.
+	 */
+	public function saveAllowance($allowance, $timestamp, $params = []);
+}
diff --git a/framework/rest/RateLimiter.php b/framework/rest/RateLimiter.php
new file mode 100644
index 0000000..753a0f0
--- /dev/null
+++ b/framework/rest/RateLimiter.php
@@ -0,0 +1,85 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\rest;
+
+use yii\base\Component;
+use yii\base\Action;
+use yii\web\Request;
+use yii\web\Response;
+use yii\web\TooManyRequestsHttpException;
+
+/**
+ * RateLimiter implements a rate limiting algorithm based on the [leaky bucket algorithm](http://en.wikipedia.org/wiki/Leaky_bucket).
+ *
+ * You may call [[check()]] to enforce rate limiting.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @since 2.0
+ */
+class RateLimiter extends Component
+{
+	/**
+	 * @var boolean whether to include rate limit headers in the response
+	 */
+	public $enableRateLimitHeaders = true;
+	/**
+	 * @var string the message to be displayed when rate limit exceeds
+	 */
+	public $errorMessage = 'Rate limit exceeded.';
+
+	/**
+	 * Checks whether the rate limit exceeds.
+	 * @param RateLimitInterface $user the current user
+	 * @param Request $request
+	 * @param Response $response
+	 * @param Action $action the action to be executed
+	 * @throws TooManyRequestsHttpException if rate limit exceeds
+	 */
+	public function check($user, $request, $response, $action)
+	{
+		$current = time();
+		$params = [
+			'request' => $request,
+			'action' => $action,
+		];
+
+		list ($limit, $window) = $user->getRateLimit($params);
+		list ($allowance, $timestamp) = $user->loadAllowance($params);
+
+		$allowance += (int)(($current - $timestamp) * $limit / $window);
+		if ($allowance > $limit) {
+			$allowance = $limit;
+		}
+
+		if ($allowance < 1) {
+			$user->saveAllowance(0, $current, $params);
+			$this->addRateLimitHeaders($response, $limit, 0, $window);
+			throw new TooManyRequestsHttpException($this->errorMessage);
+		} else {
+			$user->saveAllowance($allowance - 1, $current, $params);
+			$this->addRateLimitHeaders($response, $limit, 0, (int)(($limit - $allowance) * $window / $limit));
+		}
+	}
+
+	/**
+	 * Adds the rate limit headers to the response
+	 * @param Response $response
+	 * @param integer $limit the maximum number of allowed requests during a period
+	 * @param integer $remaining the remaining number of allowed requests within the current period
+	 * @param integer $reset the number of seconds to wait before having maximum number of allowed requests again
+	 */
+	protected function addRateLimitHeaders($response, $limit, $remaining, $reset)
+	{
+		if ($this->enableRateLimitHeaders) {
+			$response->getHeaders()
+				->set('X-Rate-Limit-Limit', $limit)
+				->set('X-Rate-Limit-Remaining', $remaining)
+				->set('X-Rate-Limit-Reset', $reset);
+		}
+	}
+}
diff --git a/framework/rest/Serializer.php b/framework/rest/Serializer.php
new file mode 100644
index 0000000..75a6664
--- /dev/null
+++ b/framework/rest/Serializer.php
@@ -0,0 +1,248 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\rest;
+
+use Yii;
+use yii\base\Component;
+use yii\base\Model;
+use yii\data\DataProviderInterface;
+use yii\data\Pagination;
+use yii\helpers\ArrayHelper;
+use yii\web\Link;
+use yii\web\Request;
+use yii\web\Response;
+
+/**
+ * Serializer converts resource objects and collections into array representation.
+ *
+ * Serializer is mainly used by REST controllers to convert different objects into array representation
+ * so that they can be further turned into different formats, such as JSON, XML, by response formatters.
+ *
+ * The default implementation handles resources as [[Model]] objects and collections as objects
+ * implementing [[DataProviderInterface]]. You may override [[serialize()]] to handle more types.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @since 2.0
+ */
+class Serializer extends Component
+{
+	/**
+	 * @var string the name of the query parameter containing the information about which fields should be returned
+	 * for a [[Model]] object. If the parameter is not provided or empty, the default set of fields as defined
+	 * by [[Model::fields()]] will be returned.
+	 */
+	public $fieldsParam = 'fields';
+	/**
+	 * @var string the name of the query parameter containing the information about which fields should be returned
+	 * in addition to those listed in [[fieldsParam]] for a resource object.
+	 */
+	public $expandParam = 'expand';
+	/**
+	 * @var string the name of the HTTP header containing the information about total number of data items.
+	 * This is used when serving a resource collection with pagination.
+	 */
+	public $totalCountHeader = 'X-Pagination-Total-Count';
+	/**
+	 * @var string the name of the HTTP header containing the information about total number of pages of data.
+	 * This is used when serving a resource collection with pagination.
+	 */
+	public $pageCountHeader = 'X-Pagination-Page-Count';
+	/**
+	 * @var string the name of the HTTP header containing the information about the current page number (1-based).
+	 * This is used when serving a resource collection with pagination.
+	 */
+	public $currentPageHeader = 'X-Pagination-Current-Page';
+	/**
+	 * @var string the name of the HTTP header containing the information about the number of data items in each page.
+	 * This is used when serving a resource collection with pagination.
+	 */
+	public $perPageHeader = 'X-Pagination-Per-Page';
+	/**
+	 * @var string the name of the envelope (e.g. `items`) for returning the resource objects in a collection.
+	 * This is used when serving a resource collection. When this is set and pagination is enabled, the serializer
+	 * will return a collection in the following format:
+	 *
+	 * ```php
+	 * [
+	 *     'items' => [...],  // assuming collectionEnvelope is "items"
+	 *     '_links' => {  // pagination links as returned by Pagination::getLinks()
+	 *         'self' => '...',
+	 *         'next' => '...',
+	 *         'last' => '...',
+	 *     },
+	 *     '_meta' => {  // meta information as returned by Pagination::toArray()
+	 *         'totalCount' => 100,
+	 *         'pageCount' => 5,
+	 *         'currentPage' => 1,
+	 *         'perPage' => 20,
+	 *     },
+	 * ]
+	 * ```
+	 *
+	 * If this property is not set, the resource arrays will be directly returned without using envelope.
+	 * The pagination information as shown in `_links` and `_meta` can be accessed from the response HTTP headers.
+	 */
+	public $collectionEnvelope;
+	/**
+	 * @var Request the current request. If not set, the `request` application component will be used.
+	 */
+	public $request;
+	/**
+	 * @var Response the response to be sent. If not set, the `response` application component will be used.
+	 */
+	public $response;
+
+	/**
+	 * @inheritdoc
+	 */
+	public function init()
+	{
+		if ($this->request === null) {
+			$this->request = Yii::$app->getRequest();
+		}
+		if ($this->response === null) {
+			$this->response = Yii::$app->getResponse();
+		}
+	}
+
+	/**
+	 * Serializes the given data into a format that can be easily turned into other formats.
+	 * This method mainly converts the objects of recognized types into array representation.
+	 * It will not do conversion for unknown object types or non-object data.
+	 * The default implementation will handle [[Model]] and [[DataProviderInterface]].
+	 * You may override this method to support more object types.
+	 * @param mixed $data the data to be serialized.
+	 * @return mixed the converted data.
+	 */
+	public function serialize($data)
+	{
+		if ($data instanceof Model) {
+			return $data->hasErrors() ? $this->serializeModelErrors($data) : $this->serializeModel($data);
+		} elseif ($data instanceof DataProviderInterface) {
+			return $this->serializeDataProvider($data);
+		} else {
+			return $data;
+		}
+	}
+
+	/**
+	 * @return array the names of the requested fields. The first element is an array
+	 * representing the list of default fields requested, while the second element is
+	 * an array of the extra fields requested in addition to the default fields.
+	 * @see Model::fields()
+	 * @see Model::extraFields()
+	 */
+	protected function getRequestedFields()
+	{
+		$fields = $this->request->get($this->fieldsParam);
+		$expand = $this->request->get($this->expandParam);
+		return [
+			preg_split('/\s*,\s*/', $fields, -1, PREG_SPLIT_NO_EMPTY),
+			preg_split('/\s*,\s*/', $expand, -1, PREG_SPLIT_NO_EMPTY),
+		];
+	}
+
+	/**
+	 * Serializes a data provider.
+	 * @param DataProviderInterface $dataProvider
+	 * @return array the array representation of the data provider.
+	 */
+	protected function serializeDataProvider($dataProvider)
+	{
+		$models = $this->serializeModels($dataProvider->getModels());
+
+		if (($pagination = $dataProvider->getPagination()) !== false) {
+			$this->addPaginationHeaders($pagination);
+		}
+
+		if ($this->request->getIsHead()) {
+			return null;
+		} elseif ($this->collectionEnvelope === null) {
+			return $models;
+		} else {
+			$result = [
+				$this->collectionEnvelope => $models,
+			];
+			if ($pagination !== false) {
+				$result['_links'] = Link::serialize($pagination->getLinks());
+				$result['_meta'] = $pagination->toArray();
+			}
+			return $result;
+		}
+	}
+
+	/**
+	 * Adds HTTP headers about the pagination to the response.
+	 * @param Pagination $pagination
+	 */
+	protected function addPaginationHeaders($pagination)
+	{
+		$links = [];
+		foreach ($pagination->getLinks(true) as $rel => $url) {
+			$links[] = "<$url>; rel=$rel";
+		}
+
+		$this->response->getHeaders()
+			->set($this->totalCountHeader, $pagination->totalCount)
+			->set($this->pageCountHeader, $pagination->getPageCount())
+			->set($this->currentPageHeader, $pagination->getPage() + 1)
+			->set($this->perPageHeader, $pagination->pageSize)
+			->set('Link', implode(', ', $links));
+	}
+
+	/**
+	 * Serializes a model object.
+	 * @param Model $model
+	 * @return array the array representation of the model
+	 */
+	protected function serializeModel($model)
+	{
+		if ($this->request->getIsHead()) {
+			return null;
+		} else {
+			list ($fields, $expand) = $this->getRequestedFields();
+			return $model->toArray($fields, $expand);
+		}
+	}
+
+	/**
+	 * Serializes the validation errors in a model.
+	 * @param Model $model
+	 * @return array the array representation of the errors
+	 */
+	protected function serializeModelErrors($model)
+	{
+		$this->response->setStatusCode(422, 'Data Validation Failed.');
+		$result = [];
+		foreach ($model->getFirstErrors() as $name => $message) {
+			$result[] = [
+				'field' => $name,
+				'message' => $message,
+			];
+		}
+		return $result;
+	}
+
+	/**
+	 * Serializes a set of models.
+	 * @param array $models
+	 * @return array the array representation of the models
+	 */
+	protected function serializeModels(array $models)
+	{
+		list ($fields, $expand) = $this->getRequestedFields();
+		foreach ($models as $i => $model) {
+			if ($model instanceof Model) {
+				$models[$i] = $model->toArray($fields, $expand);
+			} elseif (is_array($model)) {
+				$models[$i] = ArrayHelper::toArray($model);
+			}
+		}
+		return $models;
+	}
+}
diff --git a/framework/rest/UpdateAction.php b/framework/rest/UpdateAction.php
new file mode 100644
index 0000000..7a14a0a
--- /dev/null
+++ b/framework/rest/UpdateAction.php
@@ -0,0 +1,67 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\rest;
+
+use Yii;
+use yii\base\Model;
+use yii\db\ActiveRecord;
+
+/**
+ * UpdateAction implements the API endpoint for updating a model.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @since 2.0
+ */
+class UpdateAction extends Action
+{
+	/**
+	 * @var string the scenario to be assigned to the model before it is validated and updated.
+	 */
+	public $scenario = Model::SCENARIO_DEFAULT;
+	/**
+	 * @var boolean whether to start a DB transaction when saving the model.
+	 */
+	public $transactional = true;
+
+
+	/**
+	 * Updates an existing model.
+	 * @param string $id the primary key of the model.
+	 * @return \yii\db\ActiveRecordInterface the model being updated
+	 * @throws \Exception if there is any error when updating the model
+	 */
+	public function run($id)
+	{
+		/** @var ActiveRecord $model */
+		$model = $this->findModel($id);
+
+		if ($this->checkAccess) {
+			call_user_func($this->checkAccess, $this->id, $model);
+		}
+
+		$model->scenario = $this->scenario;
+		$model->load(Yii::$app->getRequest()->getBodyParams(), '');
+
+		if ($this->transactional && $model instanceof ActiveRecord) {
+			if ($model->validate()) {
+				$transaction = $model->getDb()->beginTransaction();
+				try {
+					$model->update(false);
+					$transaction->commit();
+				} catch (\Exception $e) {
+					$transaction->rollback();
+					throw $e;
+				}
+			}
+		} else {
+			$model->save();
+		}
+
+		return $model;
+	}
+}
diff --git a/framework/rest/UrlRule.php b/framework/rest/UrlRule.php
new file mode 100644
index 0000000..5e4b218
--- /dev/null
+++ b/framework/rest/UrlRule.php
@@ -0,0 +1,250 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\rest;
+
+use Yii;
+use yii\base\InvalidConfigException;
+use yii\helpers\Inflector;
+use yii\web\CompositeUrlRule;
+
+/**
+ * UrlRule is provided to simplify the creation of URL rules for RESTful API support.
+ *
+ * The simplest usage of UrlRule is to declare a rule like the following in the application configuration,
+ *
+ * ```php
+ * [
+ *     'class' => 'yii\rest\UrlRule',
+ *     'controller' => 'user',
+ * ]
+ * ```
+ *
+ * The above code will create a whole set of URL rules supporting the following RESTful API endpoints:
+ *
+ * - `'PUT,PATCH users/<id>' => 'user/update'`: update a user
+ * - `'DELETE users/<id>' => 'user/delete'`: delete a user
+ * - `'GET,HEAD users/<id>' => 'user/view'`: return the details/overview/options of a user
+ * - `'POST users' => 'user/create'`: create a new user
+ * - `'GET,HEAD users' => 'user/index'`: return a list/overview/options of users
+ * - `'users/<id>' => 'user/options'`: process all unhandled verbs of a user
+ * - `'users' => 'user/options'`: process all unhandled verbs of user collection
+ *
+ * You may configure [[only]] and/or [[except]] to disable some of the above rules.
+ * You may configure [[patterns]] to completely redefine your own list of rules.
+ * You may configure [[controller]] with multiple controller IDs to generate rules for all these controllers.
+ * For example, the following code will disable the `delete` rule and generate rules for both `user` and `post` controllers:
+ *
+ * ```php
+ * [
+ *     'class' => 'yii\rest\UrlRule',
+ *     'controller' => ['user', 'post'],
+ *     'except' => ['delete'],
+ * ]
+ * ```
+ *
+ * The property [[controller]] is required and should be the controller ID. It should be prefixed with
+ * the module ID if the controller is within a module.
+ *
+ * The controller ID used in the pattern will be automatically pluralized (e.g. `user` becomes `users`
+ * as shown in the above examples). You may configure [[urlName]] to explicitly specify the controller ID
+ * in the pattern.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @since 2.0
+ */
+class UrlRule extends CompositeUrlRule
+{
+	/**
+	 * @var string the common prefix string shared by all patterns.
+	 */
+	public $prefix;
+	/**
+	 * @var string the suffix that will be assigned to [[\yii\web\UrlRule::suffix]] for every generated rule.
+	 */
+	public $suffix;
+	/**
+	 * @var string|array the controller ID (e.g. `user`, `post-comment`) that the rules in this composite rule
+	 * are dealing with. It should be prefixed with the module ID if the controller is within a module (e.g. `admin/user`).
+	 *
+	 * By default, the controller ID will be pluralized automatically when it is put in the patterns of the
+	 * generated rules. If you want to explicitly specify how the controller ID should appear in the patterns,
+	 * you may use an array with the array key being as the controller ID in the pattern, and the array value
+	 * the actual controller ID. For example, `['u' => 'user']`.
+	 *
+	 * You may also pass multiple controller IDs as an array. If this is the case, this composite rule will
+	 * generate applicable URL rules for EVERY specified controller. For example, `['user', 'post']`.
+	 */
+	public $controller;
+	/**
+	 * @var array list of acceptable actions. If not empty, only the actions within this array
+	 * will have the corresponding URL rules created.
+	 * @see patterns
+	 */
+	public $only = [];
+	/**
+	 * @var array list of actions that should be excluded. Any action found in this array
+	 * will NOT have its URL rules created.
+	 * @see patterns
+	 */
+	public $except = [];
+	/**
+	 * @var array patterns for supporting extra actions in addition to those listed in [[patterns]].
+	 * The keys are the patterns and the values are the corresponding action IDs.
+	 * These extra patterns will take precedence over [[patterns]].
+	 */
+	public $extraPatterns = [];
+	/**
+	 * @var array list of tokens that should be replaced for each pattern. The keys are the token names,
+	 * and the values are the corresponding replacements.
+	 * @see patterns
+	 */
+	public $tokens = [
+		'{id}' => '<id:\\d[\\d,]*>',
+	];
+	/**
+	 * @var array list of possible patterns and the corresponding actions for creating the URL rules.
+	 * The keys are the patterns and the values are the corresponding actions.
+	 * The format of patterns is `Verbs Pattern`, where `Verbs` stands for a list of HTTP verbs separated
+	 * by comma (without space). If `Verbs` is not specified, it means all verbs are allowed.
+	 * `Pattern` is optional. It will be prefixed with [[prefix]]/[[controller]]/,
+	 * and tokens in it will be replaced by [[tokens]].
+	 */
+	public $patterns = [
+		'PUT,PATCH {id}' => 'update',
+		'DELETE {id}' => 'delete',
+		'GET,HEAD {id}' => 'view',
+		'POST' => 'create',
+		'GET,HEAD' => 'index',
+		'{id}' => 'options',
+		'' => 'options',
+	];
+	/**
+	 * @var array the default configuration for creating each URL rule contained by this rule.
+	 */
+	public $ruleConfig = [
+		'class' => 'yii\web\UrlRule',
+	];
+	/**
+	 * @var boolean whether to automatically pluralize the URL names for controllers.
+	 * If true, a controller ID will appear in plural form in URLs. For example, `user` controller
+	 * will appear as `users` in URLs.
+	 * @see controllers
+	 */
+	public $pluralize = true;
+
+
+	/**
+	 * @inheritdoc
+	 */
+	public function init()
+	{
+		if (empty($this->controller)) {
+			throw new InvalidConfigException('"controller" must be set.');
+		}
+
+		$controllers = [];
+		foreach ((array)$this->controller as $urlName => $controller) {
+			if (is_integer($urlName)) {
+				$urlName = $this->pluralize ? Inflector::pluralize($controller) : $controller;
+			}
+			$controllers[$urlName] = $controller;
+		}
+		$this->controller = $controllers;
+
+		$this->prefix = trim($this->prefix, '/');
+
+		parent::init();
+	}
+
+	/**
+	 * @inheritdoc
+	 */
+	protected function createRules()
+	{
+		$only = array_flip($this->only);
+		$except = array_flip($this->except);
+		$patterns = array_merge($this->patterns, $this->extraPatterns);
+		$rules = [];
+		foreach ($this->controller as $urlName => $controller) {
+			$prefix = trim($this->prefix . '/' . $urlName, '/');
+			foreach ($patterns as $pattern => $action) {
+				if (!isset($except[$action]) && (empty($only) || isset($only[$action]))) {
+					$rules[$urlName][] = $this->createRule($pattern, $prefix, $controller . '/' . $action);
+				}
+			}
+		}
+		return $rules;
+	}
+
+	/**
+	 * Creates a URL rule using the given pattern and action.
+	 * @param string $pattern
+	 * @param string $prefix
+	 * @param string $action
+	 * @return \yii\web\UrlRuleInterface
+	 */
+	protected function createRule($pattern, $prefix, $action)
+	{
+		$verbs = 'GET|HEAD|POST|PUT|PATCH|DELETE|OPTIONS';
+		if (preg_match("/^((?:($verbs),)*($verbs))(?:\\s+(.*))?$/", $pattern, $matches)) {
+			$verbs = explode(',', $matches[1]);
+			$pattern = isset($matches[4]) ? $matches[4] : '';
+		} else {
+			$verbs = [];
+		}
+
+		$config = $this->ruleConfig;
+		$config['verb'] = $verbs;
+		$config['pattern'] = rtrim($prefix . '/' . strtr($pattern, $this->tokens), '/');
+		$config['route'] = $action;
+		if (!in_array('GET', $verbs)) {
+			$config['mode'] = \yii\web\UrlRule::PARSING_ONLY;
+		}
+		$config['suffix'] = $this->suffix;
+
+		return Yii::createObject($config);
+	}
+
+	/**
+	 * @inheritdoc
+	 */
+	public function parseRequest($manager, $request)
+	{
+		$pathInfo = $request->getPathInfo();
+		foreach ($this->rules as $urlName => $rules) {
+			if (strpos($pathInfo, $urlName) !== false) {
+				foreach ($rules as $rule) {
+					/** @var \yii\web\UrlRule $rule */
+					if (($result = $rule->parseRequest($manager, $request)) !== false) {
+						Yii::trace("Request parsed with URL rule: {$rule->name}", __METHOD__);
+						return $result;
+					}
+				}
+			}
+		}
+		return false;
+	}
+
+	/**
+	 * @inheritdoc
+	 */
+	public function createUrl($manager, $route, $params)
+	{
+		foreach ($this->controller as $urlName => $controller) {
+			if (strpos($route, $controller) !== false) {
+				foreach ($this->rules[$urlName] as $rule) {
+					/** @var \yii\web\UrlRule $rule */
+					if (($url = $rule->createUrl($manager, $route, $params)) !== false) {
+						return $url;
+					}
+				}
+			}
+		}
+		return false;
+	}
+}
diff --git a/framework/rest/ViewAction.php b/framework/rest/ViewAction.php
new file mode 100644
index 0000000..c37522f
--- /dev/null
+++ b/framework/rest/ViewAction.php
@@ -0,0 +1,33 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\rest;
+
+use Yii;
+
+/**
+ * ViewAction implements the API endpoint for returning the detailed information about a model.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @since 2.0
+ */
+class ViewAction extends Action
+{
+	/**
+	 * Displays a model.
+	 * @param string $id the primary key of the model.
+	 * @return \yii\db\ActiveRecordInterface the model being displayed
+	 */
+	public function run($id)
+	{
+		$model = $this->findModel($id);
+		if ($this->checkAccess) {
+			call_user_func($this->checkAccess, $this->id, $model);
+		}
+		return $model;
+	}
+}
diff --git a/framework/web/IdentityInterface.php b/framework/web/IdentityInterface.php
index c796b50..2aac17f 100644
--- a/framework/web/IdentityInterface.php
+++ b/framework/web/IdentityInterface.php
@@ -52,6 +52,14 @@ interface IdentityInterface
 	 */
 	public static function findIdentity($id);
 	/**
+	 * Finds an identity by the given secrete token.
+	 * @param string $token the secrete token
+	 * @return IdentityInterface the identity object that matches the given token.
+	 * Null should be returned if such an identity cannot be found
+	 * or the identity is not in an active state (disabled, deleted, etc.)
+	 */
+	public static function findIdentityByAccessToken($token);
+	/**
 	 * Returns an ID that can uniquely identify a user identity.
 	 * @return string|integer an ID that uniquely identifies a user identity.
 	 */
diff --git a/framework/web/Link.php b/framework/web/Link.php
new file mode 100644
index 0000000..9e10e9b
--- /dev/null
+++ b/framework/web/Link.php
@@ -0,0 +1,83 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\web;
+
+use yii\base\Arrayable;
+use yii\base\Object;
+
+/**
+ * Link represents a link object as defined in [JSON Hypermedia API Language](https://tools.ietf.org/html/draft-kelly-json-hal-03).
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @since 2.0
+ */
+class Link extends Object implements Arrayable
+{
+	/**
+	 * The self link.
+	 */
+	const REL_SELF = 'self';
+
+	/**
+	 * @var string a URI [RFC3986](https://tools.ietf.org/html/rfc3986) or
+	 * URI template [RFC6570](https://tools.ietf.org/html/rfc6570). This property is required.
+	 */
+	public $href;
+	/**
+	 * @var string a secondary key for selecting Link Objects which share the same relation type
+	 */
+	public $name;
+	/**
+	 * @var string a hint to indicate the media type expected when dereferencing the target resource
+	 */
+	public $type;
+	/**
+	 * @var boolean a value indicating whether [[href]] refers to a URI or URI template.
+	 */
+	public $templated = false;
+	/**
+	 * @var string a URI that hints about the profile of the target resource.
+	 */
+	public $profile;
+	/**
+	 * @var string a label describing the link
+	 */
+	public $title;
+	/**
+	 * @var string the language of the target resource
+	 */
+	public $hreflang;
+
+	/**
+	 * @inheritdoc
+	 */
+	public function toArray()
+	{
+		return array_filter((array)$this);
+	}
+
+	/**
+	 * Serializes a list of links into proper array format.
+	 * @param array $links the links to be serialized
+	 * @return array the proper array representation of the links.
+	 */
+	public static function serialize(array $links)
+	{
+		foreach ($links as $rel => $link) {
+			if (is_array($link)) {
+				foreach ($link as $i => $l) {
+					$link[$i] = $l instanceof self ? $l->toArray() : ['href' => $l];
+				}
+				$links[$rel] = $link;
+			} elseif (!$link instanceof self) {
+				$links[$rel] = ['href' => $link];
+			}
+		}
+		return $links;
+	}
+}
diff --git a/framework/web/Linkable.php b/framework/web/Linkable.php
new file mode 100644
index 0000000..8d1558b
--- /dev/null
+++ b/framework/web/Linkable.php
@@ -0,0 +1,42 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\web;
+
+/**
+ * Linkable is the interface that should be implemented by classes that typically represent locatable resources.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @since 2.0
+ */
+interface Linkable
+{
+	/**
+	 * Returns a list of links.
+	 *
+	 * Each link is either a URI or a [[Link]] object. The return value of this method should
+	 * be an array whose keys are the relation names and values the corresponding links.
+	 *
+	 * If a relation name corresponds to multiple links, use an array to represent them.
+	 *
+	 * For example,
+	 *
+	 * ```php
+	 * [
+	 *     'self' => 'http://example.com/users/1',
+	 *     'friends' => [
+	 *         'http://example.com/users/2',
+	 *         'http://example.com/users/3',
+	 *     ],
+	 *     'manager' => $managerLink, // $managerLink is a Link object
+	 * ]
+	 * ```
+	 *
+	 * @return array the links
+	 */
+	public function getLinks();
+}
diff --git a/framework/web/UrlRule.php b/framework/web/UrlRule.php
index b069cb3..42c9097 100644
--- a/framework/web/UrlRule.php
+++ b/framework/web/UrlRule.php
@@ -199,7 +199,7 @@ class UrlRule extends Object implements UrlRuleInterface
 			return false;
 		}
 
-		if ($this->verb !== null && !in_array($request->getMethod(), $this->verb, true)) {
+		if (!empty($this->verb) && !in_array($request->getMethod(), $this->verb, true)) {
 			return false;
 		}
 
diff --git a/framework/web/User.php b/framework/web/User.php
index ab33004..c32ae9c 100644
--- a/framework/web/User.php
+++ b/framework/web/User.php
@@ -211,6 +211,23 @@ class User extends Component
 	}
 
 	/**
+	 * Logs in a user by the given access token.
+	 * Note that unlike [[login()]], this method will NOT start a session to remember the user authentication status.
+	 * Also if the access token is invalid, the user will remain as a guest.
+	 * @param string $token the access token
+	 * @return IdentityInterface the identity associated with the given access token. Null is returned if
+	 * the access token is invalid.
+	 */
+	public function loginByAccessToken($token)
+	{
+		/** @var IdentityInterface $class */
+		$class = $this->identityClass;
+		$identity = $class::findIdentityByAccessToken($token);
+		$this->setIdentity($identity);
+		return $identity;
+	}
+
+	/**
 	 * Logs in a user by cookie.
 	 *
 	 * This method attempts to log in a user using the ID and authKey information
diff --git a/tests/unit/framework/base/ModelTest.php b/tests/unit/framework/base/ModelTest.php
index d304741..88926a9 100644
--- a/tests/unit/framework/base/ModelTest.php
+++ b/tests/unit/framework/base/ModelTest.php
@@ -147,7 +147,7 @@ class ModelTest extends TestCase
 		$this->assertTrue($speaker->hasErrors('firstName'));
 		$this->assertFalse($speaker->hasErrors('lastName'));
 
-		$this->assertEquals(['Something is wrong!'], $speaker->getFirstErrors());
+		$this->assertEquals(['firstName' => 'Something is wrong!'], $speaker->getFirstErrors());
 		$this->assertEquals('Something is wrong!', $speaker->getFirstError('firstName'));
 		$this->assertNull($speaker->getFirstError('lastName'));