startbit/restserver

startbit 提供的超轻量级 REST API PHP 服务器

dev-master 2018-10-23 09:44 UTC

This package is auto-updated.

Last update: 2024-09-16 21:31:21 UTC


README

A PHP REST server for providing a very light-weight REST API. Very easy to set up and get going. Independent from other libraries and frameworks. Supports HTTP authentication.

PHP 简单 REST 服务器

在构建了几个使用 Zend Framework 的 RESTful 服务之后,我决定创建一个极其简单的 REST 服务器,以便我可以跳过所有不需要的功能,以及 Zend Framework MVC 中的大量类。还有一些有用的功能可以添加(例如,XML 支持),但总体来说,我对自己的成果相当满意。

我的解决方案 RestServer 目前是一个 JSON REST 服务器。添加对 XML 或其他格式的支持应该是很简单的,但需要对对象在 XML 中的外观做出假设(XML-RPC 风格,自定义 XML 格式等)。首先,我们将查看您编写的用于处理请求的类,然后我们将查看如何在 index.php 文件中将其组合在一起。

REST 控制器

RestServer 类假设您正在使用 URL 重写,并查看请求的 URL 以映射到必要的操作。从 URL 到类方法的映射都在类的文档注释中。以下是一个处理某些用户操作的类示例:

class TestController
{
    /**
     * Returns a JSON string object to the browser when hitting the root of the domain
     *
     * @url GET /
     */
    public function test()
    {
        return "Hello World";
    }

    /**
     * Logs in a user with the given username and password POSTed. Though true
     * REST doesn't believe in sessions, it is often desirable for an AJAX server.
     *
     * @url POST /login
     */
    public function login()
    {
        $username = $_POST['username'];
        $password = $_POST['password'];
        // validate input and log the user in
    }

    /**
     * Gets the user by id or current user
     *
     * @url GET /users/$id
     * @url GET /users/current
     */
    public function getUser($id = null)
    {
        if ($id) {
            $user = User::load($id); // possible user loading method
        } else {
            $user = $_SESSION['user'];
        }

        return $user; // serializes object into JSON
    }

    /**
     * Saves a user to the database
     *
     * @url POST /users
     * @url PUT /users/$id
     */
    public function saveUser($id = null, $data)
    {
        // ... validate $data properties such as $data->username, $data->firstName, etc.
        $data->id = $id;
        $user = User::saveUser($data); // saving the user to the database
        return $user; // returning the updated or newly created user object
    }

    /**
     * Gets user list
     *
     * @url GET /users
     */
    public function listUsers($query)
    {
        $users = array('Andra Combes', 'Valerie Shirkey', 'Manda Douse', 'Nobuko Fisch', 'Roger Hevey');
        if (isset($query['search'])) {
          $users = preg_grep("/$query[search]/i", $users);
        }
        return $users; // serializes object into JSON
    }
}

让我们详细分析上面的 TestController 类,讨论展示的功能。首先,我们将查看 test 方法。您会注意到文档块中有一个新的文档注释标签类型。@url 将 URL 映射到下面的方法,其格式为

@url <REQUEST_METHOD> <URL>

在这个特定的例子中,当有人对 http://www.example.com/(假设 example.com 是我们的服务所在的位置)进行 GET 请求时,它将输出

"Hello World"

这是一个有效的 JSON 字符串表示。

接下来是下一个方法,login,我们看到 @url 将任何 POST 映射到 http://www.example.com/loginlogin 方法。从常规的 web 类型 POST 获取数据与任何 PHP 应用程序相同,允许您与该 REST 服务器一起使用自己的验证或其他框架。如果需要,也可以保留会话。虽然保留会话不是真正的 REST 风格,但我们通常只需要 REST 服务器向我们的 Ajax 应用程序提供数据,而使用会话可能更容易一些。

接下来是我们的 getUser 方法(你会发现我给方法命名并不重要,因为我们的 @url 指令定义了哪些 URL 映射到方法)。在这里你可以看到几个地方。首先,这个方法有多个 @url 映射。其次,第一个 URL 映射中有一个奇怪的 /$id。RestServer 将任何 :keyword 占位符视为 URL 中的通配符,并将 URL 的这部分传递给方法中同名参数。在这个例子中,当访问 http://www.example.com/users/1234 时,$id 将等于 1234。当访问 http://www.example.com/users/current 时,$id 将等于 null。参数的顺序无关紧要,只要它们的名称与占位符(:id$id:username$username)相同。当你有多个不需要参数的 URL 映射时,请确保你的参数是可选的($id = null)。否则,将会抛出错误,告诉你没有传递必填参数。

getUser 中需要注意的是,这个方法只是简单地返回一个 User 对象。这个对象会被序列化为 JSON(或可能是 XML)并打印出来,以便应用程序消费。

接下来是 saveUser 方法。你可以看到这里我们又有了多个 URL 映射,这次它们还有不同的 HTTP 方法(POST 和 PUT)用于创建和更新用户。这里的新特点是 $data 变量。这是一个特殊的关键字参数,它将包含发送到服务器的任何 POST 或 PUT 的值。这与常规的 Web POST 不同,因为它不需要仅是键值对,可以是像 JSON 一样健壮,可以发送复杂对象。例如,常规 Web POST 的正文,比如登录请求,可能看起来像这样

username=bob&password=supersecretpassw0rd

但是为我们的 saveUser 方法 POST 新用户对象可能看起来像这样

{ "username": "bob", "password": "supersecretpassword", "firstName": "Bob", "lastName": "Smith" }

因此,你能够允许除了常规 Web 风格的 POST 以外还可以 POST JSON。

最后,我们来看 listUsers 方法。它和 test 方法一样简单,但 $query 参数是新的。这个特殊参数可以用来读取查询字符串,并将查询字符串参数作为关联数组保存。例如,如果客户端通过 URL /users?search=Manda 请求这个 API,那么 $query 参数将包含 [search => Manada]

我把处理请求的这些类称为 Controllers。它们可以完全自包含,包括 URL 映射、数据库配置等,这样你就可以将它们轻松地放入其他 RestServer 服务中。

REST index.php

为了启动整个服务器,你需要创建一个 index.php 文件,将 URL 重写指向它(这是一个其他地方可以学习的话题),并创建 RestServer 并添加控制器类来处理请求。RestServer 会使用 APC 或文件在请求之间缓存 URL 映射,以加快请求速度。如果你使用自动加载和这个缓存,就不需要每次请求都加载每个控制器文件,只需加载所需的那个。这个缓存只在生产模式下运行。以下是一个示例 index.php 文件

spl_autoload_register(); // don't load our classes unless we use them

$mode = 'debug'; // 'debug' or 'production'
$server = new RestServer($mode);
// $server->refreshCache(); // uncomment momentarily to clear the cache if classes change in production mode

$server->addClass('TestController');
$server->addClass('ProductsController', '/products'); // adds this as a base to all the URLs in this class

$server->handle();

就这样。你可以添加任意多的类。如果有冲突,后来添加的类将覆盖先前添加的重复 URL 映射。并且 addClass 的第二个参数可以是一个基本 URL,它将被添加到给定类中的 URL 映射之前,这允许你更加模块化。

您可以查看RestServer类,复制它并用于您的目的。它在MIT许可下。要添加的功能包括XML支持和HTTP身份验证支持。如果您使这个类变得更好,请通过留下评论与大家分享您的更新。我会尽量在分享新功能时保持这个类的更新。希望您喜欢!

祝您好运,如果您最终使用了它,请告诉我!

更新:我为可能需要的人提供了一个示例 .htaccess 文件。它只会重写不存在文件的请求,所以您可以在您的webroot中有图像、CSS或其他PHP文件,并且它们仍然可以工作。任何会导致404的都会重定向到您的index.php文件。

DirectoryIndex index.php
<IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteRule ^$ index.php [QSA,L]
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule ^(.*)$ index.php [QSA,L]
</IfModule>

身份验证

身份验证对每个应用程序都是独特的。但将您的身份验证机制与RestServer结合在一起很简单。通过简单地给您的Controller添加一个名为authorize的方法,所有请求都将首先调用该方法。如果authorize()返回false,服务器将发出401未授权响应。如果authorize()返回true,请求将继续调用正确的控制器操作。除非您在操作文档中添加了@noAuth,否则所有操作都将首先运行授权(我通常将其放在@url映射上方)。

在您的身份验证方法中,您可以使用PHP的getallheaders函数或$_COOKIE来授权您的用户。这就是您从数据库中加载用户对象并将其设置为$this->user = getUserFromDatabase()的地方,这样您的操作就可以在稍后被调用时访问它。

RestServer旨在是一个简单的机制,将您的应用程序映射到REST API。其余的细节由您决定。

跨源资源共享

出于安全原因,浏览器限制了从脚本内部发起的跨源HTTP或REST请求。因此,使用浏览器从REST API的Web应用程序只能向其自己的域发出API请求。为了覆盖此限制,可以在REST index.php文件中包含以下代码,以配置RestServer允许跨源请求。

    /**
     *
     * include following lines after $server = new RestServer($mode); 
     */
     $server->useCors = true;
     $server->allowedOrigin = 'http://example.com';
     // or use array of multiple origins
     $server->allowedOrigin = array('http://example.com', 'https://example.com');
     // or a wildcard
     $server->allowedOrigin = '*';

抛出和处理错误

您可以通过抛出具有RestException类的异常来轻松地向您的API用户提供错误。示例

    /**
     * Gets the user by id or current user
     *
     * @url GET /users/$id
     * @url GET /users/current
     */
    public function getUser($id = null)
    {
        if ($id) {
            $user = User::load($id); // possible user loading method

            if (!$user) {
                throw new RestException(404, 'User not found');
            }

        } else {
            $user = $_SESSION['user'];
        }

        return $user; // serializes object into JSON
    }

您控制您的REST服务如何处理错误。您可以使用$server->addErrorClass('ErrorController');添加错误控制器。此控制器可以定义名为handle401handle404的方法,以添加您自己的自定义错误处理逻辑。

安装

通过手工

cd <your project>
mkdir -p vendor/startbit/RestServer
cd vendor/startbit/RestServer
git clone https://github.com/jugal-startbit/RestServer .
composer install

通过Packagist

cd <your project>
composer require 'startbit/restserver:dev-master'