教程 3: 创建 RESTfulé£Žæ ¼ API ====================================== 在本节教程ä¸ï¼Œæˆ‘们将展示如何使用ä¸åŒçš„HTTP方法创建一个简å•çš„ RESTful_ é£Žæ ¼çš„API。 * 使用HTTP GET方法获å–ä»¥åŠæ£€ç´¢æ•°æ® * 使用HTTP POSTæ–¹æ³•æ·»åŠ æ•°æ® * 使用HTTP PUTæ–¹æ³•æ›´æ–°æ•°æ® * 使用HTTP DELETEæ–¹æ³•åˆ é™¤æ•°æ® Defining the API ---------------- API包括以下方法: +--------+----------------------------+----------------------------------------------------------+ | Method | URL | Action | +========+============================+==========================================================+ | GET | /api/robots | Retrieves all robots | +--------+----------------------------+----------------------------------------------------------+ | GET | /api/robots/search/Astro | Searches for robots with ‘Astro’ in their name | +--------+----------------------------+----------------------------------------------------------+ | GET | /api/robots/2 | Retrieves robots based on primary key | +--------+----------------------------+----------------------------------------------------------+ | POST | /api/robots | Adds a new robot | +--------+----------------------------+----------------------------------------------------------+ | PUT | /api/robots/2 | Updates robots based on primary key | +--------+----------------------------+----------------------------------------------------------+ | DELETE | /api/robots/2 | Deletes robots based on primary key | +--------+----------------------------+----------------------------------------------------------+ 创建应用 ------------------------ RESTfulé£Žæ ¼çš„åº”ç”¨ç¨‹åºéžå¸¸ç®€å•,我们用ä¸ç€ä½¿ç”¨å®Œæ•´çš„MVC环境æ¥å¼€å‘å®ƒã€‚åœ¨è¿™ç§æƒ…况下,我们åªè¦ä½¿ç”¨ :doc:`micro application <micro>` å°±å¯ä»¥äº†ã€‚ 下é¢çš„æ–‡ä»¶ç»“构足够了: .. code-block:: php my-rest-api/ models/ Robots.php index.php .htaccess 首先,我们需è¦åˆ›å»ºä¸€ä¸ª.htaccess的文件,包å«index.php文件的全部é‡å†™è§„则,下é¢ç¤ºä¾‹å°±æ˜¯æ¤æ–‡ä»¶çš„全部: 译者注:使用.htaccessæ–‡ä»¶ï¼Œå‰ææ˜¯æŒ‡å®šäº†ä½ ä½¿ç”¨çš„æ˜¯Apache WEB Sever. .. code-block:: apacheconf <IfModule mod_rewrite.c> RewriteEngine On RewriteCond %{REQUEST_FILENAME} !-f RewriteRule ^(.*)$ index.php?_url=/$1 [QSA,L] </IfModule> ç„¶åŽï¼Œæˆ‘们按以下方å¼åˆ›å»º index.php 文件: .. code-block:: php <?php $app = new \Phalcon\Mvc\Micro(); //define the routes here $app->handle(); 现在,我们按我们上é¢çš„定义创建路由规则: .. code-block:: php <?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的相关信æ¯ï¼Œè¿™äº›æ•°æ®éƒ½å˜å‚¨åœ¨æ•°æ®åº“ä¸ã€‚下é¢çš„æ¨¡åž‹ä½¿æˆ‘们以一ç§é¢å‘对象的方å¼è®¿é—®æ•°æ®è¡¨ã€‚我们需è¦ä½¿ç”¨å†…置的验è¯å™¨å®žçŽ°ä¸€äº›ä¸šåŠ¡è§„åˆ™ã€‚è¿™æ ·åšï¼Œä¼šä½¿æˆ‘ä»¬å¯¹æ•°æ®æ›´å®‰å…¨çš„å˜å‚¨æ”¾å¿ƒï¼Œä»¥è¾¾åˆ°æˆ‘们想è¦å®žçŽ°çš„ç›®çš„ï¼š .. code-block:: php <?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; } } } 现在,我们æ¥åˆ›å»ºæ•°æ®åº“连接以便使用这个模型: .. code-block:: php <?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æ•°æ®æ ¼å¼ï¼š .. code-block:: php <?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); }); :doc:`PHQL <phql>`,æ ¹æ®æˆ‘们使用的数æ®åº“系统,å…许我们使用é¢å‘对象的SQL方言,在内部将其转化为普通的SQLè¯è¨€ï¼Œæ¤ä¾‹ä½¿ç”¨"use"关键è¯çš„匿å函数,å…è®¸ä»Žæ•´ä½“åˆ°å±€éƒ¨ä¼ é€’å˜é‡ã€‚ 译者注:ä¸äº†è§£åŒ¿å函数åŠuseè¯æ³•的,请查看PHP 5.4版本的文档(具体是5.3开始,还是5.4开始我也ä¸å¤ªæ¸…æ¥šï¼Œå°±ä¸æŸ¥è¯äº†ï¼‰ã€‚ 处ç†ç¨‹åºçœ‹èµ·æ¥åƒè¿™æ ·ï¼š .. code-block:: php <?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"æ£€ç´¢ä¸Žä¸Šä¾‹ç›¸å½“ç±»ä¼¼ï¼Œåœ¨è¿™ç§æƒ…况下,如果没有检索到,会æç¤ºæœªæ‰¾åˆ°ã€‚ .. code-block:: php <?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æ’入: .. code-block:: php <?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将被更新: .. code-block:: php <?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è¢«åˆ é™¤ï¼š .. code-block:: php <?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: .. code-block:: bash 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: .. code-block:: bash 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: .. code-block:: bash 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: .. code-block:: bash 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: .. code-block:: bash 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: .. code-block:: bash 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æ•°æ®ï¼š .. code-block:: bash 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"} 结论 ---------- æ£å¦‚ä½ æ‰€çœ‹åˆ°çš„é‚£æ ·ï¼Œä½¿ç”¨Phalconå¼€å‘RESTfulé£Žæ ¼çš„API相当容易。在接下æ¥çš„æ–‡æ¡£ä¸ï¼Œæˆ‘们会具体讲解如何开å‘微应用(micro applications)以åŠå¦‚何使用 :doc:`PHQL <phql>` 。 .. _curl : http://en.wikipedia.org/wiki/CURL .. _RESTful : http://en.wikipedia.org/wiki/Representational_state_transfer