Lumen 5.7 复习笔记

Lumen 利用 Composer 来管理它的代码依赖,所以在使用 Lumen 之前,请先确认你的电脑上安装了 Composer。

看官请绕道: Lumen 中文文档 5.7 ,此篇博客是本人复习+学习笔记。

安装

环境要求

  • PHP >= 7.1.3
  • OpenSSL PHP Extension
  • PDO PHP Extension
  • Mbstring PHP Extension

安装Lumen

1
2
3
4
5
6
// 通过 Lumen 安装器
composer global require "laravel/lumen-installer"
lumen new site

// 通过 Composer Create-Project 命令安装
composer create-project --prefer-dist laravel/lumen site

运行

1
php -S localhost:8000 -t public

应用程序密钥

安装完 Lumen 后,首先需要做的事情是设置一个随机字符串到应用程序密钥。通常这个密钥会有 32 字符长。
Laravel 有自动生成的命令: php artisan key:generate

如果应用程序密钥没有被设置的话,你的用户 Session 和其它的加密数据都是不安全的!

配置

获取/设置配置

1
2
3
4
5
6
// 读取
$value = config('app.locale');
// 设置
config(['app.locale' => 'en']);
// 使用前加载 在'boostrap/app.php'
$app->configure('app');

环境配置

Lumen 利用 Vance LucasPHPDotEnv 使得此项功能的实现变得非常简单。

1
2
3
4
5
try {
(new Dotenv\Dotenv(dirname(__DIR__)))->load();
} catch (Dotenv\Exception\InvalidPathException $e) {
//
}

创建应用时应该将 .env.example 文件重命名为 .env

.env 文件中列出的所有变量都会被加载到 PHP 的超全局变量 $ _ENV 中。 env 函数可以用来获取环境变量中的值

1
$debug = env('APP_DEBUG', true);

判定当前运行环境

1
2
3
4
5
6
7
8
9
10
$environment = app()->environment();

// 判断环境
if (app()->environment('local')) {
// 当前环境是 local
}

if (app()->environment('local', 'staging')) {
// 当前环境是 local 或者 staging...
}

路由

基本路由

1
2
3
$router->get('foo', function () {
return 'Hello World';
});

可供使用的路由方法

1
2
3
4
5
6
$router->get($uri, $callback);
$router->post($uri, $callback);
$router->put($uri, $callback);
$router->patch($uri, $callback);
$router->delete($uri, $callback);
$router->options($uri, $callback);

路由参数

必填参数

1
2
3
4
5
6
7
$router->get('user/{id}', function ($id) {
return 'User ' . $id;
});

$router->get('posts/{postId}/comments/{commentId}', function ($postId, $commentId) {
//
});

可选参数

1
2
3
$router->get('user[/{name}]', function ($name = null) {
return $name;
})

命名路由

