教程:创建一个简单的REST API

最后更新于:2022-04-02 05:13:15

[TOC] # 教程:创建一个简单的REST API 在本教程中,我们将解释如何使用不同的HTTP方法创建一个提供[RESTful](http://en.wikipedia.org/wiki/Representational_state_transfer) API的简单应用程序: * `GET` 检索和搜索数据 * `POST` 增加数据 * `PUT` 修改数据 * `DELETE` 删除数据 ## 定义API API包含以下方法: | 方法 | URL | Action | | -------- | ------------------------ | ---------------------------------------------- | | `GET` | /api/robots | 检索所有机器人 | | `GET` | /api/robots/search/Astro | 搜索名称中带有“Astro”的机器人| | `GET` | /api/robots/2 | 根据主键2检索机器人 | | `POST` | /api/robots | 添加一个新机器人 | | `PUT` | /api/robots/2 | 根据主键2更新机器人 | | `DELETE` | /api/robots/2 | 删除基于主键2的机器人 | ## 创建应用程序 由于应用程序非常简单,我们不会实现任何完整的MVC环境来开发它。在这种情况下,我们将使用微应用程序来实现我们的目标。 以下文件结构绰绰有余: ```php my-rest-api/ models/ Robots.php index.php .htaccess ``` 首先,我们需要一个`.htaccess`文件,其中包含将请求URI重写到`index.php`文件(应用程序入口点)的所有规则: ```apacheconfig RewriteEngine On RewriteCond %{REQUEST_FILENAME} !-f RewriteRule ^((?s).*)$ index.php?_url=/$1 [QSA,L] ``` 我们的大部分代码都将放在`index.php`中。该文件创建如下: ```php handle(); ``` 现在我们将按照上面的定义创建路由: ```php get( '/api/robots', function () { // Operation to fetch all the robots } ); // Searches for robots with $name in their name $app->get( '/api/robots/search/{name}', function ($name) { // Operation to fetch robot with name $name } ); // Retrieves robots based on primary key $app->get( '/api/robots/{id:[0-9]+}', function ($id) { // Operation to fetch robot with id $id } ); // Adds a new robot $app->post( '/api/robots', function () { // Operation to create a fresh robot } ); // Updates robots based on primary key $app->put( '/api/robots/{id:[0-9]+}', function ($id) { // Operation to update a robot with id $id } ); // Deletes robots based on primary key $app->delete( '/api/robots/{id:[0-9]+}', function ($id) { // Operation to delete the robot with id $id } ); $app->handle(); ``` 每个路由都使用与HTTP方法同名的方法定义,作为第一个参数,我们传递路由模式,然后是处理程序。在这种情况下,处理程序是一个匿名函数。以下路径:`/api/robots/{id:[0-9]+}`,例如,显式设置 `id` 参数必须具有数字格式。 当定义的路由与请求的URI匹配时,应用程序将执行相应的处理程序。 ## 创建模型 我们的API提供有关`robots`的信息,这些数据存储在数据库中。以下模型允许我们以面向对象的方式访问该表。我们使用内置验证器和简单验证实现了一些业务规则。这样做将使我们高枕无忧,保存的数据符合我们的应用程序的要求。此模型文件`Robots.php`应放在`models`文件夹中。 ```php add( 'type', new InclusionIn( [ 'domain' => [ 'Mechanical', 'Virtual', 'Droid', ] ] ) ); $validator->add( 'name', new Uniqueness( [ 'message' => 'The robot name must be unique', ] ) ); return $this->validate($validator); } } ``` 现在,我们必须建立一个这个模型使用的连接,并在我们的app [File:`index.php`]中加载它: ```php registerNamespaces( [ 'Store\Toys' => __DIR__ . '/models/', ] ); $loader->register(); $di = new FactoryDefault(); // Set up the database service $di->set( 'db', function () { return new PdoMysql( [ 'host' => 'localhost', 'username' => 'asimov', 'password' => 'zeroth', 'dbname' => 'robotics', ] ); } ); // Create and bind the DI to the application $app = new Micro($di); ``` ## 检索数据 我们将实现的第一个处理程序是通过方法GET返回所有可用的机器人。让我们使用PHQL来执行这个简单的查询,将结果作为JSON返回。[文件:`index.php`] ```php get( '/api/robots', function () use ($app) { $phql = 'SELECT * FROM Store\Toys\Robots ORDER BY name'; $robots = $app->modelsManager->executeQuery($phql); $data = []; foreach ($robots as $robot) { $data[] = [ 'id' => $robot->id, 'name' => $robot->name, ]; } echo json_encode($data); } ); ``` PHQL,允许我们使用高级,面向对象的SQL方言编写查询,该方言在内部转换为正确的SQL语句,具体取决于我们使用的数据库系统。在匿名函数中使用的`use`允许我们容易地将一些变量从全局传递到本地范围。 按名称处理程序搜索看起来像[File:`index.php`]: ```php get( '/api/robots/search/{name}', function ($name) use ($app) { $phql = 'SELECT * FROM Store\Toys\Robots WHERE name LIKE :name: ORDER BY name'; $robots = $app->modelsManager->executeQuery( $phql, [ 'name' => '%' . $name . '%' ] ); $data = []; foreach ($robots as $robot) { $data[] = [ 'id' => $robot->id, 'name' => $robot->name, ]; } echo json_encode($data); } ); ``` 通过字段`id`搜索它非常相似,在这种情况下,我们还会通知机器人是否被找到[文件:`index.php`]: ```php get( '/api/robots/{id:[0-9]+}', function ($id) use ($app) { $phql = 'SELECT * FROM Store\Toys\Robots WHERE id = :id:'; $robot = $app->modelsManager->executeQuery( $phql, [ 'id' => $id, ] )->getFirst(); // Create a response $response = new Response(); if ($robot === false) { $response->setJsonContent( [ 'status' => 'NOT-FOUND' ] ); } else { $response->setJsonContent( [ 'status' => 'FOUND', 'data' => [ 'id' => $robot->id, 'name' => $robot->name ] ] ); } return $response; } ); ``` ## 插入数据 将数据作为插入请求正文的JSON字符串,我们也使用PHQL进行插入[File:`index.php`]: ```php post( '/api/robots', function () use ($app) { $robot = $app->request->getJsonRawBody(); $phql = 'INSERT INTO Store\Toys\Robots (name, type, year) VALUES (:name:, :type:, :year:)'; $status = $app->modelsManager->executeQuery( $phql, [ 'name' => $robot->name, 'type' => $robot->type, 'year' => $robot->year, ] ); // Create a response $response = new Response(); // Check if the insertion was successful if ($status->success() === true) { // Change the HTTP status $response->setStatusCode(201, 'Created'); $robot->id = $status->getModel()->id; $response->setJsonContent( [ 'status' => 'OK', 'data' => $robot, ] ); } else { // Change the HTTP status $response->setStatusCode(409, 'Conflict'); // Send errors to the client $errors = []; foreach ($status->getMessages() as $message) { $errors[] = $message->getMessage(); } $response->setJsonContent( [ 'status' => 'ERROR', 'messages' => $errors, ] ); } return $response; } ); ``` ## 更新数据 数据更新与插入类似。作为参数传递的`id`表示必须更新哪个机器人[File:`index.php`]: ```php put( '/api/robots/{id:[0-9]+}', function ($id) use ($app) { $robot = $app->request->getJsonRawBody(); $phql = 'UPDATE Store\Toys\Robots SET name = :name:, type = :type:, year = :year: WHERE id = :id:'; $status = $app->modelsManager->executeQuery( $phql, [ 'id' => $id, 'name' => $robot->name, 'type' => $robot->type, 'year' => $robot->year, ] ); // Create a response $response = new Response(); // Check if the insertion was successful if ($status->success() === true) { $response->setJsonContent( [ 'status' => 'OK' ] ); } else { // Change the HTTP status $response->setStatusCode(409, 'Conflict'); $errors = []; foreach ($status->getMessages() as $message) { $errors[] = $message->getMessage(); } $response->setJsonContent( [ 'status' => 'ERROR', 'messages' => $errors, ] ); } return $response; } ); ``` ## 删除数据 数据删除与更新类似。作为参数传递的`id`表示必须删除哪个机器人[File:`index.php`]: ```php delete( '/api/robots/{id:[0-9]+}', function ($id) use ($app) { $phql = 'DELETE FROM Store\Toys\Robots WHERE id = :id:'; $status = $app->modelsManager->executeQuery( $phql, [ 'id' => $id, ] ); // Create a response $response = new Response(); if ($status->success() === true) { $response->setJsonContent( [ 'status' => 'OK' ] ); } else { // Change the HTTP status $response->setStatusCode(409, 'Conflict'); $errors = []; foreach ($status->getMessages() as $message) { $errors[] = $message->getMessage(); } $response->setJsonContent( [ 'status' => 'ERROR', 'messages' => $errors, ] ); } return $response; } ); ``` ## 创建数据库 现在我们将为我们的应用程序创建数据库运行SQL查询,如下所示: ```SQL CREATE DATABASE `robotics`; CREATE TABLE `robotics`.`robots` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `name` varchar(200) COLLATE utf8_bin NOT NULL, `type` varchar(200) COLLATE utf8_bin NOT NULL, `year` smallint(2) unsigned NOT NULL, PRIMARY KEY (`id`) ) ``` ## 测试我们的应用 使用[curl](http://en.wikipedia.org/wiki/CURL),我们将测试应用程序中的每条路径,以验证其是否正常运行。 获取所有机器人: ```bash curl -i -X GET http://localhost/my-rest-api/api/robots HTTP/1.1 200 OK Date: Tue, 21 Jul 2015 07:05:13 GMT Server: Apache/2.2.22 (Unix) DAV/2 Content-Length: 117 Content-Type: text/html; charset=UTF-8 [{"id":"1","name":"Robotina"},{"id":"2","name":"Astro Boy"},{"id":"3","name":"Terminator"}] ``` 按名称搜索机器人: ```bash curl -i -X GET http://localhost/my-rest-api/api/robots/search/Astro HTTP/1.1 200 OK Date: Tue, 21 Jul 2015 07:09:23 GMT Server: Apache/2.2.22 (Unix) DAV/2 Content-Length: 31 Content-Type: text/html; charset=UTF-8 [{"id":"2","name":"Astro Boy"}] ``` 通过其id获取机器人: ```bash curl -i -X GET http://localhost/my-rest-api/api/robots/3 HTTP/1.1 200 OK Date: Tue, 21 Jul 2015 07:12:18 GMT Server: Apache/2.2.22 (Unix) DAV/2 Content-Length: 56 Content-Type: text/html; charset=UTF-8 {"status":"FOUND","data":{"id":"3","name":"Terminator"}} ``` 插入一个新机器人: ```bash curl -i -X POST -d '{"name":"C-3PO","type":"droid","year":1977}' http://localhost/my-rest-api/api/robots HTTP/1.1 201 Created Date: Tue, 21 Jul 2015 07:15:09 GMT Server: Apache/2.2.22 (Unix) DAV/2 Content-Length: 75 Content-Type: text/html; charset=UTF-8 {"status":"OK","data":{"name":"C-3PO","type":"droid","year":1977,"id":"4"}} ``` 尝试插入一个具有现有机器人名称的新机器人: ```bash curl -i -X POST -d '{"name":"C-3PO","type":"droid","year":1977}' http://localhost/my-rest-api/api/robots HTTP/1.1 409 Conflict Date: Tue, 21 Jul 2015 07:18:28 GMT Server: Apache/2.2.22 (Unix) DAV/2 Content-Length: 63 Content-Type: text/html; charset=UTF-8 {"status":"ERROR","messages":["The robot name must be unique"]} ``` 或者更新未知类型的机器人: ```bash curl -i -X PUT -d '{"name":"ASIMO","type":"humanoid","year":2000}' http://localhost/my-rest-api/api/robots/4 HTTP/1.1 409 Conflict Date: Tue, 21 Jul 2015 08:48:01 GMT Server: Apache/2.2.22 (Unix) DAV/2 Content-Length: 104 Content-Type: text/html; charset=UTF-8 {"status":"ERROR","messages":["Value of field 'type' must be part of list: droid, mechanical, virtual"]} ``` 最后,删除一个机器人: ```bash curl -i -X DELETE http://localhost/my-rest-api/api/robots/1 HTTP/1.1 200 OK Date: Tue, 21 Jul 2015 08:49:29 GMT Server: Apache/2.2.22 (Unix) DAV/2 Content-Length: 15 Content-Type: text/html; charset=UTF-8 {"status":"OK"} ``` ## 结论 正如我们所看到的,使用微应用程序和PHQL开发使用Phalcon的[RESTful](http://en.wikipedia.org/wiki/Representational_state_transfer) API很容易。
';