Laravel 5 控制器如何实现基于自身的中间件

声明:本文由夭℃~空投稿,未经作者许可,禁止转载!

这个想法的产生背景是在我碰到一个实际需求的时候。当时我碰到了这么一个需求:

首先我有很多模块,每个模块都有一个鉴权方式。

我本来想用中间件来做,但是随即发现,由于每个模块的过滤方式不一样导致中间件的数量一下子增加了很多。而且,由于中间件的分离到了中间件的文件夹去,且并不能实现中间件的复用价值。所以,我抛弃了使用中间件的方法。

这个时候,我想到,每个控制器自己有独立的construct不就好了,这样每个控制器都有自己的独特的鉴权方式。

但是问题随之而来,Laravel 的控制器虽然能实现自动construct,但是在construct里面的,并不能实现response,也就是说在construct里面的response将会被忽略。不是实现返回错误信息,或者重定向的需求。

翻了文档之后似乎找不到一个完美的解决方法。于是,我去翻源代码,最终把目标放beforeFilter,并用boforeFilter实现了这个需求。

重新整理适用场景:

本用法适用于每个控制器都需要同样的一套过滤,而具体的过滤方式由控制器自身决定,也就是基于控制器本身的中间件。

代码如下:

首先建立一个基础的控制器:

<?php
namespace App\Http\Controllers\Api\Business;

use RuntimeException;
use Illuminate\Routing\Controller as BaseController;
use Illuminate\Auth\Guard;
use Illuminate\Http\Request;

abstract class Controller extends BaseController
{
    protected $request;
    protected $auth;

    public function __construct(Request $request,Guard $auth){
        $this->request = $request;
        $this->auth = $auth;

        //统一鉴权
        $this->beforeFilter(function(){
            return $this->checkPermission();
        });
    }

    protected function checkPermission(){
        throw new RuntimeException('['.get_class($this).'] missing method[checkPermission]');
    }

}

requestauth放到基础控制器,是为了方便鉴权过程中调用,关键的地方,在于设置beforeFilter,通过beforeFilter调用控制器自身的协议方法。至于我在基础控制器里面放checkPermission是要求所有基于这个控制器的控制器必须重载掉这个方法。beforeFilter可以设置第二个参数,跟middleware()一样,可以设置except或者only

然后定义模块控制器继承这个方法:

<?php

namespace App\Http\Controllers\Api\Business;

class TicketController extends Controller
{

    //鉴定权限
    protected function checkPermission(){
        //return redirect()->to('某个url');
        //return view();
        //return 'string';
        ...
    }

}

只要checkPermission里面返回response,那么就会跟中间件一样作为整个请求的response.如果里面不返回任何东西,那么php会自动返回null。只要是null,就代表着通过checkPermission的验证。

这样就能实现基于控制器自身的中间件,更多用法有待发掘。

注:目前这个我在5.0版本使用通过,更高的版本有待测试。

本文由夭℃~空投稿,他邮箱是anwelblue@qq.com,有什么问题欢迎与他一起探讨,此外你还可以在学院二群找到他:542175836

学院君

学院君 has written 554 articles

资深PHP工程师,Laravel学院院长