手动配置 beforeAction() afterAction()

walkor

注意
webman版本>=1.2时请使用 webman/action-hook插件 实现beforeAction() afterAction()
否则请参考以下教程手动配置

注意
webman版本>=1.4.6 时,webman默认关闭了控制器复用,可以直接使用控制器__construct()为每个请求初始化工作

在传统框架中,每个请求都会实例化一次控制器,所以很多开发者__construct()方法中做一些请求前的准备工作。

而webman由于控制器常驻内存,无法在__construct()里做这些工作,不过webman提供了更好的解决方案beforeAction() afterAction(),它不仅让开发者可以介入到请求前的流程中,而且还可以介入到请求后的处理流程中。

为了介入请求流程,我们需要使用中间件

1、创建文件 app/middleware/ActionHook.php(middleware目录不存在请自行创建)

<?php
namespace app\middleware;

use support\Container;
use Webman\MiddlewareInterface;
use Webman\Http\Response;
use Webman\Http\Request;
use Webman\Route;

class ActionHook implements MiddlewareInterface
{
    public function process(Request $request, callable $next) : Response
    {
        if ($request->controller) {
            // 禁止直接访问beforeAction afterAction
            if ($request->action === 'beforeAction' || $request->action === 'afterAction') {
                $callback = Route::getFallback() ?? function () {
                    return new Response(404, [], \file_get_contents(public_path() . '/404.html'));
                };
                $reponse = $callback($request);
                return $reponse instanceof Response ? $reponse : \response($reponse);
            }
            $controller = Container::get($request->controller);
            if (method_exists($controller, 'beforeAction')) {
                $before_response = call_user_func([$controller, 'beforeAction'], $request);
                if ($before_response instanceof Response) {
                    return $before_response;
                }
            }
            $response = $next($request);
            if (method_exists($controller, 'afterAction')) {
                $after_response = call_user_func([$controller, 'afterAction'], $request, $response);
                if ($after_response instanceof Response) {
                    return $after_response;
                }
            }
            return $response;
        }
        return $next($request);
    }
}

2、在 config/middleware.php 中添加如下配置

return [
    '' => [
        // .... 这里省略了其它配置 ....
        app\middleware\ActionHook::class,
    ]
];

3、这样如果 controller包含了 beforeAction 或者 afterAction方法会在请求发生时自动被调用。
例如:

<?php
namespace app\controller;
use support\Request;
class Index
{
    /**
     * 该方法会在请求前调用 
     */
    public function beforeAction(Request $request)
    {
        echo 'beforeAction';
        // 若果想终止执行Action就直接返回Response对象,不想终止则无需return
        // return response('终止执行Action');
    }

    /**
     * 该方法会在请求后调用
     */
    public function afterAction(Request $request, $response)
    {
        echo 'afterAction';
        // 如果想串改请求结果,可以直接返回一个新的Response对象
        // return response('afterAction'); 
    }

    public function index(Request $request)
    {
        return response('index');
    }
}

beforeAction说明

  • 在当前控制器被执行前调用
  • 框架会传递一个Request对象给beforeAction,开发者可以从中获得用户输入
  • 如需终止执行当前控制器,则只需要在beforeAction里返回一个Response对象,比如return redirect('/user/login');
  • 无需终止执行当前控制器时,不要返回任何数据

afterAction说明

  • 在当前控制器被执行后调用
  • 框架会传递Request对象以及Response对象给afterAction,开发者可以从中获得用户输入以及控制器执行后返回的响应结果
  • 开发者可以通过$response->rawBody()获得响应内容
  • 开发者可以通过$response->getHeader()获得响应的header头
  • 开发者可以通过$response->getStatusCode()获得响应的http状态码
  • 开发者可利用$response->withBody() $response->header() $response->withStatus()串改响应,也可以创建并返回一个新的Response对象替代原响应
2451 6 6
6个评论

Tinywan

由于整个项目都是通过异常解决相应信息的,如果使用这个插件。则return response('<h1>404 Not Found</h1>', 404); 这个响应信息是否可以支持自定义?如:throw new NotFoundHttpException();

  • 暂无评论
walkor

404感觉没必要抛异常吧,这样会记录很多无用的异常日志,尤其外网有很多探测请求

  • Tinywan 2022-03-19

    前后端分离需要告诉前端统一的数据结构,通过异常就可以解决这个问题

  • walkor 2022-03-19

    路由404你是怎么触发异常的?

Tinywan

触发路由404异常

路由配置文件route.php增加以下选项

use Tinywan\ExceptionHandler\Exception\RouteNotFoundException;
...
Route::fallback(function () {
    throw new RouteNotFoundException();
});

假设访问以下接口地址(不存在的路由)

http://127.0.0.1:8888/test/route-not-exist

该地址路由不存在,即没在route.php配置文件中

接口响应输出

HTTP/1.1 404 Not Found
Content-Type: application/json;charset=UTF-8

{
    "code": 0,
    "msg": "路由地址不存在",
    "data": {
        "request_url": "GET //127.0.0.1:8888/test/route-not-exist",
        "timestamp": "2022-03-19 14:34:36",
        "client_ip": "172.18.0.1",
        "request_param": []
    }
}

异常处理

// 不需要记录错误日志
'dont_report' => [
    Tinywan\ExceptionHandler\Exception\NotFoundHttpException::class,
]

这样就不会记录很多无用的异常日志

ActionHook 同样抛出404异常

use Tinywan\ExceptionHandler\Exception\RouteNotFoundException;
..
// 禁止直接访问beforeAction afterAction
if ($request->action === 'beforeAction' || $request->action === 'afterAction') {
    throw new RouteNotFoundException();
}
  • 暂无评论
walkor

composer require webman/action-hook ^v1.0.1
更新到 1.0.1或者后续更高版本,404统一使用Route::fallback设定的回调返回值。

liziyu

这个跟 __construct() 功用一样的?

  • 暂无评论
南部荒人

在BeforeAction中无法改变Controller中的属性值。
例如在ArticleController定义一个public 属性 $pageInfo, 在beforeAction中赋值$this->pageInfo, 在Action中发现并未写入。

  • walkor 2023-02-26

    执行 composer info 看下webman-framework 版本

  • 南部荒人 2023-02-26

    v1.5.1 , 我昨天才create project创建的~~

  • walkor 2023-02-26

    这边文章是给 1.0 1.1版本写的,1.5直接用__construct就行

  • 南部荒人 2023-02-27

    好的~ 感谢~~

年代过于久远,无法发表评论

walkor

140721
积分
0
获赞数
0
粉丝数
2014-05-04 加入
🔝