1
2
3
4
5
6
7
8
$router->get('profile', ['as' => 'profile', function () {
//
});

$router->get('profile', [
'as' => 'profile',
'uses' => 'UserController@showProfile'
]);

中间件/命名空间/路由前缀

1
2
3
4
5
6
7
8
9
$router->group([
'middleware' => 'auth',
'namespace' => 'Admin',
'prefix' => 'admin'
], function () use ($router) {
$router->get('/', function () {
// 使用Auth中间件
});
});

中间件

HTTP 中间件提供了一个方便的机制来过滤进入应用程序的 HTTP 请求。
例如,Lumen 内置了一个中间件来验证用户的身份认证。如果用户未通过身份验证,中间件将会把用户导向登录页面,反之,当用户通过了身份验证,中间件将会通过此请求并接着往下执行。

当然,除了身份验证之外,中间件也可以被用来运行各式各样的任务,
如:CORS 中间件负责替所有即将离开程序的响应加入适当的标头;而日志中间件则可以记录所有传入应用程序的请求。

定义中间件

可以通过复制 Lumen 内置的示例文件 ExampleMiddleware 来创建一个中间件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php

namespace App\Http\Middleware;

use Closure;

class OldMiddleware
{
/**
* 运行请求过滤器
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
if ($request->input('age') <= 200) {
return redirect('home');
}

return $next($request);
}
}

前置/后置中间件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php

namespace App\Http\Middleware;

use Closure;

class BeforeMiddleware
{
public function handle($request, Closure $next)
{
// 执行操作

return $next($request);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php

namespace App\Http\Middleware;

use Closure;

class AfterMiddleware
{
public function handle($request, Closure $next)
{
$response = $next($request);

// 执行操作

return $response;
}
}

注册中间件

全局中间件

只需要在 bootstrap/app.php 文件中的 $app->middleware() 方法中列出这个中间件:

1
2
3
$app->middleware([
App\Http\Middleware\OldMiddleware::class
]);

为路由分配中间件

首先需要在 bootstrap/app.php 文件中调用 $app->routeMiddleware() 方法时为中间件分配一个简短的键:

1
2
3
$app->routeMiddleware([
'auth' => App\Http\Middleware\Authenticate::class,
]);

一旦在 HTTP 内核中定义好了中间件,就可以在路由选项内使用 middleware 键:

1
2
3
4
5
6
7
8
9
$router->get('admin/profile', ['middleware' => 'auth'], function () {
//
});

// 可以使用数组为路由指定多个中间件:

$router->get('/', ['middleware' => ['first', 'second'], function () {
//
}]);

中间件参数

中间件也可以接收自定义传参,例如,要在运行特定操作前检查已验证用户是否具备该操作的 “ 角色 “ ,可以创建 RoleMiddleware 来接收角色名称作为额外的传参。

附加的中间件参数将会在 $next 参数之后被传入中间件:

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
<?php

namespace App\Http\Middleware;

use Closure;

class RoleMiddleware
{
/**
* 运行请求过滤
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @param string $role
* @return mixed
*/
public function handle($request, Closure $next, $role)
{
if (! $request->user()->hasRole($role)) {
// 重定向...
}

return $next($request);
}

}

在路由中可使用冒号 : 来区隔中间件名称与指派参数,多个参数可使用逗号作为分隔:

1
2
3
$router->put('post/{id}', ['middleware' => 'role:editor'], function ($id) {
//
});

Terminable 中间件

有时中间件可能需要在 HTTP 响应发送到浏览器之后处理一些工作。例如, "session" 中间件会在响应发送到浏览器之后将会话数据写入存储器中。
想要做到这一点,你需要定义一个名为 "terminable" 的中间并添加一个 terminate 方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php

namespace Illuminate\Session\Middleware;

use Closure;

class StartSession
{
public function handle($request, Closure $next)
{
return $next($request);
}

public function terminate($request, $response)
{
// 存储 session 数据...
}
}

terminate 方法应该同时接收和响应。一旦定义了这个中间件,你应该将它添加到路由列表或 bootstrap/app.php 文件的全局中间件中。

控制器

基础控制器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php

namespace App\Http\Controllers;

use App\User;

class UserController extends Controller
{
/**
* 获取指定 ID 的用户
* @param int $id
* @return Response
*/
public function show($id)
{
return User::findOrFail($id);
}
}

然后像下面这样将路由指向控制器的方法:

1
$router->get('user/{id}', 'UserController@show');

控制器中间件

中间件 可通过如下方式分配到路由中:

1
2
3
4
$router->get('profile', [
'middleware' => 'auth',
'uses' => 'UserController@showProfile'
]);

更方便的方式是在控制器的构造方法里面使用 middleware 方法指定中间件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public function __construct()
{
$this->middleware('auth');

$this->middleware('log', ['only' => [
'fooAction',
'barAction',
]]);

$this->middleware('subscribed', ['except' => [
'fooAction',
'barAction',
]]);
}

依赖注入

构造器注入

Lumen 使用 「 服务容器 」 来解析所有的控制器的依赖注入。
因此,你可以在控制器的构造函数中使用类型提示需要的任何依赖。这些依赖将会自动的解析并注入到控制器实例中:

1
2
3
4
5
6
use App\Repositories\UserRepository;

public function __construct(UserRepository $ur)
{
$this->users = $ur;
}

方法注入

除了构造器注入以外,你也可以在你的控制器方法中使用类型提示依赖项。
例如,在某个方法中添加 Illuminate\Http\Request 实例的类型提示:

1
2
3
4
5
6
7
8
9
10
11
12
13
use Illuminate\Http\Request;

public function store(Request $request)
{
$name = $request->input('name');
}

// 用类型提示注入 Illuminate\Http\Request 类和你的路由参数 id
// $router->put('user/{id}', 'UserController@update');
public function update(Request $request, $id)
{
//
}

请求

获取请求实例

要通过依赖注入的方式获取当前HTTP请求的实例,你应该在控制器构造函数或方法中使用 Illuminate\Http\Request 类,当前请求实例将自动被 服务容器 注入:

同方法注入.

获取请求的URI

1
2
3
4
5
6
7
8
9
10
11
12
$uri = $request->path();

// is 方法可以校验接收到的请求 URI 是否与指定规则匹配,你可以使用 * 符号作为通配符:
if ($request->is('admin/*')) {
//
}

// 如果要获取完整的 URL 而不是 URI,可以使用 url 或者 fullUrl 方法:
// 不包含请求参数
$url = $request->url();
//抱会请求参数
$url = $request->fullUrl();

获取请求的方法

1
2
3
4
5
6
$method = $request->method();

// 使用 isMethod 方法校验 HTTP 动作是否与指定字符串匹配:
if ($request->isMethod('post')) {
//
}

PSR-7 请求

PSR-7 标准规定了 HTTP 消息接口包含了请求及响应,如果你想获得 PSR-7 的请求实例,就需要先安装几个库,
Laravel 使用 SymfonyHTTP 消息桥接组件,将原 Laravel 的请求及响应转换至 PSR-7 所支持的实现:

1
2
composer require symfony/psr-http-message-bridge
composer require zendframework/zend=diactoros

安装完这些库后,你就可以在路由或控制器中,简单的对请求类型使用类型提示来获取 PSR-7 请求:

1
2
3
4
user Psr\Http\Message\ServerRequestInterface;
$router->get('/', function (ServerRequestInterface $request) {
//
});

获取输入数据

  • 获取指定输入值
1
2
3
4
5
6
7
8
$name = $request->input('name');

// 你可以在 input 方法的第二个参数中传入一个默认值,当请求参数不存在时,就会返回默认值:
$name = $request->input('name', 'Caoxl');

// 当数据是以数组形式输入时,你可以使用 " 点 " 符号来获取数组:
$name = $request->input('array.0.name');
$name = $request->input('array.*.name');
  • 确定输入值是否存在
1
2
3
4
5
6
7
8
9
10
11
12
13
if ($request->has('name')) {
//
}

// 当给定一个数组时, has 方法将确定是否所有指定值都存在:
if ($request->has(['name', 'email'])) {
//
}

// 如果你想确定请求中是否存在值并且不为空,可以使用 filled 方法:
if ($request->filled('name')) {
//
}
  • 获取所有输入数据
1
$input = $reuest->all();
  • 获取部分输入数据
1
2
3
4
$input = $request->only(['username', 'password']);
$input = $request->only('username', 'password');
$input = $request->except(['credit_card']);
$input = $request->except('credit_card');

文件上传

  • 获取上传文件
1
2
3
4
5
6
$file = $request->file('photo');

// 可以使用 hasFile 方法确认上传的文件是否存在:
if ($request->hasFile('photo')) {
//
}
  • 验证上传是否成功
1
2
3
if ($request->file('photo')->isValid()) {
//
}
  • 移动上传文件
1
2
$request->file('photo')->move($destinationPath);
$request->file('photo')->move($destinationPath, $fileName);

响应

基本响应

1
2
3
$router->get('/', function () {
return 'Hello World';
});

响应对象

1
2
3
4
5
6
7
8
9
10
11
12
use Illuminate\Http\Response;

$router->get('home', function () {
return (new Response($content, $status))
->header('Content-Type', $value);
});

// 为了方便起见,你可以使用 response 辅助函数:
$router->get('home', function () {
return response($content, $ststus)
->header('Content-Type', $value);
});

附加标头至响应

1
2
3
4
5
6
7
8
9
10
11
12
return response($content)
->header('Content-Type', $type)
->header('X-header-One', 'Header Value')
->header('X-header-Two', 'Header Value')

// 或者你可以使用 withHeaders 方法来设置数组标头:
return response($content)
->withHeaders([
'Content-Type' => $type,
'X-Header-One' => 'Header Value',
'X-Header-Two' => 'Header Value',
]);

JSON响应

json 方法会自动将标头的 Content-Type 设置为 application/json,并通过 PHP 的 json_encode 函数将指定的数组转换为 JSON:

1
2
3
4
5
6
7
8
9
10
11
12
13
return response()->json(['name' => 'Abigail', 'state' => 'CA']);

// 你可以选择提供一个状态码和一个额外的标题数组:
return response()->json(
['error' => 'Unauthorized'],
401,
['X-Header-One' => 'Header Value']
);

// 如果你想创建一个 JSONP 响应,则可以使用 json 方法并加上 setCallback 方法:
return response()
->json(['name' => 'Abigail', 'state' => 'CA'])
->setCallback($reqeust->input('callback'));

文件下载

1
2
return response()->download($pathToFile);
return response()->download($pathToFile, $name, $headers);

重定向

1
2
3
4
5
6
7
8
9
10
11
12
$router->get('dashboard', function () {
return redirect('home/dashboard');
});

// 重定向至命名路由
return redirect()->route('login');

// 如果你的路由有参数,则可以将参数放进 route 方法的第二个参数,如下:
return redirect()->route('profile', ['id' => 1]);

// 如果你要重定向至路由且路由的参数为 Eloquent 模型的「ID」,则可以直接将模型传入, ID 将会自动被提取:
return redirect()->route('profile', [$user]);

用户认证

认证服务提供者

注意: 在使用 Lumen 的认证功能前,请取消 bootstrap/app.php 文件中的 AuthServiceProvider 调用代码的注释。

1
2
3
$this->app['auth']->viaRequest('api',function ($request) {
// 返回User 或 null
});

获取已认证的用户信息

就像 Laravel 框架一样,你可以使用 Auth::user() 方法获取当前用户,
或者,你可以在 Illuminate\Http\Request 实例上使用 $request->user() 方法:

1
2
3
4
5
6
7
use Illuminate\Http\Request;

$router->get('/post/{id}', ['middleware' => 'auth'], function (Request $request, $id) {
$user = Auth::user();

$user = $request->user();
})

注意: 如果你想使用 Auth::user() 来获取当前的认证用户,你需要去掉 bootstrap/app.php 文件中 $app->withFacades() 方法调用的注释。

当然,所有你想要认证的路由,都可以分配给 auth 中间件 ,但需要你去掉 bootstrap/app.php 文件中对 $app->routeMiddleware() 调用的注释:

1
2
3
$app->routeMiddleware([
'auth' => App\Http\Middleware\Authenticate::class,
]);

用户授权

与 Laravel 的差异

定义权限

在 Lumen 中,你可以很方便的在你的 AuthServiceProvider 文件中使用 Gate facade 来定义你的权限

1
2
3
Gate::define('update-post', function ($user. $post) {
return $user->id === $post->user_id;
});

定义授权策略

和 Laravel 不一样的是,Lumen 的 AuthServiceProvider 中没有 $policies 数组。
但是你可以在 AuthServiceProviderboot 方法里使用 Gate facade 来调用 policy 方法:

1
Gate::policy(Post::class, PostPolicy::class);

检查权限

你可以像 Laravel 一样使用 Gate facade 来检查权限,首先你要确保你的 bootstrap/app.php 文件开启了 facade

1
2
3
4
5
6
7
if (Gate::allows('update-post', $post)) {
//
}

if (Gate::denies('update-post', $post)) {
abort(403);
}

当然你也可以传递 User 实例来检查他的权限:

1
2
3
4
5
6
7
if ($request->user()->can('update-post', $post)) {
// 用户拥有更新内容的权限
}

if ($request->user()->cannot('update-post', $post)) {
abort(403);
}

数据库

基本用法

注意: 如果你想使用 DB Facade,你应该去掉在 bootstrap/app.php 文件中 $app->withFacades() 的调用的注释。

例如,在不启用 Facades 时,你可以通过 app 辅助函数连接数据库:

1
$results = app('db')->select("SELECT * FROM users");

或者,在启用 Facades 后,你可以通过 DB Facade 来连接数据库:

1
$results = DB::select("SELECT * FROM users");

加密与解密

配置

在使用 Lumen 的加解密前,你应该先把 .env 文件中的 APP_KEY 选项设置为 32 位随机字符串。如果没有适当地设置这个值,所有被 Lumen 加密的值都将是不安全的。

加密

你可以使用 Crypt 门面来加密一个值。所有的加密值都使用 OpenSSLAES-256-CBC 来进行加密。
此外, 所有加密过的值都会使用消息认证码 (MAC)来进行签名,以检测加密字符串是否被篡改过 :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php

namespace App\Http\Controllers;

use App\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Crypt;

class UserController extends Controller
{
public function storeSecret(Request $request, $id)
{
$user = User::findOrFail($id);

$user->fill(['secret' => Crypt::encrypt($request->secret)])->save();
}
}

解密

当然,你可以使用 Crypt 门面的 decrypt 方法来解密值。如果该值不能够被正确的解密,
例如当 MAC(消息认证码)无效时,就会抛出异常 Illuminate\Contracts\Encryption\DecryptException

1
2
3
4
5
6
7
use Illuminate\Contracts\Encryption\DescryptException;

try {
$decrypted = Crypt::decrypt($encryptedValue);
} catch (DecryptException $e) {
//
}

错误日志

重写日志

首先在bootstrap\app.php中修改:

1
2
3
$app = new App\Application(
realpath(__DIR__.'/../')
);

然后再修改Application

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
<?php

// Custom default or hard-coded behavior of lumen
// @caoxl

namespace App;

use Laravel\Lumen\Application as LumenBase;
use Monolog\Formatter\LineFormatter;
use Monolog\Handler\StreamHandler;
use Monolog\Logger;

class Application extends LumenBase
{
// Rewrite log handler
protected function getMonologHandler()
{
return (
new StreamHandler(storage_path(env(
'APP_LOG_PATH',
'logs/' . date('Y-m-d') . '.log'
)),
Logger::DEBUG
))
->setFormatter(new LineFormatter(null, null, true, true));
}
}

事件

生成器

Lumen 中没有可以用来生成事件和监听器的命令,你可以通过简单复制 ExampleEvent 或者 ExampleListener 文件来定义你自己的事件和监听器,这两个示例文件提供了每个事件和监听器的基础类结构。

注册事件或监听器

像 Laravel 框架一样,Lumen 应用内置的 EventServiceProvider 提供了一个注册所有事件监听器的地方。 listen 属性是一个数组,它包含了所有的事件(键)和监听器(值)。所以,你可以根据应用程序的需要添加事件到这个数组:

1
2
3
4
5
protect $listen = [
'App\Events\ExampleEvent' => [
'App\Listeners\ExampleListener',
],
];

触发事件

你可以使用 event 辅助函数或者 Event 门面在 Lumen 应用程序中触发事件。同样,这些函数的行为与 Laravel 框架一致:

1
2
3
event(new ExampleEvent);

Event::dispatch(new ExampleEvent);

队列

Lumen 不包括用于自动创建新 Job 类的生成器。因此你需要复制框架所带的 ExampleJob 类。
这个类提供了每个 Job 类共享的基本结构。 ExampleJob 所继承的 Job 基类已包含所需的 InteractsWithQueueQueueableSerializesModels trait

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php

namespace App\Jobs;

class ExampleJob extends Job
{
public function __construct() {

}

public function handle()
{

}
}

调度作业

1
dispatch(new ExampleJob);

当然,你也可以使用 Queue facade。如果你选择使用 facade,请务必在 bootstrap/app.php 文件中取消对 $app->withFacades() 调用的注释:

1
Queue::push(new ExampleJob);

服务容器

Laravel 的服务容器是一个管理类依赖和执行依赖注入的强力工具
依赖注入是个花俏的名词,事实上是指:类的依赖通过构造器或在某些情况下通过「setter」方法「注入」。

获取服务容器

Laravel\Lumen\Application 实例是 Illuminate\Container\Container 的扩展,所以你可以当做服务容器来使用。

通常我们会在 服务提供者 注册我们的容器解析规则。

解析实例

1
$instance = app(Something::class);

服务提供者

服务提供者是所有 Lumen 应用程序启动的中心所在。包括你自己的应用程序,以及所有的 Lumen 核心服务,都是通过服务提供者启动的。

编写服务提供者

所有的服务提供者都继承了 Illuminate\Support\ServiceProvider 这个类。
这个抽象类要求你在你的提供者上定义至少一个方法:register
register 方法内,你应该 只需要将事物绑定到服务容器中
永远不要试图在 register 方法中注册任何事件侦听器、路由或任何其它功能。

注册方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php

namespace App\Providers;

use Tiak\Connection;
use Illuminate\Support\ServiceProvider;

class RiakServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->singelton(Connection::class, function ($app) {
return new Connection(config('riak'));
});
}
}

