servitgroup/eloquentrestserver

非常轻量级的REST API的PHP REST服务器

dev-master 2018-05-15 07:34 UTC

This package is not auto-updated.

Last update: 2024-09-19 16:10:45 UTC


README

一个提供非常轻量级REST API的PHP REST服务器。非常容易设置和启动。独立于其他库和框架。支持HTTP认证。

简单的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方法。您会注意到在docblock中有一个新的doc-comment标签。@url将一个URL映射到下面的方法,其形式为:

@url <REQUEST_METHOD> <URL>

在这个特定的例子中,当有人对(假设example.com是我们服务所在的位置)执行GET操作时,它将打印出:

"Hello World"

这是字符串在JSON中的有效表示。

接下来是下一个方法login,我们看到@url将任何POST映射到login方法。从常规的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将在请求之间缓存URL映射,使用APC或文件来加速请求。如果你使用自动加载和这个缓存,你就不需要在每个请求上加载每个控制器文件,只需加载需要的那个。这个缓存只在生产模式下运行。以下是一个示例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中添加authenticateauthorize方法,所有请求都会首先调用这些方法。如果authenticate()authorize()返回false,则服务器将分别发出401无效凭证403未授权响应。如果两者都返回true,则请求将继续调用正确的控制器操作。除非在操作文档中添加@noAuth,否则所有操作都会首先运行授权(我通常将其放在@url映射之上)。

您可以根据自己的需求选择认证和授权方法。如果您只想确认客户端身份,则可以仅实现autenticate。或者,您可以选择两者,然后authorize可以帮助确认当前客户端是否有权访问某个特定操作。有关认证的更多详细信息以及如何将JWT令牌用作载体头部的说明,请参阅示例文件TestAuthControll.php

目前默认认证处理程序支持基于BasicBearer头部的认证,并将[username, password]bearer token分别传递到控制器中的authenticate()方法。如果您想使用如cookie等其他方法来认证客户端,您可以在authenticate方法中做到这一点。您可以通过传递自己的AuthServer接口实现到RestServer实例来替换默认认证处理程序。

    /**
     * include following lines after $server = new RestServer($mode);
     */
    $server->authHandler = new myAuthServer();

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 = '*';

与文件一起工作

使用REST API中的Multipart是一个坏主意,而且RestServer也不支持它。RestServer使用类似于S3服务的直接文件上传方法。您可以每个请求上传一个文件,而无需任何额外的表单数据。

  • 上传:在文件上传操作中,您可以在方法定义中使用两个特殊参数。第一个参数将持有文件内容,而$mime参数可以提供有关文件内容类型的详细信息。

  • 下载:如果操作返回SplFileInfo对象,RestServer将启动文件下载。

有关更多详细信息,请参阅示例中的uploaddownload方法。

抛出和处理错误

您可以通过抛出带有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的方法来添加您自己的自定义错误处理逻辑。

安装

Hand 提供

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

Packagist 提供

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

POSTMAN API 客户端测试链接 https://www.getpostman.com/collections/a420618cd4e0619fdc44