常日,一个成熟的框架是过渡的。考虑对Web运用程序进行原型设计,创建快速而脏的CRUD前端,或支配基本REST API。您可以利用传统框架完成所有这些任务,但学习和利用它所花费的韶光和精力每每超过了好处。现在考虑一下微框架,它可以实现快速的Web运用程序开拓和原型设计,而不须要成熟的框架的性能开销和学习曲线。
在本文中,我向您先容Slim,这是一个PHP微框架,专为快速开拓Web运用程序和API而设计。不要被这个名字所迷惑:Slim带有繁芜的URL路由器,支持页面模板,flash,加密cookie和中间件。它也非常易于理解和利用,并附带出色的文档和激情亲切的开拓职员社区。
本文将勾引您完成利用Slim构建大略REST API的过程。除理解释如何利用Slim的URL路由器实现四种基本REST方法之外,它还演示了Slim的独特功能如何轻松添加API身份验证和多格式支持等高等功能。

1.理解REST
首先,刷新您对REST的理解,也称为Representational State Transfer。REST与SOAP的不同之处在于它基于资源和操作,而不是基于方法和数据类型。资源只是引用您要在其上实行操作的工具或实体的URL(例如/ users或/ photos),并且操作是四个HTTP谓词之一:
GET(检索)
POST(创建)
PUT(更新)
DELETE(删除)
要更好地理解这一点,请考虑一个大略的例子 假设您有一个文件共享运用程序,并且您须要API方法供开拓职员远程向运用程序数据存储添加新文件或从中检索现有文件。在REST方法下,您公开URL端点(例如/ files),并检讨用于访问此URL的HTTP方法以理解所需的操作。例如,您将HTTP数据包发送到/ files以创建新文件,或者向GET /文件发送要求以检索可用文件列表。
这种方法更随意马虎理解,由于它将现有的HTTP谓词映射到CRUD操作。它还花费更少的资源,由于不须要正式定义要求/相应头的数据类型。
URL要求的范例REST约定及其含义是:
GET / items:检索项目列表
GET / items / 123:检索项目123
POST / items:创建一个新项目
PUT / items / 123:更新项目123
DELETE / items / 123:删除项目123
Slim的URL路由器可帮助开拓职员“将资源URI映射到特定HTTP要求方法的回调函数”,例如GET,POST,PUT和DELETE。定义新的REST API很大略,由于定义这些回调方法并利用适当的代码添补它们。这正是您在本文别的部分将要做的事情。
2.创建运用程序存根
在开始实现REST API之前,让我们回顾几个注释。在本文中,我假设您有一个可用的开拓环境(Apache,PHP和MySQL),并且您熟习SQL和XML的根本知识。我还假设您的Apache Web做事器配置为支持虚拟主机,URL重写以及PUT和DELETE要求。
REST API旨在利用资源。这种情形下的资源是文章,每个文章都有标题,URL,日期和唯一标识符。这些资源存储在MySQL数据库中,本文中开拓的示例REST API许可开拓职员利用常规REST约定检索,添加,删除和更新这些文章。本文的大部分内容假定JSON要乞降相应主体。有关如何处理XML要乞降相应的更多信息,请参阅本文后面的支持多相应格式。
第1步:创建运用程序目录构造
切换到Web做事器的文档根目录(常日是Linux®上的/ usr / local / apache / htdocs或Windows®上的c:\ Program Files \ Apache \ htdocs)并为运用程序创建一个新的子目录。将此目录命名为slim /。
shell> cd /usr/local/apache/htdocs
shell> mkdir slim
本文在本文中引用为$ SLIM_ROOT。
例如:我目录构造
第2步:下载Slim框架
接下来,您须要添加Slim。如果您正在利用Composer(PHP依赖项管理器),只需在$ SLIM_ROOT / composer.json中创建一个文件,个中包含以下内容:
{
\"大众require\"大众: {
\"大众slim/slim\"大众: \"大众2.\"大众
}
}
利用Composer利用以下命令安装Slim:
shell> php composer.phar install
要加载Slim,请将此行添加到运用程序的'index.php'文件中:
<?php
require 'vendor/autoload.php';
如果您不该用Composer,您可以手动下载Slim框架并将下载存档中的Slim目录解压缩到PHP包含路径中的目录,或者解压缩到$ SLIM_ROOT / Slim。
例如:我采取的方法
https://slimphp.app/ 此网址为slim的官方中文网站。
我们可以看得官网见告我们的如何安装这个框架,但是我并不建议初学者利用这个方法。可以采取Github办法下载封装包。可以先选择2.x版本。
下载,解压放置项目路径下:
第3步:初始化示例数据库
下一步是初始化运用程序数据库。创建一个名为“articles”的新MySQL表来保存文章记录:
CREATE TABLE IF NOT EXISTS `articles` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`title` text NOT NULL,
`url` text NOT NULL,
`date` date NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8
利用一些启动记录为此表添加数据:
INSERT INTO `articles` (`id`, `title`, `url`, `date`) VALUES
(1, 'Search and integrate Google+ activity streams with PHP applications',
'http://www.ibm.com/developerworks/xml/library/x-googleplusphp/index.html', '2012-07-10');
INSERT INTO `articles` (`id`, `title`, `url`, `date`) VALUES
(2, 'Getting Started with Zend Server CE',
'http://devzone.zend.com/1389/getting-started-with-zend-server-ce/', '2009-03-02');
例如:我创建的数据库与表
要与此MySQL数据库进行交互,还应下载RedBeanPHP,这是一种占用空间小的ORM库。该库可作为单个文件供应,您可以轻松地将其包含在PHP脚本中。请记住将文件复制到PHP包含路径中的目录或$ SLIM_ROOT / RedBean。
http://redbeanphp.com/downloads/
第4步:定义虚拟主机
要使访问运用程序更加随意马虎,请定义新的虚拟主机并将其设置为事情目录。建议利用此可选步骤,尤其是当您利用包含多个正在进行的运用程序的开拓打算机时,由于它会创建目标支配环境的更靠近的副本。Slim还附带一个“.htaccess”文件,许可您利用删除“index.php”前缀的俊秀URL。
要为运用程序设置命名虚拟主机,请打开Apache配置文件(httpd.conf或httpd-vhosts.conf)并将以下行添加到个中:
<VirtualHost 127.0.0.1>
ServerName example.localhost
ServerAlias example.localhost
DocumentRoot \公众${INSTALL_DIR}/wwwwww/Slim\"大众
<Directory \公众${INSTALL_DIR}/www/Slim/\"大众>
Options +Indexes +Includes +FollowSymLinks +MultiViews
AllowOverride All
Require local
</Directory>
</VirtualHost>这些行定义了一个新的虚拟主机http://example.localhost/,其文档根目录对应于$ SLIM_ROOT。重新启动Web做事器以激活这些新设置。请把稳,您可能须要更新网络确当地DNS做事器,以奉告它有关新主机的信息。
完成此任务后,将浏览器指向http://example.localhost/,您该当会看到如图
3.处理GET要求
Slim的事情办法是为HTTP方法和端点定义路由器回调,只需为GET要求调用相应的方法-get(),为POST要求调用post()等等,然后通报URL路由作为第一个参数匹配。方法。该方法的末了一个参数是一个函数,它指定当路由与传入要求匹配时要采纳的操作。
范例的REST API支持两种类型的GET要求,第一种是资源列表(GET /文章),第二种是特定资源(GET / articles / 123)。让我们首先编写代码来处理第一个场景:利用所有可用文章记录列表相应GET / articles要求。
利用清单1中的代码更新$ SLIM / index.php文件:
清单1.多资源GET要求的处理程序
<?php
// load required files
require 'Slim/Slim.php';
require 'RedBean/rb.php';
// register Slim auto-loader
\Slim\Slim::registerAutoloader();
// set up database connection
R::setup('mysql:host=127.0.0.1:3308;dbname=slimtest','host','');
R::freeze(true);
// initialize app
$app = new \Slim\Slim();
// handle GET requests for /articles
$app->get('/articles', function () use ($app) {
// query database for all articles
$articles = R::find('articles');
// send response header for JSON content type
$app->response()->header('Content-Type', 'application/json');
// return JSON-encoded response body with query results
echo json_encode(R::exportAll($articles));
});
// run
$app->run();
清单1加载了Slim和RedBean类。它还注册了Slim自动加载器,以确保根据须要加载其他Slim类(请记住,如果利用Composer,则不须要Slim或注册其自动加载器)。接下来,RedBeanPHP的 R::setup()方法通过向MySQL数据库通报适当的凭据来打开与MySQL数据库的连接,并且其R::freeze() 方法将数据库模式锁定为任何RedBean传播的变动。末了,初始化一个新的Slim运用程序工具; 此工具用作定义路由器回调的紧张掌握点。
下一步是为HTTP方法和端点定义路由器回调。 清单1调用运用程序工具的 get()方法,并将URL路由'/ articles作为其第一个参数通报给它。作为终极参数通报的匿名函数利用RedBeanPHP的R::find()方法从“articles”数据库表中检索所有记录,利用该R::exportAll()方法将它们转换为PHP数组 ,并将数组作为JSON编码的相应体返回给调用者。Slim的相应工具还公开了一个 header()可用于设置任何相应头的方法; 在清单1中,header() 方法设置'Content-Type'标头,以便客户端知道将其阐明为JSON相应。
请把稳,您可以利用$app->contentType()赞助方法而不是header()方法来直接访问要求的内容类型,或者将Slim的相应工具视为数组(它实现ArrayAccess接口)并设置标头。
同样,您可以通过为URL路由“/ articles /:id”添加回调来处理特定资源的GET要求。清单2 解释了。
清单2.单资源GET要求的处理程序
<?php
// load required files
require 'Slim/Slim.php';
require 'RedBean/rb.php';
// register Slim auto-loader
\Slim\Slim::registerAutoloader();
// set up database connection
R::setup('mysql:host=127.0.0.1:3308;dbname=slimtest','host','');
R::freeze(true);
// initialize app
$app = new \Slim\Slim();
class ResourceNotFoundException extends Exception {}
// handle GET requests for /articles/:id
$app->get('/articles/:id', function ($id) use ($app) {
try {
// query database for single article
$article = R::findOne('articles', 'id=?', array($id));
if ($article) {
// if found, return JSON response
$app->response()->header('Content-Type', 'application/json');
echo json_encode(R::exportAll($article));
} else {
// else throw exception
throw new ResourceNotFoundException();
}
} catch (ResourceNotFoundException $e) {
// return 404 server error
$app->response()->status(404);
} catch (Exception $e) {
$app->response()->status(400);
$app->response()->header('X-Status-Reason', $e->getMessage());
}
});
// run
$app->run();
Slim可以自动从URL中提取路由参数。清单2解释了这一点,由于它从GET要求中提取:id参数并将其通报给回调函数。在该函数内,该R::findOne()方法检索相应的资源记录。此记录作为JSON编码的相应正文返回给客户端。如果供应的标识符无效且找不到匹配的资源,则回调函数会抛出自定义的ResourceNotFoundException。然后try-catch块将其转换为404 Not Found做事器相应。
结果:成功
结果:不堪利
Slim还支持路由条件,许可开拓职员为路由参数指定正则表达式。如果不知足这些参数,则不会实行路由回调。您可以在每个路由的根本上指定条件,如下所示:
<?php
$app->get('/articles/:id', function ($id) use ($app) {
// callback code
})->conditions(array('id' => '([0-9]{1,}'));
或者您可以利用该setDefaultConditions()方法全局指定条件 ,如下所示:
<?php
// set default conditions for route parameters
\Slim\Route::setDefaultConditions(array(
'id' => '[0-9]{1,}',
));
Slim运用程序还公开了一种notFound()方法,您可以利用该方法为无法匹配路由的方案定义自定义代码。此方法是抛出自定义非常并手动设置404做事器相应代码的替代方法。
4.处理POST要求
处理POST要求更繁芜一些。常日的REST约定是POST要求创建一个新资源,要求主体包含资源的所有必要输入(在本例中为作者和标题)。回调函数须要解码要求主体,将其转换为RedBean工具,并将其保存到数据库,然后返回新创建的资源的JSON编码表示。
清单3显示了处理POST要求的代码。
清单3. POST要求的处理程序
// handle POST requests to /articles
$app->post('/articles', function () use ($app) {
try {
// get and decode JSON request body
$request = $app->request();
$body = $request->getBody();
$input = json_decode($body);
// store article record
$article = R::dispense('articles');
$article->title = (string)$input->title;
$article->url = (string)$input->url;
$article->date = (string)$input->date;
$id = R::store($article);
// return JSON-encoded response body
$app->response()->header('Content-Type', 'application/json');
echo json_encode(R::exportAll($article));
} catch (Exception $e) {
$app->response()->status(400);
$app->response()->header('X-Status-Reason', $e->getMessage());
}
});
在清单3中,回调函数利用要求工具的getBody()检索要求的主体(假定为JSON编码的数据包),并利用json_decode()函数将其转换为PHP工具。接下来,利用PHP工具中的属性初始化新的RedBeanPHP文章工具,并利用该R::store()方法将其存储在数据库中。然后将新工具导出到数组并作为JSON数据包返回到客户端。
{
\"大众title\"大众: \公众test\"大众,
\"大众url\"大众: \"大众http://example.localhost/Slim/articles\"大众,
\"大众date\公众: \"大众2019-03-21\"大众
}
结果:
利用Navicat数据库软件查当作果如下:
5.处理PUT要求
PUT要求用于指示对现有资源的修正,并且因此在要求字符串中包括资源标识符。成功的PUT意味着现有资源已被PUT要求体中指定的资源更换。对成功PUT的相应可以是状态代码200(OK),个中相应主体包含更新资源的表示,或者状态代码204(No Content)具有空相应主体。
清单4显示了处理PUT要求的代码。
清单4. PUT要求的处理程序
// handle PUT requests to /articles/:id
$app->put('/articles/:id', function ($id) use ($app) {
try {
// get and decode JSON request body
$request = $app->request();
$body = $request->getBody();
$input = json_decode($body);
// query database for single article
$article = R::findOne('articles', 'id=?', array($id));
// store modified article
// return JSON-encoded response body
if ($article) {
$article->title = (string)$input->title;
$article->url = (string)$input->url;
$article->date = (string)$input->date;
R::store($article);
$app->response()->header('Content-Type', 'application/json');
echo json_encode(R::exportAll($article));
} else {
throw new ResourceNotFoundException();
}
} catch (ResourceNotFoundException $e) {
$app->response()->status(404);
} catch (Exception $e) {
$app->response()->status(400);
$app->response()->header('X-Status-Reason', $e->getMessage());
}
});
修正:不堪利
修正
{
\公众title\"大众: \公众test2\"大众,
\"大众url\公众: \"大众http://example.localhost/Slim/articles\"大众,
\公众date\"大众: \"大众2019-03-22\"大众
}
修正:成功
利用Navicat数据库软件查当作果如下:
6.处理DELETE要求
您还可以编写回调来处理从数据存储中删除指定资源的DELETE要求。清单5 显示了必要的代码。
清单5. DELETE要求的处理程序
// handle DELETE requests to /articles/:id
$app->delete('/articles/:id', function ($id) use ($app) {
try {
// query database for article
$request = $app->request();
$article = R::findOne('articles', 'id=?', array($id));
// delete article
if ($article) {
R::trash($article);
$app->response()->status(204);
} else {
throw new ResourceNotFoundException();
}
} catch (ResourceNotFoundException $e) {
$app->response()->status(404);
} catch (Exception $e) {
$app->response()->status(400);
$app->response()->header('X-Status-Reason', $e->getMessage());
}
});
删除:不堪利
删除:成功
利用Navicat数据库软件查当作果如下:
与清单4一样,清单5利用URL中的资源标识符从数据库中检索相应的资源作为工具,然后利用该R::trash() 方法永久删除它。对成功DELETE要求的相应可以是200(OK),其状态在相应正文中,或者204(No Content)具有空相应正文; 清单5实行后者。
7.利用路由中间件添加身份验证
除了灵巧的路由,Slim还支持所谓的“路由中间件”。最大略的说,中间件是一个或多个自定义函数,它们在相应路由的回调函数之前调用。中间件可以对要求实行自定义处理; 作为关于“机架中间件用例示例”的Ephemera博客文章阐明说,中间件“......可以操纵要求,相应,它可以完备停滞处理,或者实行完备不干系的操作,例如日志记录。”
为相识释Slim的中间件架构如何事情,请考虑一个常见的API哀求:身份验证。利用中间件,可以通过在许可处理之前通过自定义身份验证方法通报要求来确保API要求得到精确身份验证。
清单6展示了一个大略的身份验证函数,并将此函数作为中间件添加到GET要求处理程序中:
清单6.身份验证中间件
<?php
// do initial application and database setup
// initialize app
$app = new \Slim\Slim();
// route middleware for simple API authentication
function authenticate(\Slim\Route $route) {
$app = \Slim\Slim::getInstance();
if (validateUserKey() === false) {
$app->halt(401);
}
}
function validateUserKey($uid, $key) {
// insert your (hopefully complex) validation routine here
}
// handle GET requests for /articles
$app->get('/articles', 'authenticate', function () use ($app) {
// query database for all articles
$articles = R::find('articles');
// send response header for JSON content type
$app->response()->header('Content-Type', 'application/json');
// return JSON-encoded response body with query results
echo json_encode(R::exportAll($articles));
});
// run
$app->run();
清单6中的示例authenticate()函数在GET路由处理程序中作为中间件引用,并自动吸收路由工具作为参数。它还可以利用该Slim::getInstance() 方法访问Slim运用程序工具。
现在,当用户要求URL“/ articles”时,将在处理要求之前调用authenticate()函数。此函数利用自定义验证例程来验证要求,并仅在验证例程返回true时许可进一步处理。如果验证失落败,则authenticate()函数将停滞处理并向客户端发送401 Authorization Required相应。
清单7供应了一个更详细的示例,解释如何authenticate()通过利用用户标识符“demo”和API密钥“demo”检讨cookie 来实现该方法。这些cookie是通过调用新的API方法设置的/demo,该方法许可客户端在五分钟内临时访问API。
清单7.身份验证中间件的大略实现
<?php
// do initial application and database setup
// initialize app
$app = new \Slim\Slim();
// route middleware for simple API authentication
function authenticate(\Slim\Route $route) {
$app = \Slim\Slim::getInstance();
$uid = $app->getEncryptedCookie('uid');
$key = $app->getEncryptedCookie('key');
if (validateUserKey($uid, $key) === false) {
$app->halt(401);
}
}
function validateUserKey($uid, $key) {
// insert your (hopefully more complex) validation routine here
if ($uid == 'demo' && $key == 'demo') {
return true;
} else {
return false;
}
}
// handle GET requests for /articles
$app->get('/articles', 'authenticate', function () use ($app) {
// query database for all articles
$articles = R::find('articles');
// send response header for JSON content type
$app->response()->header('Content-Type', 'application/json');
// return JSON-encoded response body with query results
echo json_encode(R::exportAll($articles));
});
// generates a temporary API key using cookies
// call this first to gain access to protected API methods
$app->get('/demo', function () use ($app) {
try {
$app->setEncryptedCookie('uid', 'demo', '5 minutes');
$app->setEncryptedCookie('key', 'demo', '5 minutes');
} catch (Exception $e) {
$app->response()->status(400);
$app->response()->header('X-Status-Reason', $e->getMessage());
}
});
// run
$app->run();
在这种情形下,客户端首先向API端点'/ demo'发送要求,该端点设置加密的cookie,其“demo”凭据有效期为5分钟。这些cookie将自动伴随对同一主机的任何后续要求。 )中间件函数,附加到“/ articles”URL端点的GET处理程序,检讨每个传入的GET要求以获取这些cookie。然后利用validateUserKey()函数验证用户的凭据(在此示例中,只需通过检讨对付值“demo”)。在cookie过期后,validateUserKey()函数返回false,authenticate()中间件终止要求,并禁止访问带有401做事器缺点的“/ article”URL端点。
随附的代码存档利用相同的中间件来验证POST,PUT和DELETE要求。出于显而易见的缘故原由,该系统不是特殊安全,不应该在生产环境中利用; 它仅仅是出于解释目的而包含在本文中。
如本例所示,路由中间件可以派上用场进行要求预处理; 它还可以用于其他任务,例如要求过滤或日志记录。
结果:
8.支持多相应格式
前面的部分解释了如何设置大略的基于JSON的REST API。XML也是一种盛行的数据交流格式,并且常日须要在REST API中支持XML要乞降相应主体。
Slim供应对要求标头的完备访问权限。知足此哀求的最大略方法之一是利用“Content-Type”要求标头来确定事务中利用的数据格式。考虑 清单8,它根据“Content-Type”标题修正GET要求的回调函数,以返回XML或JSON相应主体:
清单8. GET要求的处理程序,支持XML和JSON格式
<?php
// do initial application and database setup
// initialize app
$app = new \Slim\Slim();
// handle GET requests for /articles
$app->get('/articles', function () use ($app) {
try {
// query database for articles
$articles = R::find('articles');
// check request content type
// format and return response body in specified format
$mediaType = $app->request()->getMediaType();
if ($mediaType == 'application/xml') {
$app->response()->header('Content-Type', 'application/xml');
$xml = new SimpleXMLElement('<root/>');
$result = R::exportAll($articles);
foreach ($result as $r) {
$item = $xml->addChild('item');
$item->addChild('id', $r['id']);
$item->addChild('title', $r['title']);
$item->addChild('url', $r['url']);
$item->addChild('date', $r['date']);
}
echo $xml->asXml();
} else if (($mediaType == 'application/json')) {
$app->response()->header('Content-Type', 'application/json');
echo json_encode(R::exportAll($articles));
}
} catch (Exception $e) {
$app->response()->status(400);
$app->response()->header('X-Status-Reason', $e->getMessage());
}
});
// run
$app->run();
在清单8中,要求工具的 getMediaType()方法用于检索要求的内容类型。
对付内容类型为“application / json”的要求,将以JSON格式返回文章列表。
对付内容类型为“application / xml”的要求,文章列表将利用SimpleXML格式化为XML文档,并以XML格式返回。
清单9还为POST要求添加了XML支持:
清单9. POST要求的处理程序,支持XML和JSON格式
<?php
// do initial application and database setup
// initialize app
$app = new \Slim\Slim();
$app->post('articles', function () use ($app) {
try {
// check request content type
// decode request body in JSON or XML format
$request = $app->request();
$mediaType = $request->getMediaType();
$body = $request->getBody();
if ($mediaType == 'application/xml') {
$input = simplexml_load_string($body);
} elseif ($mediaType == 'application/json') {
$input = json_decode($body);
}
// create and store article record
$article = R::dispense('articles');
$article->title = (string)$input->title;
$article->url = (string)$input->url;
$article->date = (string)$input->date;
$id = R::store($article);
// return JSON/XML response
if ($mediaType == 'application/xml') {
$app->response()->header('Content-Type', 'application/xml');
$xml = new SimpleXMLElement('<root/>');
$result = R::exportAll($article);
foreach ($result as $r) {
$item = $xml->addChild('item');
$item->addChild('id', $r['id']);
$item->addChild('title', $r['title']);
$item->addChild('url', $r['url']);
$item->addChild('date', $r['date']);
}
echo $xml->asXml();
} elseif ($mediaType == 'application/json') {
$app->response()->header('Content-Type', 'application/json');
echo json_encode(R::exportAll($article));
}
} catch (Exception $e) {
$app->response()->status(400);
$app->response()->header('X-Status-Reason', $e->getMessage());
}
});
// run
$app->run();
在清单9中,根据内容类型是“application / json”还是“application / xml”,要求体利用json_decode()或simplexml_load_string()转换为PHP工具。然后将工具保存到数据库,并将资源的JSON或XML表示返回给客户端。
9.结论
Slim供应了一个强大的,可扩展的框架,用于构建REST API,使运用程序开拓职员可以轻松地利用直不雅观的体系构造模式访问运用程序功能。Slim的URL匹配和路由功能,再加上其轻量级API和对各种HTTP方法的支持,使其成为快速API原型设计和实现的空想选择。
有关本文中实现的所有代码,请参阅下载,以及可用于在示例API上实行GET,POST,PUT和DELETE要求的大略基于jQuery的测试脚本。我建议您获取代码,开始利用它,并考试测验添加新内容。我担保你不会毁坏任何东西,它肯定会增加你的学习。
附原址:https://www.ibm.com/developerworks/xml/library/x-slim-rest/index.html