当PHP发生意想不到的状况或者错误的时候就会抛出异常。经验告诉我们,异常不应该用于控制应用程序逻辑,例如 if-statements
,应该是异常类的子类。
我们知道,异常可以在我们的应用程序的任何时间点被抛出。Laravel 提供了一个方便的异常处理程序类,它将检查在 laravel 应用程序中抛出的所有异常并给出相关的响应。Laravel 中使用的所有异常都扩展了异常类,使所有异常由单个类捕获的一个主要优点,我们能够创建自定义异常处理程序,根据异常返回不同的响应消息。在本教程中,我们将介绍如何在 Laravel 5.2 中创建自定义异常处理程序,以及如何根据异常返回404页面。
如何工作
在Laravel 5.2中,所有的异常和错误不管是自定义的还是默认的都是由 app/Exceptions/Handler.php
中的 handle
类处理的,它有两个方法:
report()
这个方法可以记录引发的异常或者将它们解析到错误日志引擎(如 bugsnag
或者 sentry
),这里不再讨论,感兴趣的自行研究。
render()
这个方法将异常引发的错误消息生成 HTTP 响应,并将其发送给浏览器。
/**
* Render an exception into an HTTP response.
*
* @param \Illuminate\Http\Request $request
* @param \Exception $e
* @return \Illuminate\Http\Response
*/
public function render($request, Exception $e)
{
return parent::render($request, $e);
}
我们可以用自己自定义的异常处理程序覆盖默认的错误处理。
/**
* @param \Illuminate\Http\Request $request
* @param \Exception $e
* @return \Illuminate\Http\Response
*/
public function render($request, Exception $e)
{
if ($e instanceof CustomException) {
return response()->view('errors.custom', [], 500);
}
return parent::render($request, $e);
}
Laravel 自己会处理处理检查以确定异常的最佳响应。看看父类 Illuminate\Foundation\Exceptions\Handler
, render()
根据抛出的异常生成不同的响应。
/**
* Render an exception into a response.
*
* @param \Illuminate\Http\Request $request
* @param \Exception $e
* @return \Symfony\Component\HttpFoundation\Response
*/
public function render($request, Exception $e)
{
if ($e instanceof HttpResponseException) {
return $e->getResponse();
} elseif ($e instanceof ModelNotFoundException) {
$e = new NotFoundHttpException($e->getMessage(), $e);
} elseif ($e instanceof AuthenticationException) {
return $this->unauthenticated($request, $e);
} elseif ($e instanceof AuthorizationException) {
$e = new HttpException(403, $e->getMessage());
} elseif ($e instanceof ValidationException && $e->getResponse()) {
return $e->getResponse();
}
if ($this->isHttpException($e)) {
return $this->toIlluminateResponse($this->renderHttpException($e), $e);
} else {
return $this->toIlluminateResponse($this->convertExceptionToResponse($e), $e);
}
}
抛出一个 Laravel Eloquent 异常
接下来我们故意创建一个内置的错误来引发一个异常。因此使用 findOrFail()
方法从模型中提取不存在的记录。
使用 laravel 自带的 User 模型,执行迁移命令创建数据表
添加一个路由和控制器 app/Http/routes.php
Route::get('/user', [
'uses' => 'SampleController@findUser',
'as' => 'user'
]);
App/Http/Controllers/SampleController.php
/**
* Return the first user in the users table
*
* @return Array User details
*/
public function findUser()
{
$user = User::firstOrFail();
return $user->toArray();
}
浏览器访问,一个 ModelNotFoundException
的异常
捕获自定义异常
MODELNOTFOUNDEXCEPTION
有了这个异常,我们现在可以添加一个自定义处理程序,返回我们自己的错误消息。
如果异常是 ModelNotFoundException
或 NotFoundHttpException
之一,我们将修改 app/Exceptions/Handler.php
中的 render
方法,以返回 ajax 请求的 json 响应或正常请求的视图。如果两者都不是,就让 laravel 自己处理异常
/**
* Render an exception into an HTTP response.
*
* @param \Illuminate\Http\Request $request
* @param \Exception $e
* @return \Illuminate\Http\Response
*/
public function render($request, Exception $e)
{
//check if exception is an instance of ModelNotFoundException.
if ($e instanceof ModelNotFoundException) {
// ajax 404 json feedback
if ($request->ajax()) {
return response()->json(['error' => 'Not Found'], 404);
}
// normal 404 view page feedback
return response()->view('errors.missing', [], 404);
}
return parent::render($request, $e);
}
在 resources/views/errors
中添加一个 404.blade.php
文件包含反馈信息。
<!DOCTYPE html>
<html>
<head>
<title>User not found.</title>
</head>
<body>
<p>You broke the balance of the internet</p>
</body>
</html>
刷新页面我们可以看到该错误状态消息
NOTFOUNDHTTPEXCEPTION
当用户访问未定义的 URL(例如/foo/bar/randomstring),会抛出 NotFoundHttpException
异常。要处理这个异常, 我们将在我们之前修改的 render
方法中添加第二个条件,并返回消息到 resources/view/errors/missing.blade.php
视图中。
/**
* Render an exception into an HTTP response.
*
* @param \Illuminate\Http\Request $request
* @param \Exception $e
* @return \Illuminate\Http\Response
*/
public function render($request, Exception $e)
{
//check if exception is an instance of ModelNotFoundException.
//or NotFoundHttpException
if ($e instanceof ModelNotFoundException or $e instanceof NotFoundHttpException) {
// ajax 404 json feedback
if ($request->ajax()) {
return response()->json(['error' => 'Not Found'], 404);
}
// normal 404 view page feedback
return response()->view('errors.missing', [], 404);
}
return parent::render($request, $e);
}
/**
*
* 对于HttpException 这样写,统一性更好
*/
if($this->isHttpException($e))
{
if (view()->exists('errors.'.$e->getStatusCode()))
{
return response()->view('errors.'.$e->getStatusCode(), [], $e->getStatusCode());
}
}
return parent::render($request, $e);
使用 Laravel Abort()
方法
调用 abort()
生成404错误页面,该方法接收可选的响应消息
abort(404, 'The resource you are looking for could not be found');
它将检查相应的 resources/view/errors/404.blade.php
页面,并将404状态码的 HTTP 响应提供给浏览器,这同样适用于401和500错误状态码。