當(dāng)PHP發(fā)生意想不到的狀況或者錯(cuò)誤的時(shí)候就會拋出異常。經(jīng)驗(yàn)告訴我們,異常不應(yīng)該用于控制應(yīng)用程序邏輯,例如 if-statements,應(yīng)該是異常類的子類。
我們知道,異??梢栽谖覀兊膽?yīng)用程序的任何時(shí)間點(diǎn)被拋出。Laravel 提供了一個(gè)方便的異常處理程序類,它將檢查在 laravel 應(yīng)用程序中拋出的所有異常并給出相關(guān)的響應(yīng)。Laravel 中使用的所有異常都擴(kuò)展了異常類,使所有異常由單個(gè)類捕獲的一個(gè)主要優(yōu)點(diǎn),我們能夠創(chuàng)建自定義異常處理程序,根據(jù)異常返回不同的響應(yīng)消息。在本教程中,我們將介紹如何在 Laravel 5.2 中創(chuàng)建自定義異常處理程序,以及如何根據(jù)異常返回404頁面。
如何工作
在Laravel 5.2中,所有的異常和錯(cuò)誤不管是自定義的還是默認(rèn)的都是由 app/Exceptions/Handler.php 中的 handle 類處理的,它有兩個(gè)方法:
report()
這個(gè)方法可以記錄引發(fā)的異?;蛘邔⑺鼈兘馕龅藉e(cuò)誤日志引擎(如 bugsnag 或者 sentry),這里不再討論,感興趣的自行研究。
render()
這個(gè)方法將異常引發(fā)的錯(cuò)誤消息生成 HTTP 響應(yīng),并將其發(fā)送給瀏覽器。
/**
* 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);
}
我們可以用自己自定義的異常處理程序覆蓋默認(rèn)的錯(cuò)誤處理。
/**
* @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 自己會處理處理檢查以確定異常的最佳響應(yīng)??纯锤割?Illuminate\Foundation\Exceptions\Handler, render()根據(jù)拋出的異常生成不同的響應(yīng)。
/**
* 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);
}
}
拋出一個(gè) Laravel Eloquent 異常
接下來我們故意創(chuàng)建一個(gè)內(nèi)置的錯(cuò)誤來引發(fā)一個(gè)異常。因此使用 findOrFail() 方法從模型中提取不存在的記錄。
使用 laravel 自帶的 User 模型,執(zhí)行遷移命令創(chuàng)建數(shù)據(jù)表
添加一個(gè)路由和控制器 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();
}
瀏覽器訪問,一個(gè) ModelNotFoundException 的異常

捕獲自定義異常
MODELNOTFOUNDEXCEPTION
有了這個(gè)異常,我們現(xiàn)在可以添加一個(gè)自定義處理程序,返回我們自己的錯(cuò)誤消息。
如果異常是 ModelNotFoundException 或 NotFoundHttpException 之一,我們將修改 app/Exceptions/Handler.php中的 render 方法,以返回 ajax 請求的 json 響應(yīng)或正常請求的視圖。如果兩者都不是,就讓 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 中添加一個(gè) 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>
刷新頁面我們可以看到該錯(cuò)誤狀態(tài)消息

NOTFOUNDHTTPEXCEPTION
當(dāng)用戶訪問未定義的 URL(例如/foo/bar/randomstring),會拋出 NotFoundHttpException 異常。要處理這個(gè)異常, 我們將在我們之前修改的 render 方法中添加第二個(gè)條件,并返回消息到 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 這樣寫,統(tǒng)一性更好
*/
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()方法
調(diào)用 abort() 生成404錯(cuò)誤頁面,該方法接收可選的響應(yīng)消息
abort(404, 'The resource you are looking for could not be found');
它將檢查相應(yīng)的 resources/view/errors/404.blade.php 頁面,并將404狀態(tài)碼的 HTTP 響應(yīng)提供給瀏覽器,這同樣適用于401和500錯(cuò)誤狀態(tài)碼。