HTTP路由
- 基本路由
- 路由参数
- 必填参数
- 可选参数
- 正则表达式约束
- 命名路由
- 路由组
- 中间件
- 命令空间
- 路由前缀
基本路由
你可以在 route/web.php 文件中定义应用程序的全部路由。最基本的Lumen路由仅接受URL和一个Closure:
$router->get('foo', function(){
return 'Hello, World';
});
$router->post('foo', function(){
//
});
可供使用的路由方法
我们可以注册路由来响应任何方法的HTTP请求:
$router->get($uri, $callback);
$router->post($uri, $callback);
$router->put($uri, $callback);
$router->patch($uri, $callback);
$router->delete($uri, $callback);
$router->options($uri, $callback);
路由参数
必填参数
当然,有时需要在路由中捕获一些URL片段。例如,从URL中捕获用户的ID,可以通过通过定义路由参数来执行此操作:
$router->get('user/{id}', function ($id) {
return "User:" . $id;
});
也可以根据需要在路由中定义多个参数:
$router->get('posts/{postId}/comments/{commentId}', function ($postId, $commentId) {
//
});
路由的参数都会被放在[大括号]内。当运行路由时,参数会传递到Closure里面。
注意:路由参数不能包含-字符。请用下划线_代替。
可选参数
你可以通过将部分路由URI包含在[...]中来定义可选的路由参数。那么像/foo[bar]将会匹配到/foo和/foobar。可选参数仅支持放在URI的末尾。换句话说,你不能在路由定义的中间位置放置可选参数:
$router->get('user'[/{name}]', function ($name = null) {
return $name;
});
正则表达式约束
你可以通过在路由定义中使用正则表达式来约束路由参数的格式:
$router->get('user2/{name:[a-zA-Z]}', function () {
//
});
命名路由
命名路由可以方便的为特定路由生成URL或者进行重定向。你可以使用as数组键指定名称到路由上:
$router->get('profile', ['as' => 'profile', function() {
//
}]);
你还可以指定控制器行为的路由名称:
$router->get('profile', [
'as' => 'profile',
'uses' => 'UserController@showProfile',
]);
生成指定路由的URL
为路由制定了名称后,就可以使用全局辅导函数route来生成链接或者重定向到该路由:
//Generating URLs...
$url = route('profile');
//Generating Redirects...
return redirect()->route('profile');
如果是有定义参数的命名路由,可以把参数作为route函数的第二个参数传入,指定的参数将会自动插入到URL中对应的位置:
$router->get('user/{id}/profile', ['as' => 'profile', function ($id) {
return $id;
}]);
$url = route('profile', ['id' => 1]);
路由组
路由群组允许你共用路由属性,例如:中间件、命名空间,你可以利用路由组统一为多个路由设置共同属性,而不需要在每个路由都设置一次。共用属性被指定为数组格式,当做$router->group方法的第一个参数。
为了了解更多路由群组的相关内容,我们可以通过几个常用样例来熟悉这些特性。
中间件
要给路由组中所有的路由分配中间件,你可以在group属性数组中使用middleware字段。中间件会依照它们在数组中列出的顺序来运行:
$router->group(['middleware' => 'auth'], function () use ($router) {
$router->get('/', function () {
//使用Auth中间件
});
$router->get('user/profile', function () {
//使用Auth 中间件
});
});
命名空间
另一个常见的例子是,指定相同的PHP命名空间给控制器群组。可以使用namespace参数来指定群组内所有控制器的命名空间:
$router->group(['namespace' => 'Admin'], function () use ($router) {
//使用"App\Http\Controllers\Admin"命名空间
$router->group(['namespace' => 'User', function () use ($router) {
//使用"App\Http\Controllers\Admin\User" 命名空间...
}]);
});
路由前缀
通过路由群组数组属性中的prefix,在路由群组内为每个路由指定的URI加上前缀。例如,你可能想要在路由群组中将所有的路由URI加上前缀admin:
$router->group(['prefix,' => 'admin'], function () use ($roter) {
$router->get('users', function () {
//匹配The "/admin/users"URL
});
});
你也可以使用prefix参数去指定路由群组中共用的参数:
$router->group(['prefix' => 'accounts/{accountId}'], function () use ($router) {
$router->get('detail', function ($accountId) {
//匹配The "/accounts/{accountId}/detail" URL
});
});
HTTP中间件
- 简介
- 定义中间件
注册中间件
- 全局中间件
- 为路由指定中间件
- 中间件参数
Terminal中间件
简介
HTTP中间件提供了一个方便的机制来过滤进入应用程序的HTTP请求。例如,lumen内置了一个中间件来验证用户的身份认证。如果用户未通过省份证,中间件将会把用户导向登录页面,反之,当用户通过了身份证,中间件将会通过此请求并接着往下执行。
当然,除了身份证之外,中间件也可以被用来运行各式各样的任何,如:CORS中间件负责替换所有即将离开程序的响应加入适当的标头;而日志中间件则可以记录所有出入应用从程序的请求。
所有的中间件都放在app/Http/Middleware目录内。
定义中间件
你可以通过赋值lumen内置的示例文件ExampleMiddleware来创建一个中间件。在这个中间件中,我们只允许参数age大于200的请求才能访问该路由。否则,我们将此用户重定向到首页"home"这个URI上。
正如你所见,假如给定的age参数小于或者等于200,这个中间件将返回一个HTTP重定向到客户端;否则,请求将进一步传递到应用中。要让请求继续传递到应用程序中(即允许“通过”中间件验证的),只需要使用$request作为参数去调用回调函数$next。
最好将中间件想象为一系列HTTP请求必须经过才能进入你的应用的层。每一个都会检查请求
(是否符合某些条件),(如果不符合)甚至可以(在请求访问你的应用之前)完全拒绝掉。
前置/后置中间件
中间件是在请求之前或者之后运行取决于中间件本身。例如,接下来的这个中间件将在应用处理请求before执行其任务:
namespace App\Http\Middleware;
use Closure;
class BeforeMiddleware
{
public function handle($request, Closure $next)
{
//执行操作
return $next($request);
}
}
而接下来的这个中间件将在应用处理请求之后执行其任务:
namespace App\Http\Middelware;
use Closure;
class AfterMiddleware
{
public function handle($request, Closure $next)
{
return $next($request);
}
}
注册中间件
全局中间件
若是希望中间件在应用处理每个HTTP请求期间运行,只需要在bootstrap/app.php文件中的
$app->middleware()方法中列出这个中间件:
$app->middleware([
App\Http\Middleware\OldMiddleware::class
]);
为路由分配中间件
如果你想将中间件分配给特定的路由,首先需要在bootstrap/app.php文件中调用$app->routeMiddleware()方法时为中间件分配一个简短的键:
$app->routeMiddleware([
'auth' => App\Http\Middleware\Authenticate::class,]);
一旦在HTTP内核中定义好了中间件,就可以在路由选项内使用middleware键:
$router->get('admin/profile', ['middleware' => 'auth', function () {
//
}]);
可以使用数组为路由指定多个中间件:
$router->get('/', ['middleware' => ['first', 'second'], function () {
//
}]);
中间件参数
中间件也可以接受自定义传参,例如,要在运行特定操作前检查已验证用户是否具备该操作的“角色”,可以创建RoleMiddleware来接受角色名称作为额外的传参。
附加的中间件参数将会在$next参数之后被传入中间件:
user()->hasRole($role)) {
//重定向...
}
return $next($request);
}
}
在路由中可以使用冒号:来区隔中间件名称与指派参数,多个参数可以使用逗号作为分割:
$router->put("post/{id}", ['middleware' => 'role:editor', function($id) {
//
}]);
Terminable 中间件
有时中间件可能需要在HTTP响应发送到浏览器哦之后处理一些工作。例如,“session”中间件会在响应发送到浏览器之后将会话数据写入存储器中。想要做到这一点,你需要定义一个名为“terminable”的中间并添加一个terminal方法:
terminate方法应该同时接受和响应。一旦定义了这个中间件,你应该将它添加路由列表或者bootstrap/app.php文件的全局中间件中。
在你的中间件上条用terminite调用时,lumen会从服务器中解析出一个新的中间件实例。如果你希望在handle及terminate方法被调用时使用一致的中间件实例,只需要在容器中使用容器的singleton方法注册中间件。
HTTP控制器
简介
为了替代把所有的请求处理逻辑都定义在routes.php路由文件中,你或许想要使用控制来组织这些行为。控制器可以把相关的请求处理逻辑整合为一个控制器类。控制器类文件被存储在app/HTTP/Controllers目录下。
基础控制器
这里有一个基础控制器的示例。所有lumen的控制器都应该继承lumen安装时内置的默认的控制器基类:
我们可以像下面这样路由指向控制器的方法:
$router->get("user/{id}", "UserController@show");
现在,当请求匹配到这个特定的URL时,UserController类中的show方法就会执行。当然,路由的参数也同样传递给了这个方法。
控制器和命名空间
有一点非常重要,那就是我们要注意在定义控制器路由时,不需要指定完整的控制器命名空间。我们只需定义“根”命名空间App\Http\Controllers之后的类名部分。默认情况下,bootstrap/app.php文件在加载routes.php时已经把所有路由都配置到了根控制器命名空间。
如果你选择在App\Htpp\Controllers目录内层使用PHP命名空间嵌套或者组织控制器,只要使用相对于App\Http\Controllers根命名空间的特定名称即可。因此,如果你的控制器类全名是App\Http\Controllers\Photos\AdminController,那么你应该注册一个路由,如下所示:
$router->get('foo', 'Photos\AdminController@method');
命名控制器路由
像闭包那样,你可以给控制器路由指定一个名称:
$router->get('foo', ['uses' => 'FooController@method', 'as' => 'name']);
你也可以使用route辅助函数,来生成指向控制器路由的URL:
$url = route('name');
控制器中间件
中间件可通过如下方式分配到路由中:
$router->get('profile', [
'middleware' => 'auth',
'uses' => 'UserController@showProfile'
]);
然而,更方便的方式是在控制器的构造方法里面使用middleware方法指定中间件。你甚至可以限制中间件只应用于该控制器类的某些方法:
class UserController extends Controller
{
/**
实例化一个新的UserController实例。
@return void
*/
public function __construct()
{
$this->middleware('auth');
$this->middleware('log', [
'only' => ['fooAction', 'barAction']
]);
$this->middleware('subscribed', ['except' => [
'fooAction',
'barAction',
]]);
}
}
依赖注入与控制器
构造器注入
lumen使用[服务容器]来解析所有的控制器的依赖注入。因此,你可以在控制器的构造函数中使用类型提示需要的任何依赖。这些依赖会自动的解析并注入到控制器实例中:
namespace App\Http\Controllers;
use App\Repositories\UserRepository;
class UserController extends Controller
{
/**
*新建一个控制器实例
*
*@param UserRepository $users
*@return void
*/
public function __contruct(UserRepository $users)
{
$this->users = $users;
}
}
方法注入
除了构造器注入以外,你也可以在你的控制器方法中使用类型提示依赖。例如,在某个方法中添加Illuminate\Http\Request实例的类型提示:
input('name');
}
}
如果你想在控制器里获取路由参数,只需要在路由之后列出参数即可。例如,你的路由这样来定义:
$router->put('user/{id}', 'UserController@update');
你可以像下面的例子一样定义你的控制器,用类型提示注入Illuminate\Http\Request类和你的路由参数id:
HTTP请求
获取请求实例
要通过依赖注入的方式获取当前HTTP请求的实例,你应该在控制器构造函数或者方法中使用
Illuminate\Http\Request类,当前请求实例将自动被服务容器注入:
input('name');
//
}
}
如果你的控制器方法也期望从路由参数中获取数据,只需要将路由参数放在其他依赖后面,比如你的路由是这样定义的:
$router->put('user/{id}', 'UserController@update');
像下面这样定义你的控制器方法,就可以使用Illuminate\Http\Request类型提示,同时获取到路由参数id:
基本请求信息
Illuminate\Http\Request 实例继承了Symfony\Component\HttpFoundation\Request类,并提供了多种检查HTTP请求的方法,下面是该类的几个使用方法:
获取请求的URI
path方法会返回请求的URI,如果请求的目标是http://domain.com/foo/bar,path方法将会返回foo/bar:
$uri = $request->path();
is方法会返回请求的URI是否与指定规则匹配,你可以使用*符号作为通配符:
if($request->is('admin/*')) {
//
}
如果要获取完整的URL而不是URI,可以使用url或者fullURL方法:
//不包含请求参数
$url = $request->url();
//包含请求参数
$url = $request->fullUrl();
获取请求的方法
method方法将会请求的HTTP动作,你可以使用isMethod方法校验HTTP动作是否与指定字符串匹配:
$method = $request->method();
if ($request->isMethod('post')) {
//
}
PSR-7请求
PSR-7标准规定了HTTP消息接口包含了请求及响应,如果你想获得PSR-7的请求实例,就需要先
安装几个库,laravel使用Symfony的HTTP消息桥组件,将原laravel的氢气及响应转换至PSR-7所支持的实现:
composer require symfony/psr-http-message-bridge
composer require zendframework/zend-diactors
安装完这些库后,你就可以在路由或者控制器中,简单的对请求类型使用类型提示来获取PSR-7请求:
use Psr\Http\Message\ServerRequestInterface;
$router->get('/', function (ServerReuestInterface $request) {
//
});
如果你从路由或者控制器反悔了一个PSR-7的响应实例,那么它会被框架自动转换为laravel的响应实例并显示。
获取输入数据
获取指定输入值
你可以通过Illuminate\Http\Request实例,使用几个简单的方法来获取所有的用户输入数据,而不需要担心请求的HTTP动作,因为它们的获取方式是相同的:
$name = $request->input('name');
你可以在input方法的第二个参数中传入一个默认值,当请求参数不存在时,就会返回默认值:
$name = $request->input('name', 'Sally');
当数据是以数组形式输入时,你可以使用“点”符号来获取数组:
$name = $request->input('products.0.name');
$name = $request->input('products.*.name');
确认输入值是否存在
你可以通过has方法判断输入值是否存在,输入值存在时has方法将会返回true:
if($request->has('name')) {
//
}
当给定一个数组时,has方法将确认是否所有指定值都存在:
if ($request->has(['name', 'email'])) {
//
}
如果你想确定请求中是否存在值并且不为空,可以使用filled方法:
if($request->filled('name')) {
//
}
获取所有输入数据
你可以使用all方法以数组形式获取所有的输入数据:
$input = $request->all();
获取部分输入数据
如果你想获取数据的子集,你可以是only和except方法,这两个方法都接受单个数组或者动态列表作为参数:
$input = $request->only(['username', 'password']);
$input = $request->only('username', 'password');
$input = $request->except(['credit_card']);
$input = $request->except('credit_card');
文件上传
获取上传文件
你可以使用Illuminate\Http\Request实例中的发file方法获取上传的文件,file方法返回的对象是Symfony\Component\HttpFoundation\File\UploadFile类的实例,这个类继承了PHP的SplFileInfo类,并且提供了多种与文件交互的方法:
$file = $request->file('photo');
你可以使用hasFile方法确认上传的文件是否存在:
if ($request->hasFile('photo')) {
//
}
验证上传是否成功
除了检查文件是否存在之外,你还可以通过isValid方法验证上传是否存在问题:
if ($request->file('photo')->isValid()) {
//
}
移动上传文件
要将上传的文件移动到新的位置,你应该使用move方法,这个方法会将文件从临时位置(由PHP配置决定)移动到你指定永久存储位置:
$request->file('photo')->move($destinationPath);
$request->file('photo')->move($destinationPath, $fileName);
其他上传文件方法
UploadFile实例还有很多其他可用的方法,可以到该类的API文档了解这些方法的详细信息。
HTTP响应
基本响应
当然,所有的路由及控制器必须返回某个类型的响应,并发送回用户的浏览器。laravel提供了集中不同的方法来返回响应。最基本的响应就是从路由或者控制器简单的返回一个字符串:
$router->get('/', function () {
return 'Hello World';
});
指定的字符串会被框架自动转为HTTP响应。
响应对象
但是,对于大多数路由和控制器行为操作,你将返回完整的Illuminate\Http\Response实例。返回完整的Response实例允许你自定义响应的HTTP状态码和标题。一个Response实例继承自Symfony\Componet\HttpFoundation\Response类,并且提供了多种构建HTTP响应的方法:
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, $status)
->header('Content-Type', $value);
});
注意:有关Response方法的完整列表可以参考API文档一级Symfony api文档。
附加标头至响应
大部分的响应方法是可以链式调用的,折让你可以顺畅的创建响应。举例来说,你可以在响应发送给用户之前,使用header方法增加一系列的标头至响应:
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',
]);
其他响应类型
使用辅助函数response可以轻松的生成其他类型的响应实例、当你调用辅助函数response并且不带任何参数时,将会返回Laravel\Lumen\Http\ResponseFactory contract的实现。此Contract提供了一些有用的方法来生成响应。
JSON响应
json方法会自动将标头的Content-Type设置为application/json。并且通过PHP的接送_encode函数将制定的数组转换为json:
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($request->input('callback'));
文件下载
download方法可以用于生成强制让用户的浏览器下载指定路劲文件的响应。download方法接受文件名称作为方法的第二个参数,此名称为用户下载文件时看见的文件名称。最后,你可以传递一个标头的数组作为第三个参数传入该方法:
return reponse()->download($pathToFile);
return response()->download($pathToFile, $name, $headers);
注意:管理文件下载的扩展包Symfony HTTPFoundation,要求下载文件必须是ASCII文件名。
重定向
重定向响应是类illuminate\Http\RedirectResponse的实例,并且包含用户要重定向至另一个URL所需的正确标头。有几种方法可以生成RedirectResponse的实例。最简单的方法就是通过redirect辅助函数:
$router->get('dashboard', function () {
return redirect('home/dashboard');
});
重定向至命名路由
当你调用redirect辅助函数并且不带任何参数时,将会返回laravel\Lumen\Http\Redirecotr的实例,你可以对该Redirector的实例调用任何方法。举个例子,要生成一个RedirectResponse到一个命名路由,你可以使用route方法:
return redirect()->route('login');
如果你的路由有参数,则可以将参数放进route方法的第二个参数,如下:
//For a route with follwing URI:profile/{id}
return redirect()->route('profile', ['id' => 1]);
如果你要重定向至路由并且路由的参数为Eloquent模型的[ID],则可以直接将模型传入,ID将会自动被提取:
return redirect()->route('profile', [$user]);
用户认证
简介
lumen虽然与laravel使用了相同的底层类库实现,但是因lumen面向的是无状态API的开发,不支持session,所以默认的配置不同。lumen必须使用无状态的机制来实现认证,如api令牌(Token)。
开始
认证服务提供者
注意:在使用lumen的认证功能前,请取消bootstrap/app.php文件中的AuthServiceProvider调用代码的注释。
AuthServiceProvider存放在app/Providers文件夹中,此文件中只有一个Auth::viaRequest调用。viaRequest会在系统需要认证的时候被调用,此方法接受一个Closure(匿名函数)参数。在此closure(匿名函数)内,你可以任意的解析App\User并返回,或者在解析失败时返回null:
$this->app['auth']->viaRequest('api', function ($request) {
//返回User或者null...
});
同样,你可以使用你期望的方式取得用户认证,比如在请求头或者查询字符串中使用api令牌、请求中的bearer令牌,或者使用应用程序需要的任何其他方法。
如果你的项目没有使用Eloquent,你需要返回一个Illuminate\Auth\GenericUser类的实例。这个类接受一个属性数组作为构造函数的唯一参数:
use Illuminate\Auth\GenericUser;
return new GenericUser(['id' => 1, 'name' => 'Taylor']);
缓存
简介
laravel为各种缓存系统提供了统一的api。缓存配置位于.env文件中。在该文件中你可以指定应用默认使用哪个缓存驱动。laravel支持当前流行的后端缓存,例如memcached和Redis。
不同于laravel
lumen缓存驱动与laravel缓存驱动使用了完全相同的代码。除配置之外,在lumen中使用缓存和在laravel中使用缓存没有区别;因此,请参阅laravel文档来获取使用示例。
注意:在使用cache facade之前,请确保在bootstrap/app.php文件中没有注释掉$app->withFacaes()方法的调用。
Redis支持
在使用lumen的Redis缓存之前,你需要通过composer安装illuminate/redis(5.5.*)包。然后,你需要在bootstrap/app.php文件中注册illuminate\Redis\RedisServiceProvider。
如果你没有在bootstrap/app.php文件中调用$app->withEloquent(),那么你应该在bootstrap/app.php文件中调用$app->configure('database');以确保正确加载Redis数据库配置。
缓存
简介
laravel为各种缓存系统提供了统一的api。缓存配置位于.env文件中。在该文件中你可以指定应用默认使用哪个缓存驱动。laravel支持当前流行的后端缓存,例如Memcached和Redis。
不同于laravel
lumen缓存驱动与laravel缓存驱动使用了完全相同的代码。除配置之外,在lumen中使用缓存和在laravel中使用缓存没有区别;因此,请参阅laravel文档来获取使用示例。
注意:在使用Cache Facade之前,请确保在bootstrap/app.php文件中没有注释掉$app->withFacdes()方法的调用。
Redis支持
在使用lumen的Redis缓存之前,你需要通过composer安装illuminate/redis(5.5.*)包。然后,你需要在bootstrap/app.php文件中注册illuminate\Redis\RedisServiceProvider。
如果你没有在bootstrap/app.php文件中调用$app->withEloquent()。那么你应该在bootstrap/app.php文件中调用$app->configure();以确保正确加载Redis数据库配置。
数据库
配置
lumen让连接数据库和执行查询变得非常简单。目前lumen支持四种数据库系统:MySQL,Postgres,SQLite和SqlServer。
你可以在.env配置文件中使用DB_*选项配置数据库设置,例如数据库驱动、host、用户名和密码。
基本用法
注意:如果你想使用DB facade,你应该去掉在bootstrap/app.php文件中$app->withFacades()的调用的注释。
例如,在不启用facades时,你可以通过app辅助函数连接数据库:
$result = app('db')->select("SELECT * FROM users");
或者,在启用facades后,你可以通过DB facade来连接数据库:
$result = DB::select("SELECT * FROM users");
基本查询
要了解如何通过数据库组件执行基本的原始SQL查询,请参考laravel文档
查询构造器
lumen同样支持Eloquent ORM的流式查询构造器。要了解这个特性的更多信息,请参阅laravel文档。
Eloquent ORM
如果你喜欢使用Eloquent ORM,你应该去掉bootstrap/app.php文件中对$app->withQloquent()调用的注释。
当然,你可以在lumen中非常容易的使用完整的Eloquent ORM。要了解如何使用Eloquent,请参考laravel文档。
迁移
关于如何创建数据库表和执行迁移的更多文档,请参考laravel文档中的迁移。
加密与解密
配置
在使用lumen的加密之前,你应该先把.env文件中APP_KEY选项设置为32位随机字符串。如果没有适当地设置这个值,所有被lumen加密的值都将是不安全的。
基本用法
加密一个值
你可以使用crypt门面来加密一个值。所有的加密值都使用OpenSSL和AES-256-CBC来进行加密。此外,所有加密过的值都会使用消息认证码(MAC)来进行签名,以检测加密字符串是否被篡改过:
例如,我们可以使用encrypt方法加密机密信息,并把它保存在Eloquent模型:
fill([
'secret' => Crpty::encrypt($request->secret)
])->save();
}
}
加密一个值
当然,你可以使用crypt门面的decrypt方法来解密值。如果该值不能够被正确的解密。例如当MAC(消息认证码)无效时,就会抛出异常Illuminate \Contracts\Encryption\DecryptException:
use Illuminate\Contracts\Encryption\DecryptException;
try{
$desrypted = Crypt::decrypt($encryptedValue);
}catch (DecryptException $e){
//
}
错误与日志
简介
当你开始一个新的Lumen项目时,lumen就已经帮你配置好错误和异常处理的操作。另外,lumen也集成了Monolog日志函数库,Monolog支持和提供多种强大的日志处理功能。
有关错误的更多信息,请参阅完整的laravel错误文档。
有关日志的更多信息,请参阅完整的laravel的日志文档。
错误处理
介绍
当你启动一个新的laravel项目时,错误及异常处理是已为你配置好了的。
App\Exceptions\Handler类负责记录应用程序触发的所有异常并呈现给用户。在本文档中,我们将深入探讨这个类。
配置
你的config/app.php配置文件中的debug选项决定了对于一个错误实际上讲显示多少信息给用户。默认情况下,该选项的设置将遵循存储在.env文件中的APP_DEBUG环境变量的值。
对于本地开发,你应该将APP_DEBUG环境变量的值设置为true。在生产环境中,该值赢始终未false。如果在生产将该值设置为true,则可能会将敏感配置暴露给应用程序的最终用户。
异常处理器
report方法
所有异常都是由App\Exceptions\Handler类处理。这个类包含两个方法:report和rendor。我们将详细剖析这些方法。report方法用户记录异常或将它们发送给如Bugsnag或者Sentry等外部服务。默认情况下,report方法将异常传递给记录异常的基类。不过,你可以任何自己喜欢的方式来记录异常。
例如,如果你需要以不同方式报告不同类型的异常,则可以使用PHP的instanceof比较运算符:
/**
*报告或者记录异常
*
*此处是发送异常给sentry、Bugsnag等外部服务的好位置。
*
*@param \Exception $exception
*@return void
*/
public function report()
{
if ($exception instance CustomException) {
//
}
return parent::report($exception);
}
{tip}不要在report方法中进行太多的instanceof检查,而应该考虑使用[可报告异常(reportable exception)]
{(/docs/laravel/5.7/errors#renderable-exceptions)}。
Report辅助函数
有时你可能需要报告异常,但又不希望终止当前请求的处理。report辅助函数允许你使用异常处理器的report方法在不显示错误页面的情况下快速报告异常:
public function isValid()
{
try{
}catch(Exception $e){
report($e);
return false;
}
}
按类型忽略异常
异常处理器的$dontReport属性包含一组不会被记录的异常类型。例如,由04错误导致的异常一级其他几种类型的错误不会写入日志文件。你可以根据需要添加其他异常类型到此数组中:
/**
*不应被报告的异常类型清单。
*
*@var array
*/
protected $dontReport = [
\Illuminate\Auth\AuthenticationException::class,
\Illuminate\Auth\Access\AuthorizationException::class,
\Symfony\Component\HttpKernel\Exception\HttpException::class,
\Illuminate\DataBase\Eloquent\ModelNotFoundException::class,
\Illuminate\Validation\ValidationException::class,
];
Render方法
Rendor方法负责将给定的异常转换为将被发送回浏览器的HTTP响应。默认值情况下,异常将传递给你生成响应的基类。不过,你可以按自己意愿检查异常类型或者返回自己的自定义响应:
/**
*将异常1转换为HTTP响应。
- @param \Illuminate\Http\Request $request
@param \Illuminate\Exception $exception
@return \Illuminate\Http\Response
/
public funtion render($request, Exception $exception)
{
if ($exception instanceof CustomException) {
return response()->view('errors.custom', [], 500);
}
return parent::render($request, $exception);
}
Reportable & Renderable 异常
除了在异常处理器的report和render方法中检查异常类型,你还可以直接在定义异常上定义report和render方法。当定义了这些方法时,它们会被框架自动调用:
namespace App\Exceptions;
use Exception;
class RenderException exteds Exception
{
/**
* 报告异常
@return void
*/
public function report()
{
//
}
/**
* 转换异常为 HTTP 响应
*
* @param \Illuminate\Http\Request
* @return \Illuminate\Http\Response
*/
public function render($request)
{
return response(...);
}
}
HTTP异常
一些异常用描述产生自服务器的HTTP错误代码。例如,[页面未找到]错误(404),【未经授权的错误】(401),甚至可以是开发人员引起的500错误。你可以使用abort辅助函数从应用程序的任何地方生成这样的响应:
abort(404);
辅助函数abort会立即引发一个由异常处理器渲染的异常。你还可以选择性地提供响应文本:
abort(403, 'Unauthorized action.');
自定义HTTP错误页面
laravel可以轻松显示各种HTTP状态代码的自定义错误页面。例如,如果你希望自定义404HTTP状态码的错误页面,可以创建一个resources/views/errors/404.blade.php视图文件。该文件将被用于你的应用程序产生的所有404错误。此目录中的视图文件的命名应匹配它们对应的HTTP状态码。由abort函数引发的HTTPException实例将作为$exception变量传递给视图:
{{ $exception->getMessage() }}
日志
简介
为了帮助你更多的了解应用程序中到底发生了什么,laravel提供了强大的日志服务,允许你将日志消息,系统错误日志记录到文件,甚至使用slack通知到你的整个团队。
在laravel框架中,laravel使用monolog库,它为各种强大的日志处理提供支持。laravel使用配置这些处理程序变得简单,允许泥混合并匹配它们自定义的应用程序日志处理。
配置
所有的应用程序日志系统配置都位于config/logging.php配置文件中。这个文件允许你配置你的应用程序日志通信,
所以务必查看每个可用的通信及它们的选项。当然,我们将在下面回顾一些常用的选项。
默认情况下,laravel将使用stack去记录日志消息。stack通道被用来将多个日志通道聚合到一个单一的通道中。关于堆栈的更多信息,查看以下文档。
配置通道名称
默认强狂下,monolog使用与当前环境匹配的【通道名称】进行实例化,比如production或者local。要改变这个值,需添加一个name选项到你的通道配置中:
'stack' => [
'driver' => 'stack',
'name' => 'channel-name',
'channels' => ['single', 'slack'],
];
可用的通道驱动
名称 描述
single 一个便于创建[多通道]通道的包装器
daily 单个文件或者基于日志通道的路径(StreamHandler)
slack 一个每天轮换的基于monolog驱动的RotatingFileHandler
syslog 一个基于monolog驱动的sysloghandler
errorlog 一个基于monolog驱动的errorlogHandler
monolog 一个可以任何支持monolog处理程序的monolog工厂驱动程序
custom 一个调用指定工厂创建通信通道的驱动程序
{tip}有关monolog和custom驱动,查看高级通道自定义
配置slack通道
slack通道需要url配置选项。这个URL应当与你为slack团队配置的一个incoming webhook相匹配。
构建日志栈
如前所述,stack驱动允许你将多个通道合并到一个单一日志通道中。为了说明如何使用日志栈,让我们看一个你可能在生产应用配置中看到的实例配置:
'channels' => [
'stack' => [
'driver' => 'stack',
'channels' => ['syslog', 'slack'],
],
'syslog' => [
'driver' => 'slack',
'url' => env('LOG_SLACK_WEBHOOK_URL'),
'username' => 'Laravel Log',
'emoji' => ':boom:',
'level' => 'critical',
],
],
让我们剖析这个配置。首先,注意我们的stack通道通过它的Channels选项聚合其它两个通道:syslog和slack。因此,当记录日志消息时,这两个通道都有机会去记录日志消息。
日志级别
注意上面实例中在syslog和slack通道配置中存在的level配置选项。这个选项决定了一个消息必须被通道记录的最小[level]。monolog为laravel的日志服务提供了RFC-542规范中定义的所有日志级别:emergency,
alert,critical,error,warning,notice,info和debug。
因此,假设我们记录一个日志消息使用debug方法:
Log::debug('An informational message.');
根据我们的配置,syslog通道将写消息到系统日志;然而,由于错误消息不是critical或者这个级别之上,它将不被发送到slack。但是,如果我们记录一个emergency消息,它将被同时发送到系统日志和slack,因为emergency级别高于我们对两个通道最低级别的阈值:
Log::emergency('The system is down!');
记录日志消息
你可以通过Log外观类将信息写入到日志。如前所述,日志器提供在RPC 5424规范中定义的八个日志级别:
emergency,alert,critical,error,warning,notice,info和debug:
Log::emergency($message);
Log::alert($message);
Log::critical($message);
Log::error($mesage);
Log::warning($message);
Log::notice($message);
Log::info($message);
Log::debug($message);
因此,你可以调用这些方法中的任何一个去记录相应级别的一个日志消息。默认情况下,消息将写入到你的config/logging.php配置文件配置的默认日志通道中:
namespace App\Http\Controllers;
use App\User;
use Illuminate\Support\Facades\Log;
use App\Http\Controllers\Controllers;
class UserController extendx Controller
{
/**
* Show the profile for the given user.
@param int $id
* @return Response
*/
public function showProfile($id)
{
Log::info('Showing user profile for user: '.$id);
return view('user.profile', ['user' => User::findOrFail($id)]);
}
}
记录日志到指定通道
有时候你可能希望将日志记录到非默认通道。你可以使用log facade中的channel方法,将日志记录到应用配置中存在的任何聚到:
Log::channel('stack')->info('Something happened!');
如果你想按需要创建多个渠道的日志堆栈,你可以使用stack方法:
Log::stack(['single', 'slack'])->info("Something happened!");
先进的monolog日志通道定制
自定义monolog日志通道
首先你可能需要完全配置monolog现有的通道。例如:你想要为现有通道自定义一个monolog formatterInterface实现。
首先,在频道配置文件中定义一个tap数组。tap数组应该该包含所需的类列表,这些类就是Monolog实例创建后需要自定义(或者开发)的类:
'single' => [
'driver' => 'single',
'tap' => [],],
事件
简介
lumen事件提供了简单的监听器实现,允许你订阅和监听事件,事件类通常被保存在app/Events目录下,而它们的侦听器被保存在app/Listeners目录下。
与laravel的差异
通常,lumen中的事件方法恰好与laravel全栈框架功能一致,所以,请阅读完整的laravel文档。lumen同样支持事件广播,它允许客户端的JavaScript监听服务器的事件。然而,这里还是有些差异值得谈论。
生成器
lumen中没有可以用来生成事件和监听器的命令,你可以通过简单赋值ExampleEvent或者ExampleListener文件来定义你自己的事件和监听器,这两个示例文件提供了每个事件和监听器的基础类结构。
注册事件或者监听器
像laravel框架一样,lumen应用内置的EventServiceProvider提供了一个注册所有事件监听器的地方。listen属性一个数组,它包含了所有的事件(键)和监听器(值)。所以,你可以根据应用程序的需要添加事件到这个数组:
/**
*应用程序的事件监听器映射。
*
*@var array
*/
protected $listen = [
'App\Events\ExampleEvent => [
'App\Listeners\ExampleListener',
],
];
触发事件
你可以使用event辅助函数或者event门面在lumen应用程序中触发事件。同样,这些函数的行为与laravel框架一致:
event(new ExampleEvent);
Event::dispatch(new ExampleEvent);
队列
简介
lumen的队列服务给不同的后端队列提供统的API。队列允许你延迟处理消失的任务,例如在远程服务器上执行任务直到更晚的时间,而同一时间你的应用程序可以快速的处理web应用程序的请求。
就像该框架的许多其他部分一样,lumen的队列服务跟laravel的队列服务功能相同。因此,如果要了解更多关于lumen的队列,则可以参阅laravel消息队列文档。
配置
队列配置选项对话都在.env文件中。
如果您想要完全自定义的配置,必须您将vendor/laravel/lumen-framework/config/queue.php文件完整的复制到你的项目目录根中config目录,并且要调整必要的配置。如果config目录不存在,则要创建。
驱动程序先决条件
数据库
使用为了database队列驱动程序,您将需要数据库表保存作业和失败情况:
Schema::create('jobs', function (Blueprint $table) {
$table->bigIncrement('id');
$table->string('queue');
$table->longText('payload');
$table->tinyInteger('attempts')->unsigned();
$table->unsignedInteger('reserved_at')->nullable();
$table->unsignedInteger('available_at');
$table->unsignedInteger('created_at');
$table->index(['queue', 'reserved_at']);
});
Schema::create('failed_jobs', function (Blueprint $table) {
$table->increment('id');
$table->text('connection');
$table->text('queue');
$table->longText('payload');
$table->longText('exception');
$table->timestamp('failed_at')->useCurrent();
});
Redis的
要想使用Redis队列驱动程序,需要先通过composer安装illuminate/redis(5.5.*)扩展包。然后再bootstrap/app.php文件中注册Illuminate\Redis\RedisServiceProvider;
其他队列驱动程序的依赖包
下面列出其他队列驱动程序所需要的依赖扩展包:
亚马逊SQS:aws/aws-sdk-php ~3.0
Beanstalked:pda/pheanstalk ~3.0
与laravel的差异
与框架的许多其他部分一样,lumen队列作业的功能与laravel的队列作业功能相同。因此,要了解lumen队列作业功能,请查看完整的laravel队列文档。
不过呢,我们现在讲讨论两个框架间的一些细微差异。首先,我们来谈谈lumen中如何生成队列作业。
生成器
lumen不包括用于自动创建新job类的生成器。因此你需要赋值框架所带的exampleJob类。这个类提供了每个job类共享的基本结构。examplejob所继承的job基类已包含所需的interactswithqueue,queueable和serializesmodels特性:
namespace App\Jobs;
class ExampleJob extendx Job
{
/**
- 创建一个新的作业实例。
- @return void
*/
public function __construct()
{
//
}
/**
- 执行作业。
- @return void
*/
public function handle()
{
//
}
}
调度作业
再次重申,你应该查阅完整的laravel文档以获取有关调节队列作业的完整信息;和laravel框架一样,你可以使用dispatch函数从lumen应用程序中的任意位置调度作业:
dispatch(new ExampleJob);
当然,你也可以使用Queue facade。如果你选择使用facade,请务必在bootstrap/app.php文件中取消对$app->withFacades()调用的注释:
Queue::push(new ExampleJob);
服务容器:
简介
laravel的服务容器是一个管理类依赖和执行依赖注入的强力工具。依赖注入是个花俏的名字,事实上是指:类的依赖通过构造器或者在某些情况下通过【setter】方法【注入】。
与laravel的差异
lumen使用了与laravel框架相同的服务器。所以,你可以使用它们所有强大的功能。有关容易的完整文档,请阅读laravel容器文档。
获取服务容器
laravel\lumen\application实例是Illuminate\Container\Container的扩展,所以你可以当做服务容器来使用。
通常我们会在服务提供者注册我们的容器解析规则。当然,你可以使用bind、singleton’instance、以及容器提供的其他方法。请记住,所有这些方法都记录在laravel服务器容易文档中。
解析实例
想要从服务容器中解析实例,你可以在大部分的功能类里自动解析(依赖注入),如路由closure,控制器的构造方法、控制器方法、中间件、事件监听器,或者队列等。或者,你也可以在应用程序中的任何地方使用App函数来进行解析:
$instance = app(Something::class);
服务提供者
简介
服务提供者是所有lumen应用程序启动的中心所在。包括你自己的应用程序,以及所有的核心服务,都是服务提供者启动的。
但是,我们所说的启动值得是什么?一般而言,我们指的是注册事物,包括注册服务容易绑定、事件侦听器、中间件,甚至路由、服务提供者设置你的应用程序的中心所在。
若你打开lumen的bootstrap/app.php文件,你将会看到$app->register()方法的调用。你也许需要额外的调用来注册你的服务提供者。
编写服务提供者
所有的服务提供者都集成了illuminate\Support\ServiceProvider这个类。这个抽象类要求你在你的提供者上定义至少一个方法:register。在register方法内,你应该只需要将事物绑定到服务容器中。永远不要试图在register方法中注册任何事件侦听器,路由或者任何其他功能。
注册方法
如前面所讲,在register方法中,你只要将事物绑定到【服务容器中】。永远不要试图在register方法中注册任何事件侦听器、路由或者任何其它功能。否则,你有可能会以外的使用到尚未加载的服务提供者提供的服务。
现在,让我们来看一个基本的服务提供者代码:
app->singleton(Connection::class, function ($app) {
return new Connection(config('riak'));
});
}
}
这个服务提供者自定义了一个register方法,并且用这个方法在服务容器中绑定了Riak\Connection的一个实例。如果你不是很了解服务容器的运行原理,请查看[its documentation]。
启动方法
那么,如果我们要在服务提供者当中注册一个视图组件呢?这应该在boot方法内完成。此方法在所有其他服务提供者都注册之后才能调用,也就意味着可以访问已经被框架注册的所有服务:
注册服务提供者
所有的服务提供者在bootstrap/app.php文件中被注册。这个文件中包含对$app->register()方法调用。你也行需要额外的调用$app->register()来注册你的服务提供者。
测试
简介
lumen在创建时就已考虑到测试部分。事实上,lumen默认就支持用PHPunit来做测试,并为你的应用程序创建好了phpunit.xml文件。框架还提供了一些便利的辅助函数,让你可以更直观的测试应用程序的json响应。
测试环境
在运行测试时,lumen自动配置讲缓存驱动配置为array,意味着在测试的时候不会保存任何的缓存数据。
你可以随意创建其他必要的测试配置环境。testing的环境变量可以在phpunit.xml文件中进行修改;
定义和运行测试
要创建一个测试用例,直接将新的测试文件创建到tests文件夹下即可。测试文件必须继承TestCase。接着就可以像平常使用PHPunit一样来定义测试方法。要运行测试只需要在命令行上运行PHPunit命令即可:
assertTrue(true);
}
}
注意:如果要在你的类自定义setUp方法,请确保调用了parent::setUp。
应用测试
lumen提供了一个非常好用的api,使用它用来向你的应用发起HTTP请求,并查看输出结果。
测试json api接口
lumen同样提供了几个测试用于测试json api接口和响应数据的助手。例如,get,post,put,patch和delete方法可以被用于发起各种HTTP请求方式,并且声明以json格式返回一个指定的数组:
json('POST', '/user', ['name' => 'Sally']);
->seeJson([
'created' => true,
]);
}
}
seeJson方法数组转换为json,并验证这个json片段发生在应用返回的整个json响应的任意位置。所以,即使在json响应中存在其他属性,只要指定的片段存在,这个测试任然会成功!
验证完全匹配的json
如果你想验证传入的数组是否与应用程序返回的json完全匹配,你可以用seeJsonEquals方法:
class ExampleTest extendx TestCase
{
/**
*一个简单的测试例子
*
*@return void
*/
public function testBasicExample()
{
$this->post('/user', ['name' => 'Sally'])
->seeJsonEquals([
'created' => true,
]);
}
}
认证
actingAs辅助函数提供了简单的方式来让指定的用户认证为当前的用户:
class ExampeTest exntends TestCase
{
public function testApplication()
{
$user = factory('App\User')->create();
$this->actingAs($user)
->get('/user');
}
}
自定义HTTP请求
如果你想要创建一个自定义的HTTP请求到应用程序上,并获取完整的 Illuminate\Http\Response对象,可以使用call方法:
public function testApplication()
{
$response = $this->call('GET', '/');
$this->assertEquals(200, $response->status());
}
如果你想构造POST,PUT,或者PATCH请求,可以在请求时传入一个数组作为请求参数,当然,你也可以在路由及控制器中通过请求实例来获取传过来的参数:
$response = $this->call('POST', '/user', ['name' => 'Taylor']);
使用数据库
为了使得测试使用了数据库的应用更加简便,lumen提供了各种有用的工具。首先,你可以使用seeInDatabase助手函数来断言数据库中是否存在给定条件的数据。例如,我们想要验证users表中的有一条email的值为[email protected]的记录,我们可以按如下操作:
public function testDatabse()
{
//Make call to application...
$this->seeInDatabase('users', ['email' => '[email protected]']);
}
当然,seeInDatabase方法和类似的助手方法就是会为了方便使用。你也可以在测试中自由使用PHPunit的内置断言方法。
每次测试之后重置数据库
在每次测试后重置数据库时非常有必要的,这样之前的测试数据不会响应后面的测试。
使用迁移
有一个选择是每次测试之后回滚数据库,并且在下一次测试之前将其迁移。lumen提供了一个简单的DatabaseMigrations特性,它可以自动位您处理。简单的在您的测试类中运用这个特性如下:
get('/foo');
}
}
使用事务
另一个选择是将每一个测试用例包装在数据库事务中。撸们提供了便利的DatabaseTransactions特性,可以为您自动的执行这些:
use Laravel\Lumen\Testing\DatabaseMigrations;
use Laravel\Lumen\Testing\DatabaseTransactions;
class ExampleTest extends TestCase
{
use DatabaseTransactions;
/**
A basic functional test example
/
}
模型工厂
在测试时,在执行测试之前我们需要将少量的数据插入到数据库中很常见的。当创建这些数据的时候,lumen不会手动指定这些列的值。而是允许您使用“factories”为每个Eloquent models定义一组默认属性。首先,在您的应用中看看database/factories/ModelFactory.php文件。这个文件包含一个工厂定义:
$factory->define('App\User', function ($faker) {
return [
'name' => $faker->name,
'email' => $faker->email,
];
});
在作为工厂定义的闭包中,您可以返回模型上所有属性的默认测试值。闭包将接收Faker PHP库的一个实例,它将允许您便利的生成各种随机数据以方便测试。
当然,您可以将您自己的额外的工厂添加到ModelFactory.php文件中。
多种工厂类型
有时您可能希望同一个Eloquent模型有多种工厂。例如,可能您会希望除了普通的用户之外还有管理员用户的工厂。您可能会使用defineAs方法定义这些工厂:
$factory->defineAs('App\User', 'admin', function ($faker) {
return [
'name' => $faker->name,
'email' => $faker->email,
'admin' => true,
];
});
您可以使用raw方法检索其基本属性,而不是复制基本用户工厂中的所有属性。一旦拥有这些属性,只需要使用您需要的任何附加值补充它们:
$factory->defineAs('App\Users', 'admin', function ($faker) use ($factory) {
$user = $factory->raw('App\User');
return array_merge($user, ['admin' => true]);
});
在测试中使用工厂
在工厂定以后,就可以在测试或者是数据库的填充文件中,通过全局的factory函数来生成模型实例。接着让我们先来看看几个创建模型的例子。首先我们会使用make方法创建模型,但不将他们保存至数据库:
public function testDatabase()
{
$user = factory('App\User')->make();
}
如果你想要写模型中的某些默认值,则可以传递一个包含数值的数组至make方法。只有指定的数值会被替换,其他剩余的数值则会安装工程指定的默认值来设置:
$user = factory('App\User')->make([
'name' => 'Abigail',]);
你还可以创建许多模型的集合或者创建给定类型的模型:
//Create three App\User isntance...
$users = factory('App\User', 3)->make();
//Create an App\User "admin" instance...
$user = factory('App\User', 'admin')->make();
//Create three App\User "admin" instances...
$users = factory("App\User", 'admin', 3)->make();
维持工厂模式
可以使用create方法创建模型实例,还可以使用save方法将数据保存到数据库:
public function testDatabase()
{
$user = factory("App\User")->create();
// Use model in tests...
}
同样,你也可以使用数组的方式使用create方法将数据写入模型
$user = factory('App\User')->create([
'name' => 'abigail',
]);
添加关联至模型
你甚至可以保存多个模型到数据库上。在本例中,我们还会增加关联至我们所创建的模型。当使用create方法创建多个模型时,它会返回一个Eloquent集合实例,让你能使用集合提供的便利方法,例如each方法:
$users = factory('App\User', 3)
->create()
->each(function ($u) {
$u->posts()->save(factory('App\Posts')->make);
});
模拟
模拟事件
如果你大量地使用lumen的事件系统,你可能会希望在测试停止或者模拟某些事件。例如,如果你在测试你的注册功能,你可能不希望所有的UserRegistered事件被触发,因为它们会触发“欢迎”邮件的发送。
lumen提供了简单的expectsEvents方法,以验证预期的事件有没有被运行,可防止该事件的任何处理进程被进行:
class ExampleTest extends TestCase
{
public function testUserRegistration()
{
$this->expectsEvents('App\Events\UserRegistered');
//测试用户注册功能...
}
}
如果你想阻止所有的事件处理程序运行,你可以使用withoutEvents方法:
withoutEvents();
//Test user registraction code。。。
}
}
模拟任务
有时你可能希望当请求发送至应用程序时,简单地对控制器所派送的任务进行测试。这么做能够让你隔离测试路由或者控制器,设置除了任务以外的逻辑。当然,在此之后你也可以在一个单独的测试案例中测试该任务。
lumen提供了一个简便的expectsJob方法,以验证预期的任务有没有被派送,但任务本身不会被运行:
expectsJobs('App\Jobs\PurchasePodcast');
//测试购买博客代码...
}
}
注意:该方法只检测通过全局猪手函数dispatch或者路由或控制器中的$this->dispatch方法派送的任务。它并不会检测被直接发送到Queue::push的任务。
模拟facades
在测试时,经常需要模拟对lumen facade的调用。例如,考虑如下控制器的操作:
namespace App\Http\Controllers;
use Cache;
class UserController extends Controller
{
/**
* 展示应用程序所有用户的列表。
@return Response
*/
public function index()
{
$value = Cache::get('key');
//
}
}
我们可以使用shouldReceive方法来模拟调用Cache门面它会返回一个mockery模拟的实例。因为门面实际上已经被lumen的服务容器,解析和管理,它们比一般的静态类更具有可测性。例如,让我们模拟调用cache门面:
class FooTest extends TestCase
{
public function testGetIndex()
{
Cache::shouldReceive('get')
->once()
->with('key')
->andReturn('value');
$this->get('/users');
}
}
注意:你不应该模拟Request门面。应该在测试时使用如call及post这样的HTTP辅助函数来传递你想要的数据。
数据验证
简介
lumen提供了数种不同的方法来验证传入应用程序的数据。默认情况下,lumen的基本控制器类使用名为ProvidesConvenienceMethods的trait,其提供了一种便捷的方法来使用各种强大的验证规则验证传入的HTTP请求。
一般来说,lumen中的数据验证与laravel中的数据验证并无多大区别,因此你应该查过完整的laravel数据验证文档以熟悉其使用;不过,它们之间也存在少许重要的差异。
与laravel的差异
表单请求
lumen不支持表单请求。如果想使用表单请求,则赢使用完整的laravel框架。
$this->validate方法
在lumen中可用的$this->validate辅助方法将始终返回带有相关错误消息的json响应。而该方法的laravel版本,如果请求不是ajax请求,返回的则是重定向响应。由于lumen是无状态的,且不支持会话,所以闪存错误信息在会话中是不可能的。如果想要使用重定向及闪存错误数据,应该使用完整的laravel框架。
与laravel不同的是,lumen支持在Route闭包中访问validate方法:
use Illuminate\Http\Request;
$router->post('/user', function (Request $request) {
$this->validate($request, [
'name' => 'required',
'email' => 'required|email|unique:users',
]);
//存储用户
});
当然,你可以自由地使用Validator::make facade方法手动创建验证器实例,就像在laravel中一样。
exists和unique规则
如果想要使用exists或者unique验证规则,则应该在bootstrap/app.php文件中取消$app->withEloquent()方法调用的注释。
视图变量$errors
lumen不支持session,因此在laravel中每个视图都可用的$errors视图变量在lumen中式不可用的。如果验证失败,那么$this->validate辅助方法会抛出Illuminate\ValidationException异常,期中嵌入了包含所有相关错误消息的json响应,如果你并非只构建仅发送json响应的无状态api,则应使用完整的laravel框架。tongzhuo_examination_db