SearchTable Of ContentsPrevious topicNext topicThis Page |
教程 3: 创建 RESTful风格 API¶在本节教程中,我们将展示如何使用不同的HTTP方法创建一个简单的 RESTful 风格的API。
Defining the API¶API包括以下方法:
创建应用¶RESTful风格的应用程序非常简单,我们用不着使用完整的MVC环境来开发它。在这种情况下,我们只要使用 micro application 就可以了。 下面的文件结构足够了: my-rest-api/
models/
Robots.php
index.php
.htaccess
首先,我们需要创建一个.htaccess的文件,包含index.php文件的全部重写规则,下面示例就是此文件的全部: 译者注:使用.htaccess文件,前提是指定了你使用的是Apache WEB Sever. <IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php?_url=/$1 [QSA,L]
</IfModule>
然后,我们按以下方式创建 index.php 文件: <?php
$app = new \Phalcon\Mvc\Micro();
//define the routes here
$app->handle();
现在,我们按我们上面的定义创建路由规则: <?php
$app = new Phalcon\Mvc\Micro();
//Retrieves all robots
$app->get('/api/robots', function() {
});
//Searches for robots with $name in their name
$app->get('/api/robots/search/{name}', function($name) {
});
//Retrieves robots based on primary key
$app->get('/api/robots/{id:[0-9]+}', function($id) {
});
//Adds a new robot
$app->post('/api/robots', function() {
});
//Updates robots based on primary key
$app->put('/api/robots/{id:[0-9]+}', function() {
});
//Deletes robots based on primary key
$app->delete('/api/robots/{id:[0-9]+}', function() {
});
$app->handle();
每个API方法都需要定义一个与定义的HTTP方法相同名称的路由规则,第一个参数传递路由规则,第二个是处理程序,在这种情况下,处理程序是一个匿名函数。路由规则 ‘/api/robots/{id:[0-9]+}’,明确设置’id’参数必须是一个数字。 当用户请求匹配上已定义的路由时,应用程序将执行相应的处理程序。 创建模型(Model)¶API需要提供robots的相关信息,这些数据都存储在数据库中。下面的模型使我们以一种面向对象的方式访问数据表。我们需要使用内置的验证器实现一些业务规则。这样做,会使我们对数据更安全的存储放心,以达到我们想要实现的目的: <?php
use \Phalcon\Mvc\Model\Message;
use \Phalcon\Mvc\Model\Validator\InclusionIn;
use \Phalcon\Mvc\Model\Validator\Uniqueness;
class Robots extends \Phalcon\Mvc\Model
{
public function validation()
{
//Type must be: droid, mechanical or virtual
$this->validate(new InclusionIn(
array(
"field" => "type",
"domain" => array("droid", "mechanical", "virtual")
)
));
//Robot name must be unique
$this->validate(new Uniqueness(
array(
"field" => "name",
"message" => "The robot name must be unique"
)
));
//Year cannot be less than zero
if ($this->year < 0) {
$this->appendMessage(new Message("The year cannot be less than zero"));
}
//Check if any messages have been produced
if ($this->validationHasFailed() == true) {
return false;
}
}
}
现在,我们来创建数据库连接以便使用这个模型: <?php
$di = new \Phalcon\DI\FactoryDefault();
//Set up the database service
$di->set('db', function(){
return new \Phalcon\Db\Adapter\Pdo\Mysql(array(
"host" => "localhost",
"username" => "asimov",
"password" => "zeroth",
"dbname" => "robotics"
));
});
$app = new \Phalcon\Mvc\Micro();
//Bind the DI to the application
$app->setDI($di);
获取数据¶第一个”handler”实现通过HTTP GET获取所有可用的robots。让我们使用PHQL执行一个简单的数据查询,并返回JSON数据格式: <?php
//Retrieves all robots
$app->get('/api/robots', function() use ($app) {
$phql = "SELECT * FROM Robots ORDER BY name";
$robots = $app->modelsManager->executeQuery($phql);
$data = array();
foreach($robots as $robot){
$data[] = array(
'id' => $robot->id,
'name' => $robot->name,
);
}
echo json_encode($data);
});
PHQL,根据我们使用的数据库系统,允许我们使用面向对象的SQL方言,在内部将其转化为普通的SQL语言,此例使用”use”关键词的匿名函数,允许从整体到局部传递变量。 译者注:不了解匿名函数及use语法的,请查看PHP 5.4版本的文档(具体是5.3开始,还是5.4开始我也不太清楚,就不查证了)。 处理程序看起来像这样: <?php
//Searches for robots with $name in their name
$app->get('/api/robots/search/{name}', function($name) use ($app) {
$phql = "SELECT * FROM Robots WHERE name LIKE :name: ORDER BY name";
$robots = $app->modelsManager->executeQuery($phql, array(
'name' => '%'.$name.'%'
));
$data = array();
foreach($robots as $robot){
$data[] = array(
'id' => $robot->id,
'name' => $robot->name,
);
}
echo json_encode($data);
});
通过字段”id”检索与上例相当类似,在这种情况下,如果没有检索到,会提示未找到。 <?php
//Retrieves robots based on primary key
$app->get('/api/robots/{id:[0-9]+}', function($id) use ($app) {
$phql = "SELECT * FROM Robots WHERE id = :id:";
$robot = $app->modelsManager->executeQuery($phql, array(
'id' => $id
))->getFirst();
if ($robot==false) {
$response = array('status' => 'NOT-FOUND');
} else {
$response = array(
'status' => 'FOUND',
'data' => array(
'id' => $robot->id,
'name' => $robot->name
)
);
}
echo json_encode($response);
});
插入数据¶客户端提交JSON包装的字符串,我们也使用PHQL插入: <?php
//Adds a new robot
$app->post('/api/robots', function() use ($app) {
$robot = json_decode($app->request->getRawBody());
$phql = "INSERT INTO Robots (name, type, year) VALUES (:name:, :type:, :year:)";
$status = $app->modelsManager->executeQuery($phql, array(
'name' => $robot->name,
'type' => $robot->type,
'year' => $robot->year
));
//Check if the insertion was successfull
if($status->success()==true){
$robot->id = $status->getModel()->id;
$response = array('status' => 'OK', 'data' => $robot);
} else {
//Change the HTTP status
$this->response->setStatusCode(500, "Internal Error")->sendHeaders();
//Send errors to the client
$errors = array();
foreach ($status->getMessages() as $message) {
$errors[] = $message->getMessage();
}
$response = array('status' => 'ERROR', 'messages' => $errors);
}
echo json_encode($response);
});
更新数据¶更新数据非常类似于插入数据。传递的”id”参数指明哪个robots将被更新: <?php
//Updates robots based on primary key
$app->put('/api/robots/{id:[0-9]+}', function($id) use($app) {
$robot = json_decode($app->request->getRawBody());
$phql = "UPDATE Robots SET name = :name:, type = :type:, year = :year: WHERE id = :id:";
$status = $app->modelsManager->executeQuery($phql, array(
'id' => $id,
'name' => $robot->name,
'type' => $robot->type,
'year' => $robot->year
));
//Check if the insertion was successfull
if($status->success()==true){
$response = array('status' => 'OK');
} else {
//Change the HTTP status
$this->response->setStatusCode(500, "Internal Error")->sendHeaders();
$errors = array();
foreach ($status->getMessages() as $message) {
$errors[] = $message->getMessage();
}
$response = array('status' => 'ERROR', 'messages' => $errors);
}
echo json_encode($response);
});
删除数据¶删除数据非常类似于更新数据。传递的”id”参数指明哪个robot被删除: <?php
//Deletes robots based on primary key
$app->delete('/api/robots/{id:[0-9]+}', function($id) use ($app) {
$phql = "DELETE FROM Robots WHERE id = :id:";
$status = $app->modelsManager->executeQuery($phql, array(
'id' => $id
));
if($status->success()==true){
$response = array('status' => 'OK');
} else {
//Change the HTTP status
$this->response->setStatusCode(500, "Internal Error")->sendHeaders();
$errors = array();
foreach ($status->getMessages() as $message) {
$errors[] = $message->getMessage();
}
$response = array('status' => 'ERROR', 'messages' => $errors);
}
echo json_encode($response);
});
测试应用¶使用 curl 可以测试应用程序中每个操作的正确性: 获取所有robots: curl -i -X GET http://localhost/my-rest-api/api/robots
HTTP/1.1 200 OK
Date: Wed, 12 Sep 2012 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"}]
通过名称查找robot: curl -i -X GET http://localhost/my-rest-api/api/robots/search/Astro
HTTP/1.1 200 OK
Date: Wed, 12 Sep 2012 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 查找 robot: curl -i -X GET http://localhost/my-rest-api/api/robots/3
HTTP/1.1 200 OK
Date: Wed, 12 Sep 2012 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"}}
插入一个新的robot: curl -i -X POST -d '{"name":"C-3PO","type":"droid","year":1977}'
http://localhost/my-rest-api/api/robots
HTTP/1.1 200 OK
Date: Wed, 12 Sep 2012 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"}}
尝试插入一个与存在的robot相同名称的robot: curl -i -X POST -d '{"name":"C-3PO","type":"droid","year":1977}'
http://localhost/my-rest-api/api/robots
HTTP/1.1 500 Internal Error
Date: Wed, 12 Sep 2012 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"]}
或者使用错误的type值更新一个robot: curl -i -X PUT -d '{"name":"ASIMO","type":"humanoid","year":2000}'
http://localhost/my-rest-api/api/robots/4
HTTP/1.1 500 Internal Error
Date: Wed, 12 Sep 2012 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"]}
最后,测试删除一个robot数据: curl -i -X DELETE http://localhost/my-rest-api/api/robots/4
HTTP/1.1 200 OK
Date: Wed, 12 Sep 2012 08:49:29 GMT
Server: Apache/2.2.22 (Unix) DAV/2
Content-Length: 15
Content-Type: text/html; charset=UTF-8
{"status":"OK"}
|