laravel框架的中間件middleware的詳解


本篇文章給大家帶來的內容是關於laravel框架的中間件middleware的詳解,有一定的參考價值,有需要的朋友可以參考一下,希望對你有所幫助。

laravel中間件是個非常方便的東西,能將一些邏輯實現解耦,並且在laravel中,
中間件的編寫也是非常的方便。誰用誰知道。

1.裝飾器模式

laravel中的中間件使用的就是裝飾器模式,什么是[裝飾器模式][1],先去了解一下吧,這里大概說一下,就是這個模式主要的就是用於解決 當一個類需要動態擴展功能的時候,使用繼承的方式會讓子類膨脹,並且這個擴展的功能是個公用功能的情況下,不利於功能的復用以及代碼的解耦。

在laravel,使用對於使用這種模式的功能,稱為請求處理管道,也就是pipeline

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

//公共接口

interface middleware {

        public static function handle(Closure $next);

    }

//裝飾器1

class MiddleStepOne implements middleware{

        public static function handle(Closure $next) {

            echo "前期處理的第一步"."<br>";

            $next();

            echo "后期處理的第一步"."<br>";

        }

    }

//裝飾器2

class MiddleStepTwo implements middleware{

    public static function handle(Closure $next) {

        echo "前期處理的第二步"."<br>";

        $next();

        echo "后期處理的第二步"."<br>";

    }

}

 

function goFunc() {

    return function ($step,$className) {

      return function () use ($step,$className) {

          return $className::handle($step);

      };

    };

}

 

$pip = array(

    MiddleStepOne::class,

    MiddleStepTwo::class,

);

$pip = array_reverse($pip);  //反轉數組,以求達到要求的順序運行

$first = function (){

    echo "前期處理完畢"."<br>";

};  //實際要處理的函數

$a = array_reduce($pip,goFunc(),$first); //遍歷pip數組,並將first作為第一個參數傳遞進去

$a(); //執行

輸出:

這個就是一個簡單的基於裝飾器模式的管道。他的本質其實就是基於閉包和遞歸。

通過分析這個程序,對於最終生成的$a變量,它的值大概是這樣的 MiddleStepOne.handle(MiddleStepTwo.handle(first)),當執行的時候因為在handle中有個next()函數的存在,所以這是一個遞歸的調用。對於laravel的中間件,他的實現原理也是和這個一樣的。

鏈接:https://pan.baidu.com/s/1v5gm7n0L7TGyejCmQrMh2g 提取碼:x2p5

免費分享,但是X度限制嚴重,如若鏈接失效點擊鏈接或搜索加群 群號518475424

2.laravel中的中間件和請求處理管道

在laravel中,我們我們可以通過設置中間件來在請求執行之前做一些預先的處理。

從請求入口 public/index.php開始

重要的是這段代碼:即 處理請求,返回請求的響應

1

2

3

$response = $kernel->handle(

$request = Illuminate\Http\Request::capture() //創建一個請求實例

);

接着我們進入kernel中看他的具體實現 IlluminateFoundationHttpKernel.php中


關於dispatchToRouter()函數請大家自己去看,這里就不多說了。

接下來就是激動人心的PipeLine類了,

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

<?php

 

namespace Illuminate\Pipeline;

 

use Closure;

use RuntimeException;

use Illuminate\Contracts\Container\Container;

use Illuminate\Contracts\Pipeline\Pipeline as PipelineContract;

 

class Pipeline implements PipelineContract

{

    /**

     * The container implementation.

     *

     * @var \Illuminate\Contracts\Container\Container

     */

    protected $container;

 

    /**

     * The object being passed through the pipeline.

     *

     * @var mixed

     */

    protected $passable;

 

    /**

     * The array of class pipes.

     *

     * @var array

     */

    protected $pipes = [];

 

    /**

     * The method to call on each pipe.

     *

     * @var string

     */

    protected $method = 'handle';

 

    /**

     * Create a new class instance.

     *

     * @param  \Illuminate\Contracts\Container\Container|null  $container

     * @return void

     */

    public function __construct(Container $container = null)