启动方法

此方法在所有其他服务提供者都注册之后才能调用,也就意味着可以访问已经被框架注册的所有服务:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php

namespace App\Providers;

use Queue;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
public function boot()
{
Queue::failing(function ($event) {

});
}
}

注册服务提供者

所有的服务提供者在 bootstrap/app.php 文件中被注册。这个文件中包含对 $app->register() 方法调用。
你也可以额外的调用 $app->register() 来注册你的服务提供者。

测试

测试环境

在运行测试时,Lumen 自动配置讲缓存驱动配置为 array ,意味着在测试的时候不会保存任何的缓存数据

你可以随意创建其他必要的测试配置环境。 testing 的环境变量可以在 phpunit.xml 文件中进行修改

定义和运行测试

要创建一个测试用例,直接将新的测试文件创建到 tests 文件夹下即可。测试文件必须继承 TestCase

1
2
3
4
5
6
7
8
9
<?php

class FooTest extends TestCase
{
public function testSomethingIsTrue()
{
$this->assertTrue(true);
}
}

数据验证

表单请求

Lumen 不支持表单请求。 如果想使用表单请求,则应使用完整的 Laravel 框架。

  • $this->validate 方法
1
2
3
4
5
6
7
8
9
10
use Illuminate\Http\Request;

$router->post('/user', function (Request $request) {
$this->validate($request, [
'name' => 'required',
'email' => 'required|email|unique:users'
]);

// 存储用户...
});
  • existsunique 规则

如果想要使用 existsunique 验证规则,则应该在 bootstrap/app.php 文件中取消对 $app->withEloquent() 方法调用的注释。

  • 视图变量 $errors

Powered by Hexo and Hexo-theme-hiker

Copyright © 2017 - 2023 Keep It Simple And Stupid All Rights Reserved.

访客数 : | 访问量 :