解决slim登录与注册延时问题(使用Slim微框架创建REST)
解决slim登录与注册延时问题(使用Slim微框架创建REST){ 接下来,您需要添加Slim。如果您正在使用Composer(PHP依赖项管理器),只需在$ SLIM_ROOT / composer.json中创建一个文件,其中包含以下内容:1.了解REST 首先,刷新您对REST的理解,也称为Representational State Transfer。REST与SOAP的不同之处在于它基于资源和操作,而不是基于方法和数据类型。资源只是引用您要在其上执行操作的对象或实体的URL(例如/ users或/ photos),并且操作是四个HTTP谓词之一:第2步:下载Slim框架
今天,像CakePHP,Symfony,CodeIgniter和Zend Framework这样的框架被广泛用于PHP应用程序开发,每个框架都提供了独特的功能来简化和简化应用程序开发。
通常,一个成熟的框架是过渡的。考虑对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谓词之一:
第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