ThinkChat🤖让你学习和工作更高效,注册即送10W Token,即刻开启你的AI之旅 广告
[TOC] # 微应用 Phalcon提供了一个非常“瘦”的应用程序,因此您可以使用最少的PHP代码创建“Micro”应用程序。微应用程序适用于开销非常低的小型应用程序。此类应用程序例如是我们的网站,我们的商城,API,原型等。 ```php <?php use Phalcon\Mvc\Micro; $app = new Micro(); $app->get( '/orders/display/{name}', function ($name) { echo "<h1>This is order: {$name}!</h1>"; } ); $app->handle(); ``` ## 建微应用 `Phalcon\Mvc\Micro`类是负责创建Micro应用程序的类。 ```php <?php use Phalcon\Mvc\Micro; $app = new Micro(); ``` ## 路由 在 `Phalcon\Mvc\Micro` 应用程序中定义路径非常简单。路由定义如下: ```text Application -> (method/verb) -> (route url/regex, callable PHP function) ``` ### 设置 路由由 `Phalcon\Mvc\Router`对象处理。 >[danger] 路由必须始终以`/`开头 通常,应用程序中的起始路由是route `/`,在大多数情况下,它是通过GET HTTP方法访问的: ```php <?php // 起始路由 $app->get( '/', function () { echo '<h1>Welcome!</h1>'; } ); ``` #### 应用对象 可以使用 `Phalcon\Mvc\Micro` 应用程序对象设置路由,如下所示: ```php use Phalcon\Mvc\Micro; $app = new Micro(); // 匹配GET请求 $app->get( '/orders/display/{name}', function ($name) { echo "<h1>This is order: {$name}!</h1>"; } ); ``` #### Router 对象 你也可以创建一个 `Phalcon\Mvc\Router` 对象,在那里设置路由,然后将其注入依赖注入容器中。 ```php use Phalcon\Mvc\Micro; use Phalcon\Mvc\Router; $router = new Router(); $router->addGet( '/orders/display/{name}', 'OrdersClass::display'; } ); $app = new Micro(); $app->setService('router', $router, true); ``` 使用 `Phalcon\Mvc\Micro` 应用程序动词方法(get,post等)设置路径比设置具有相关路径的路由器对象然后将其注入应用程序要容易得多。 每种方法都有其优点和缺点。这完全取决于您的应用程序的设计和需求。 ### 重写规则 为了使路由起作用,需要在Web服务器的特定站点配置中进行某些配置更改。 [Apache Rewrite Rules](http://httpd.apache.org/docs/current/rewrite/) 和 [NGINX Rewrite Rules](https://www.nginx.com/blog/creating-nginx-rewrite-rules/) 中概述了这些更改。 ### 处理程序 处理程序是附加到路由的可调用代码段。匹配路由时,将使用所有已定义的参数执行处理程序。处理程序是PHP中存在的任何可调用代码段。 #### 定义 Phalcon提供了几种将处理程序附加到路径的方法。您的应用程序需求和设计以及编码风格将是影响您选择实施的因素。 ##### 匿名函数 最后,我们可以使用匿名函数(如上所示)来处理请求 ```php $app->get( '/orders/display/{name}', function ($name) { echo "<h1>This is order: {$name}!</h1>"; } ); ``` 访问匿名函数内的 `$app` 对象可以通过如下注入来实现: ```php $app->get( '/orders/display/{name}', function ($name) use ($app) { $content = "<h1>This is order: {$name}!</h1>"; $app->response->setContent($content); $app->response->send(); } ); ``` ##### 函数 我们可以将函数定义为处理程序并将其附加到特定路径。 ```php // With a function function order_display($name) { echo "<h1>This is order: {$name}!</h1>"; } $app->get( '/orders/display/{name}', 'orders_display' ); ``` ##### 静态方法 我们也可以使用静态方法作为我们的处理程序,如下所示: ```php class OrdersClass { public static function display($name) { echo "<h1>This is order: {$name}!</h1>"; } } $app->get( '/orders/display/{name}', 'OrdersClass::display' ); ``` ##### 对象方法 我们也可以在对象中使用一个方法: ```php class OrdersClass { public function display($name) { echo "<h1>This is order: {$name}!</h1>"; } } $orders = new OrdersClass(); $app->get( '/orders/display/{name}', [ $orders, 'display', ] ); ``` ##### 控制器 使用 `Phalcon\Mvc\Micro` ,您可以创建微型或中型应用程序。中型应用程序使用微架构,但扩展它以使用超过Micro而不是Full应用程序。 在中型应用程序中,您可以在控制器中组织处理程 ```php <?php use Phalcon\Mvc\Micro\Collection as MicroCollection; $orders = new MicroCollection(); // 设置主处理程序。即。控制器实例 $orders->setHandler(new OrdersController()); // 为所有路由设置公共前缀 $orders->setPrefix('/orders'); // 在OrdersController中使用'index'方法 $orders->get('/', 'index'); // 在OrdersController中使用'show'方法 $orders->get('/display/{slug}', 'show'); $app->mount($orders); ``` `OrdersController` 可能如下所示: ```php <?php use Phalcon\Mvc\Controller; class OrdersController extends Controller { public function index() { // ... } public function show($name) { // ... } } ``` 由于我们的控制器扩展了 `Phalcon\Mvc\Controller`,因此所有依赖注入服务都具有各自的注册名称。例如: ```php <?php use Phalcon\Mvc\Controller; class OrdersController extends Controller { public function index() { // ... } public function show($name) { $content = "<h1>This is order: {$name}!</h1>"; $this->response->setContent($content); return $this->response; } } ``` #### 延迟加载 为了提高性能,您可以考虑为控制器(处理程序)实现延迟加载。仅当相关路线匹配时才会加载控制器。 在Phalcon\Mvc\Micro\Collection`中设置处理程序时,可以轻松实现延迟加载: ```php $orders->setHandler('OrdersController', true); $orders->setHandler('Blog\Controllers\OrdersController', true); ``` ##### 用例 我们正在为在线商店开发API。端点是`/users`,`/orders`和`/products`。每个端点都使用处理程序注册,每个处理程序都是具有相关操作的控制器。 我们用作处理程序的控制器如下: ```php <?php use Phalcon\Mvc\Controller; class UsersController extends Controller { public function get($id) { // ... } public function add($payload) { // ... } } class OrdersController extends Controller { public function get($id) { // ... } public function add($payload) { // ... } } class ProductsController extends Controller { public function get($id) { // ... } public function add($payload) { // ... } } ``` 我们注册处理程序: ```php <?php use Phalcon\Mvc\Micro\Collection as MicroCollection; // 用户处理程序 $users = new MicroCollection(); $users->setHandler(new UsersController()); $users->setPrefix('/users'); $users->get('/get/{id}', 'get'); $users->get('/add/{payload}', 'add'); $app->mount($users); // 订单处理程序 $orders = new MicroCollection(); $orders->setHandler(new OrdersController()); $orders->setPrefix('/users'); $orders->get('/get/{id}', 'get'); $orders->get('/add/{payload}', 'add'); $app->mount($orders); // 产品处理程序 $products = new MicroCollection(); $products->setHandler(new ProductsController()); $products->setPrefix('/products'); $products->get('/get/{id}', 'get'); $products->get('/add/{payload}', 'add'); $app->mount($products); ``` 此实现依次加载每个处理程序并将其安装在我们的应用程序对象中。这种方法的问题是每个请求只会导致一个端点,因此会执行一个类方法。其余的方法/处理程序将保留在内存中而不会被使用。 使用延迟加载我们减少了内存中加载的对象数量,因此我们的应用程序使用更少的内存。 如果我们想要使用延迟加载,上面的实现会改变如下: ```php <?php use Phalcon\Mvc\Micro\Collection as MicroCollection; // 用户处理程序 $users = new MicroCollection(); $users->setHandler('UsersController', true); $users->setPrefix('/users'); $users->get('/get/{id}', 'get'); $users->get('/add/{payload}', 'add'); $app->mount($users); // 订单处理程序 $orders = new MicroCollection(); $orders->setHandler('OrdersController', true); $orders->setPrefix('/users'); $orders->get('/get/{id}', 'get'); $orders->get('/add/{payload}', 'add'); $app->mount($orders); // 产品处理程序 $products = new MicroCollection(); $products->setHandler('ProductsController', true); $products->setPrefix('/products'); $products->get('/get/{id}', 'get'); $products->get('/add/{payload}', 'add'); $app->mount($products); ``` 使用这种简单的实现更改,所有处理程序在调用者请求之前都保持未实例化。因此,每当调用者请求`/orders/get/2`时,我们的应用程序将实例化`OrdersController`并在其中调用get方法。我们的应用现在使用的资源比以前少。 #### Not found (404) 在我们的`Phalcon\Mvc\Micro`应用程序中未匹配的任何路由将导致它尝试执行使用`notFound`方法定义的处理程序。与其他方法/动词(`get`,`post`等)类似,您可以在`notFound`方法中注册一个处理程序,该方法可以是任何可调用的PHP函数。 ```php <?php $app->notFound( function () use ($app) { $app->response->setStatusCode(404, 'Not Found'); $app->response->sendHeaders(); $message = 'Nothing to see here. Move along....'; $app->response->setContent($message); $app->response->send(); } ); ``` 您还可以处理未与下面讨论的中间件匹配的路由(404)。 ### 方法 - 动词 `Phalcon\Mvc\Micro` 应用程序提供了一组方法来将HTTP方法与其预期的路由绑定。 #### delete 如果HTTP方法是 `DELETE` 且路由是 `/api/products/delete/{id}`,则匹配 ```php $app->delete( '/api/products/delete/{id}', 'delete_product' ); ``` #### get 如果HTTP方法是 `GET` 且路由是 `/api/products`,则匹配 ```php $app->get( '/api/products', 'get_products' ); ``` #### head 如果HTTP方法是 `HEAD` 且路由是 `/api/products`,则匹配 ```php $app->head( '/api/products', 'get_products' ); ``` #### map Map允许您将同一端点附加到多个HTTP方法。如果HTTP方法是`GET`或`POST`并且路由是`/repos/store/refs`,则下面的示例匹配 ```php $app ->map( '/repos/store/refs', 'action_product' ) ->via( [ 'GET', 'POST', ] ); ``` #### options 如果HTTP方法是`OPTIONS` 且路由是 `/api/products/options`,则匹配 ```php $app->options( '/api/products/options', 'info_product' ); ``` #### patch 如果HTTP方法是`PATCH` 且路由是 `/api/products/update/{id}`,则匹配 ```php $app->patch( '/api/products/update/{id}', 'update_product' ); ``` #### post 如果HTTP方法是 `POST` 且路由是 `/api/products/add`,则匹配 ```php $app->post( '/api/products', 'add_product' ); ``` #### put 如果HTTP方法是 `PUT` 且路由是 `/api/products/update/{id}`,则匹配 ```php $app->put( '/api/products/update/{id}', 'update_product' ); ``` ### 集合 集合是一种方便的方法,可以将附加到处理程序的集合和公共前缀(如果需要)分组。对于假设`/orders`端点,我们可以有以下端点: /orders/get/{id} /orders/add/{payload} /orders/update/{id} /orders/delete/{id} 所有这些路由都由`OrdersController`处理。我们使用如下集合设置路由: ```php <?php use Phalcon\Mvc\Micro\Collection as MicroCollection; $orders = new MicroCollection(); $orders->setHandler(new OrdersController()); $orders->setPrefix('/orders'); $orders->get('/get/{id}', 'displayAction'); $orders->get('/add/{payload}', 'addAction'); $orders->get('/update/{id}', 'updateAction'); $orders->get('/delete/{id}', 'deleteAction'); $app->mount($orders); ``` >[warning] 我们绑定每个路由的名称后缀为`Action`。 这不是必需的,您可以根据自己喜欢的方式调用方法。 ### 参数 我们在上面简要介绍了如何在路由中定义参数。通过将参数名称括在括号中,可以在路径字符串中设置参数。 ```php $app->get( '/orders/display/{name}', function ($name) { echo "<h1>This is order: {$name}!</h1>"; } ); ``` 我们还可以使用正则表达式为每个参数强制执行某些规则。正则表达式在参数名称后面设置,用以下内容分隔 `:` ```php // 匹配订单id $app->get( '/orders/display/{id:[0-9]+}', function ($id) { echo "<h1>This is order: #{$id}!</h1>"; } ); // 匹配数字(4)年和标题(alpha) $app->get( '/posts/{year:[0-9][4]}/{title:[a-zA-Z\-]+}', function ($year, $title) { echo '<h1>Title: $title</h1>'; echo '<h2>Year: $year</h2>'; } ); ``` 附加信息:`Phalcon\Mvc\Router` ### 重定向 您可以使用 `Phalcon\Http\Response` 对象将一个匹配的路由重定向到另一个匹配的路由,就像在完整的应用程序中一样。 ```php $app->post('/old/url', function () use ($app) { $app->response->redirect('new/url'); $app->response->sendHeaders(); } ); $app->post('/new/welcome', function () use ($app) { echo 'This is the new Welcome'; } ); ``` **Note** 我们必须在匿名函数中传递`$app`对象才能访问`request`对象。 使用控制器作为处理程序时,您可以轻松执行重定向: ```php <?php use Phalcon\Mvc\Controller; class UsersController extends Controller { public function oldget($id) { return $this->response->redirect('users/get/' . $id); } public function get($id) { // ... } } ``` 最后,您可以在中间件中执行重定向(如果您正在使用它)。以下是相关部分的示例。 ### 路由的URL 路由的另一个特性是设置命名路由并为这些路由生成URL。这是一个两步过程。 * 首先,我们需要命名我们的路由。这可以通过我们的应用程序中的方法/动词公开的`setName()`方法来实现(`get`,`post`等); ```php // 设置名为“show-order”的路由 $app ->get( '/orders/display/{id}', function ($id) use ($app) { // ... 找到订单并显示它 } ) ->setName('show-order'); ``` * 我们需要使用 `Phalcon\Mvc\Url` 组件为命名路由生成URL。 ```php // 使用指定的路由并从中生成URL $app->get( '/', function () use ($app) { $url = sprintf( '<a href="%s">Show the order</a>', $app->url->get( [ 'for' => 'show-order', 'id' => 1234, ] ) ); echo $url; } ); ``` ## 依赖注入 创建微应用程序时,将隐式创建 `Phalcon\Di\FactoryDefault` 服务容器。 ```php <?php use Phalcon\Mvc\Micro; $app = new Micro(); $app->get( '/', function () use ($app) { $app->response->setContent('Hello!!'); $app->response->send(); } ); ``` 您也可以自己创建Di容器,并将其分配给微应用程序,从而根据应用程序的需要操作服务。 ```php <?php use Phalcon\Mvc\Micro; use Phalcon\Di\FactoryDefault; use Phalcon\Config\Adapter\Ini as IniConfig; $di = new FactoryDefault(); $di->set( 'config', function () { return new IniConfig('config.ini'); } ); $app = new Micro(); $app->setDI($di); $app->get( '/', function () use ($app) { // 从配置中读取设置 echo $app->config->app_name; } ); $app->post( '/contact', function () use ($app) { $app->flash->success('What are you doing Dave?'); } ); ``` 您还可以使用数组语法从应用程序对象注册依赖项注入容器中的服务: ```php <?php use Phalcon\Mvc\Micro; use Phalcon\Db\Adapter\Pdo\Mysql as MysqlAdapter; $app = new Micro(); // 设置数据库服务 $app['db'] = function () { return new MysqlAdapter( [ 'host' => 'localhost', 'username' => 'root', 'password' => 'secret', 'dbname' => 'test_db', ] ); }; $app->get( '/blog', function () use ($app) { $news = $app['db']->query('SELECT * FROM news'); foreach ($news as $new) { echo $new->title; } } ); ``` ## 响应 微应用程序可以返回许多不同类型的响应。直接输出,使用模板引擎,计算数据,基于视图的数据,JSON等。 处理程序可以使用纯文本,`Phalcon\Http\Response` 对象或实现 `Phalcon\Http\ResponseInterface`的自定义构建组件返回原始响应。 ### 直接输出 ```php $app->get( '/orders/display/{name}', function ($name) { echo "<h1>This is order: {$name}!</h1>"; } ); ``` ### 包含另一个文件 ```php $app->get( '/orders/display/{name}', function ($name) { require 'views/results.php'; } ); ``` ### 直接输出JSON ```php $app->get( '/orders/display/{name}', function ($name) { echo json_encode( [ 'code' => 200, 'name' => $name, ] ); } ); ``` ### 新的Response对象 您可以使用Response对象的setContent方法返回响应: ```php $app->get( '/show/data', function () { // 创建一个响应 $response = new Phalcon\Http\Response(); // 设置Content-Type标头 $response->setContentType('text/plain'); // 传递文件的内容 $response->setContent(file_get_contents('data.txt')); // 返回响应 return $response; } ); ``` ### 应用响应 您还可以使用 `Phalcon\Http\Response` 对象将响应返回给调用者。Response对象有许多有用的方法,使返回响应更容易。 ```php $app->get( '/show/data', function () use ($app) { // Set the Content-Type header $app->response->setContentType('text/plain'); $app->response->sendHeaders(); // Print a file readfile('data.txt'); } ); ``` ### 返回应用响应 将数据返回给调用者的另一种方法是直接从应用程序返回Response对象。当处理程序返回响应时,它们将由应用程序自动发送。 ```php <?php use Phalcon\Mvc\Micro; use Phalcon\Http\Response; $app = new Micro(); // Return a response $app->get( '/welcome/index', function () { $response = new Response(); $response->setStatusCode(401, 'Unauthorized'); $response->setContent('Access is not authorized'); return $response; } ); ``` ### JSON 使用 `Phalcon\Http\Response` 对象可以轻松地发回JSON: ```php $app->get( '/welcome/index', function () use ($app) { $data = [ 'code' => 401, 'status' => 'error', 'message' => 'Unauthorized access', 'payload' => [], ]; $response->setJsonContent($data); return $response; } ); ``` ## 事件 `Phalcon\Mvc\Micro` 应用程序与 `Phalcon\Events\Manager` 密切配合(如果存在),以触发可在整个应用程序中使用的事件。这些事件的类型是微观的。这些事件在我们的应用程序中触发,并且可以附加到将执行我们的应用程序所需的操作的相关处理程序。 ### 可用事件 支持以下事件: | 事件名称 | Triggered | Can stop operation? | | ------------------ | ---------------------------------------- | :-----------------: | | beforeHandleRoute | Main 方法调用;路由尚未检查 | Yes | | beforeExecuteRoute | 路由匹配,处理程序有效,处理程序尚未执行 | Yes | | afterExecuteRoute | 处理程序刚刚运行完毕 | No | | beforeNotFound | 尚未找到路由 | Yes | | afterHandleRoute | 路由刚刚执行结束 | Yes | | afterBinding | 在绑定模型之后但在执行处理程序之前触发 | Yes | #### 验证示例 您可以使用`beforeExecuteRoute`事件轻松检查用户是否已经过身份验证。在以下示例中,我们将介绍如何使用事件控制应用程序安全性: ```php <?php use Phalcon\Mvc\Micro; use Phalcon\Events\Event; use Phalcon\Events\Manager as EventsManager; // 创建一个事件管理器 $eventsManager = new EventsManager(); $eventsManager->attach( 'micro:beforeExecuteRoute', function (Event $event, $app) { if ($app->session->get('auth') === false) { $app->flashSession->error("The user isn't authenticated"); $app->response->redirect('/'); $app->response->sendHeaders(); // 返回(false)停止操作 return false; } } ); $app = new Micro(); // 将事件管理器绑定到应用程序 $app->setEventsManager($eventsManager); ``` #### Not found 示例 您可以订阅以实现业务逻辑的另一个内置事件是`beforeNotFound`。以下示例显示了处理不存在的地址请求的方法之一: ```php <?php use Phalcon\Mvc\Micro; use Phalcon\Events\Event; use Phalcon\Events\Manager as EventsManager; // 创建一个事件管理器 $eventsManager = new EventsManager(); $eventsManager->attach( 'micro:beforeNotFound', function (Event $event, $app) { $app->response->redirect('/404'); $app->response->sendHeaders(); return $app->response; } ); $app = new Micro(); // 将事件管理器绑定到应用程序 $app->setEventsManager($eventsManager); ``` ## 中间件 中间件是可以附加到应用程序的类,并引入了可以存在业务逻辑的另一个层。它们按照它们注册的顺序依次运行,不仅通过封装特定功能而且还提高了性能,从而提高了可维护性。中间件类可以在未满足特定业务规则时停止执行,从而允许应用程序提前退出而不执行请求的完整周期。 `Phalcon\Events\Manager` 的存在对于中间件的运行至关重要,因此必须在我们的Di容器中注册。 ### 附加事件 中间件可以在3个不同的事件中附加到微应用程序。那些是: | 事件 | 描述 | | ------ | ------------------ | | before | 在处理程序执行之前 | | after | 处理程序执行完毕后 | | final | 响应发送给调用者后 | >[warning] 您可以在上述每个事件中附加任意数量的中间件类。 当相关事件触发时,它们将按顺序执行。 #### before 如果不满足某些条件,此事件非常适合停止执行应用程序。在下面的示例中,我们检查用户是否已经过身份验证,并使用必要的重定向停止执行。 ```php <?php $app = new Phalcon\Mvc\Micro(); // 在执行每个路由之前执行 // 返回false取消路由执行 $app->before( function () use ($app) { if (false === $app['session']->get('auth')) { $app['flashSession']->error("The user isn't authenticated"); $app['response']->redirect('/error'); // 返回false会停止正常执行 return false; } return true; } ); ``` #### after 此事件可用于操作数据或执行处理程序执行完毕后所需的操作。在下面的示例中,我们操纵响应以将JSON发送回调用者。 ```php $app->map( '/api/robots', function () { return [ 'status' => 'OK', ]; } ); $app->after( function () use ($app) { // 这是在执行路由后执行的 echo json_encode($app->getReturnedValue()); } ); ``` #### finish 当整个请求周期完成时,这甚至会启动。在下面的示例中,我们使用它来清理一些缓存文件。 ```php $app->finish( function () use ($app) { if (true === file_exists('/tmp/processing.cache')) { unlink('/tmp/processing.cache'); } } ); ``` ### 设置 如上所示,使用`before`,`after`和`finish`方法调用将中间件附加到应用程序非常简单。 ```php $app->before( function () use ($app) { if (false === $app['session']->get('auth')) { $app['flashSession']->error("The user isn't authenticated"); $app['response']->redirect('/error'); // 返回false会停止正常执行 return false; } return true; } ); $app->after( function () use ($app) { // 这是在执行路由后执行的 echo json_encode($app->getReturnedValue()); } ); ``` 将中间件作为类附加到应用程序并让它监听事件管理器中的事件可以实现如下: ```php <?php use Phalcon\Events\Manager; use Phalcon\Mvc\Micro; use Website\Middleware\CacheMiddleware; use Website\Middleware\NotFoundMiddleware; use Website\Middleware\ResponseMiddleware; /** * 创建一个新的事件管理器。 */ $eventsManager = new Manager(); $application = new Micro(); /** * 将中间件附加到事件管理器和应用程序 */ $eventsManager->attach('micro', new CacheMiddleware()); $application->before(new CacheMiddleware()); $eventsManager->attach('micro', new NotFoundMiddleware()); $application->before(new NotFoundMiddleware()); /** * 这个人需要听'after`事件 */ $eventsManager->attach('micro', new ResponseMiddleware()); $application->after(new ResponseMiddleware()); /** * 确保我们的事件管理器现在位于DI容器中 */ $application->setEventsManager($eventsManager); ``` 我们需要一个`Phalcon\Events\Manager` 对象。这可以是一个新实例化的对象,或者我们可以获取DI容器中存在的对象(如果您使用了FactoryDefault `FactoryDefault`对象)。 我们在事件管理器中的`微应用`钩子中附加每个中间件类。我们也可以更具体一点,并附上它来说明:`beforeExecuteRoute`事件。 然后,我们将应用程序中的中间件类附加到上面讨论的三个侦听事件之一(`before`, `after`, `finish`)。 ### 履行 中间件可以是任何类型的PHP可调用函数。您可以按照自己喜欢的方式组织代码来实现中间件。如果您选择使用中间件类,则需要它们来实现`Phalcon\Mvc\Micro\MiddlewareInterface` ```php <?php use Phalcon\Mvc\Micro; use Phalcon\Mvc\Micro\MiddlewareInterface; /** * CacheMiddleware * * 缓存页面以减少处理 */ class CacheMiddleware implements MiddlewareInterface { /** * Calls the middleware * * @param Micro $application * * @returns bool */ public function call(Micro $application) { $cache = $application['cache']; $router = $application['router']; $key = preg_replace('/^[a-zA-Z0-9]/', '', $router->getRewriteUri()); // 检查请求是否已缓存 if ($cache->exists($key)) { echo $cache->get($key); return false; } return true; } } ``` ### 中间件中的事件 为我们的应用程序触发的事件也会在实现`Phalcon\Mvc\Micro\MiddlewareInterface`的类中触发。这为开发人员提供了极大的灵活性和功能,因为我们可以与请求流程进行交互 #### API示例 假设我们已经使用Micro应用程序实现了API。我们需要在应用程序中附加不同的中间件类,以便我们可以更好地控制应用程序的执行。 我们将使用的中间件是: * Firewall * NotFound * Redirect * CORS * Request * Response ##### Firewall Middleware 此中间件附加到Micro应用程序的`before`事件。此中间件的目的是检查谁在调用我们的API并基于白名单,允许它们继续或不继续 ```php <?php use Phalcon\Events\Event; use Phalcon\Mvc\Micro; use Phalcon\Mvc\Micro\MiddlewareInterface; /** * FirewallMiddleware * * 检查白名单并允许客户端与否 */ class FirewallMiddleware implements MiddlewareInterface { /** * Before anything happens * * @param Event $event * @param Micro $application * * @returns bool */ public function beforeHandleRoute(Event $event, Micro $application) { $whitelist = [ '10.4.6.1', '10.4.6.2', '10.4.6.3', '10.4.6.4', ]; $ipAddress = $application->request->getClientAddress(); if (true !== array_key_exists($ipAddress, $whitelist)) { $this->response->redirect('/401'); $this->response->send(); return false; } return true; } /** * Calls the middleware * * @param Micro $application * * @returns bool */ public function call(Micro $application) { return true; } } ``` ##### Not Found Middleware 处理此中间件时,这意味着允许请求IP访问我们的应用程序。应用程序将尝试匹配路由,如果没有找到,则会触发 `beforeNotFound`事件。我们将停止处理,然后向用户发回相关的404响应。此中间件附加到Micro应用程序的`before`事件 ```php <?php use Phalcon\Mvc\Micro; use Phalcon\Mvc\Micro\MiddlewareInterface; /** * NotFoundMiddleware * * 处理404s */ class NotFoundMiddleware implements MiddlewareInterface { /** * The route has not been found * * @returns bool */ public function beforeNotFound() { $this->response->redirect('/404'); $this->response->send(); return false; } /** * Calls the middleware * * @param Micro $application * * @returns bool */ public function call(Micro $application) { return true; } } ``` ##### Redirect Middleware 我们将此中间件再次附加到Micro应用程序的`before`事件,因为如果需要重定向请求的端点,我们不希望请求继续。 ```php <?php use Phalcon\Events\Event; use Phalcon\Mvc\Micro; use Phalcon\Mvc\Micro\MiddlewareInterface; /** * RedirectMiddleware * * 检查请求并在需要时将用户重定向到其他位置 */ class RedirectMiddleware implements MiddlewareInterface { /** * Before anything happens * * @param Event $event * @param Micro $application * * @returns bool */ public function beforeHandleRoute(Event $event, Micro $application) { if ('github' === $application->request->getURI()) { $application->response->redirect('https://github.com'); $application->response->send(); return false; } return true; } /** * Calls the middleware * * @param Micro $application * * @returns bool */ public function call(Micro $application) { return true; } } ``` ##### CORS Middleware 此中间件再次附加到Micro应用程序的`before`事件。我们需要确保它在我们的应用程序发生任何事情之前触发 ```php <?php use Phalcon\Events\Event; use Phalcon\Mvc\Micro; use Phalcon\Mvc\Micro\MiddlewareInterface; /** * CORSMiddleware * * CORS checking */ class CORSMiddleware implements MiddlewareInterface { /** * Before anything happens * * @param Event $event * @param Micro $application * * @returns bool */ public function beforeHandleRoute(Event $event, Micro $application) { if ($application->request->getHeader('ORIGIN')) { $origin = $application->request->getHeader('ORIGIN'); } else { $origin = '*'; } $application ->response ->setHeader('Access-Control-Allow-Origin', $origin) ->setHeader( 'Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS' ) ->setHeader( 'Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Range, ' . 'Content-Disposition, Content-Type, Authorization' ) ->setHeader('Access-Control-Allow-Credentials', 'true'); return true; } /** * Calls the middleware * * @param Micro $application * * @returns bool */ public function call(Micro $application) { return true; } } ``` ##### Request Middleware 此中间件正在接收JSON有效负载并对其进行检查。如果JSON有效负载无效,它将停止执行。 ```php <?php use Phalcon\Events\Event; use Phalcon\Mvc\Micro; use Phalcon\Mvc\Micro\MiddlewareInterface; /** * RequestMiddleware * * Check incoming payload */ class RequestMiddleware implements MiddlewareInterface { /** * Before the route is executed * * @param Event $event * @param Micro $application * * @returns bool */ public function beforeExecuteRoute(Event $event, Micro $application) { json_decode($application->request->getRawBody()); if (JSON_ERROR_NONE !== json_last_error()) { $application->response->redirect('/malformed'); $application->response->send(); return false; } return true; } /** * Calls the middleware * * @param Micro $application * * @returns bool */ public function call(Micro $application) { return true; } } ``` ##### Response Middleware 该中间件负责操作我们的响应并将其作为JSON字符串发送回调用者。因此,我们需要将它附加到Micro应用程序的`after`事件。 >[warning] 我们将对此中间件使用`call`方法,因为我们几乎执行了整个请求周期。 ```php <?php use Phalcon\Mvc\Micro; use Phalcon\Mvc\Micro\MiddlewareInterface; /** * ResponseMiddleware * * Manipulates the response */ class ResponseMiddleware implements MiddlewareInterface { /** * Before anything happens * * @param Micro $application * * @returns bool */ public function call(Micro $application) { $payload = [ 'code' => 200, 'status' => 'success', 'message' => '', 'payload' => $application->getReturnedValue(), ]; $application->response->setJsonContent($payload); $application->response->send(); return true; } } ``` ## 模型 模型可以在Micro应用程序中使用,只要我们指示应用程序如何使用自动加载器找到相关的类。 >[warning] 必须在Di容器中注册相关的`db`服务。 ```php <?php $loader = new \Phalcon\Loader(); $loader ->registerDirs( [ __DIR__ . '/models/', ] ) ->register(); $app = new \Phalcon\Mvc\Micro(); $app->get( '/products/find', function () { $products = \MyModels\Products::find(); foreach ($products as $product) { echo $product->name, '<br>'; } } ); $app->handle(); ``` ## 注入模型实例 通过使用 `Phalcon\Mvc\Model\Binder` 类,您可以将模型实例注入到路径中: ```php <?php $loader = new \Phalcon\Loader(); $loader->registerDirs( [ __DIR__ . '/models/', ] )->register(); $app = new \Phalcon\Mvc\Micro(); $app->setModelBinder(new \Phalcon\Mvc\Model\Binder()); $app->get( "/products/{product:[0-9]+}", function (Products $product) { // 用$product对象做任何事情 } ); $app->handle(); ``` 由于`Binder`对象使用的内部Reflection Api很重,因此可以设置缓存以加快进程。这可以通过使用`setModelBinder()`的第二个参数来完成,该参数也可以接受服务名称或仅通过将缓存实例传递给`Binder`构造函数。 目前,构建器仅使用模型主键来执行 `findFirst()` 。上面的示例路由是 `/products/1`。 ## Views `Phalcon\Mvc\Micro` 本身并不具有视图服务。但是我们可以使用`Phalcon\Mvc\View\Simple` 组件来呈现视图。 ```php <?php $app = new Phalcon\Mvc\Micro(); $app['view'] = function () { $view = new \Phalcon\Mvc\View\Simple(); $view->setViewsDir('app/views/'); return $view; }; // Return a rendered view $app->get( '/products/show', function () use ($app) { // 渲染app/views /products/ show.phtml传递一些变量 echo $app['view']->render( 'products/show', [ 'id' => 100, 'name' => 'Artichoke', ] ); } ); ``` >[warning] 上面的示例使用` Phalcon\Mvc\View\Simple`组件,该组件使用相对路径而不是控制器和操作。 您可以使用`Phalcon\Mvc\View`组件,但为此,您需要更改传递给的参数`render()` ```php <?php $app = new Phalcon\Mvc\Micro(); $app['view'] = function () { $view = new \Phalcon\Mvc\View(); $view->setViewsDir('app/views/'); return $view; }; // Return a rendered view $app->get( '/products/show', function () use ($app) { // 渲染app/views /products/ show.phtml传递一些变量 echo $app['view']->start()->render( 'products', 'show', [ 'id' => 100, 'name' => 'Artichoke', ] )->finish()->getContent(); } ); ``` ## 错误处理 `Phalcon\Mvc\Micro` 应用程序还有一个`error`方法,可用于捕获源自异常的任何错误。以下代码段显示了此功能的基本用法: ```php <?php $app = new Phalcon\Mvc\Micro(); $app->get( '/', function () { throw new \Exception('Some error happened', 401); } ); $app->error( function ($exception) { echo json_encode( [ 'code' => $exception->getCode(), 'status' => 'error', 'message' => $exception->getMessage(), ] ); } ); ```