    {

        $this->container = $container;

    }

 

    /**

     * Set the object being sent through the pipeline.

     *

     * @param  mixed  $passable

     * @return $this

     */

    public function send($passable)

    {

        $this->passable = $passable;

 

        return $this;

    }

 

    /**

     * Set the array of pipes.

     *

     * @param  array|mixed  $pipes

     * @return $this

     */

    public function through($pipes)

    {

        $this->pipes = is_array($pipes) ? $pipes : func_get_args();

 

        return $this;

    }

 

    /**

     * Set the method to call on the pipes.

     *

     * @param  string  $method

     * @return $this

     */

    public function via($method)

    {

        $this->method = $method;

 

        return $this;

    }

 

    /**

     * Run the pipeline with a final destination callback.

     *

     * @param  \Closure  $destination

     * @return mixed

     */

    public function then(Closure $destination)

    {

        $pipeline = array_reduce(

            array_reverse($this->pipes), $this->carry(), $this->prepareDestination($destination)

        );

 

        return $pipeline($this->passable);

    }

 

    /**

     * Get the final piece of the Closure onion.

     *

     * @param  \Closure  $destination

     * @return \Closure

     */

    protected function prepareDestination(Closure $destination)

    {

        return function ($passable) use ($destination) {

            return $destination($passable);

        };

    }

 

    /**

     * Get a Closure that represents a slice of the application onion.

     *

     * @return \Closure

     */

    protected function carry()

    {

        return function ($stack, $pipe) {

            return function ($passable) use ($stack, $pipe) {

                if (is_callable($pipe)) {

                    // If the pipe is an instance of a Closure, we will just call it directly but

                    // otherwise we'll resolve the pipes out of the container and call it with

                    // the appropriate method and arguments, returning the results back out.

                    //如果pip也就中間件函數是一個閉包可調用函數,就直接返回這個閉包函數就行了

                    //這里我還沒有找到對應的使用場景,后續補充

                    return $pipe($passable, $stack);

                } elseif (! is_object($pipe)) {

                    list($name, $parameters) = $this->parsePipeString($pipe);

 

                    // If the pipe is a string we will parse the string and resolve the class out

                    // of the dependency injection container. We can then build a callable and

                    // execute the pipe function giving in the parameters that are required.

                    $pipe = $this->getContainer()->make($name);

 

                    $parameters = array_merge([$passable, $stack], $parameters);

                } else {

                    // If the pipe is already an object we'll just make a callable and pass it to

                    // the pipe as-is. There is no need to do any extra parsing and formatting

                    // since the object we're given was already a fully instantiated object.

                    $parameters = [$passable, $stack];

                }

 

                return method_exists($pipe, $this->method)

                                ? $pipe->{$this->method}(...$parameters)

                                : $pipe(...$parameters);

            };

        };

    }

 

    /**

     * Parse full pipe string to get name and parameters.

     *

     * @param  string $pipe

     * @return array

     */

    protected function parsePipeString($pipe)

    {

        list($name, $parameters) = array_pad(explode(':', $pipe, 2), 2, []);

 

        if (is_string($parameters)) {

            $parameters = explode(',', $parameters);

        }

 

        return [$name, $parameters];

    }

 

    /**

     * Get the container instance.

     *

     * @return \Illuminate\Contracts\Container\Container

     * @throws \RuntimeException

     */

    protected function getContainer()

    {

        if (! $this->container) {

            throw new RuntimeException('A container instance has not been passed to the Pipeline.');

        }

 

        return $this->container;

    }

}

總的來說pipeLine類的實現和我之前寫的修飾器是差不多,這里主要麻煩的地方就在於就在於

protected function carry()函數內部,對於當pip是閉包,字符串,還有對象的處理。

之前覺得laravel的中間件是個很神秘的東西,但是看了之后才覺得也就那樣,很精巧,在實際開發中這種模式也是很有幫助的,例如我們目前用的一個gateway項目,因為沒有使用任何框架,所以將判斷條件剝離,寫入到中間件中, 這樣實現了一定程度上的模塊化編程。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM