什么是异常?
就是超出正常流程的部分叫做异常。
举几个例子:
这个虽然是Error,但如果你看一下源码就知道这个是
Symfony\Component\Debug\Exception\FatalThrowableError
的实例。他就是个异常
这个是firstOrFail或者findOrFail触发的异常。
特别是在开发的时候,经常出现这中页面,大家真的思考过为什么代码出现了问题就会自动出现这种页面?Laravel是怎么处理的?
下面以firstOrFail触发异常来说说这个页面是怎么出来的。
/**
* Execute the query and get the first result or throw an exception.
*
* @param array $columns
* @return \Illuminate\Database\Eloquent\Model|static
*
* @throws \Illuminate\Database\Eloquent\ModelNotFoundException
*/
public function firstOrFail($columns = ['*'])
{
if (! is_null($model = $this->first($columns))) {
return $model;
}
throw (new ModelNotFoundException)->setModel(get_class($this->model));
}
这个代码是Laravel源码中firstOrFail的处理代码,看起来很简单,没有查到模型就抛出一个ModelNotFoundException异常。
能抛出,那必然是有这个异常,咱们去看看。
/**
* Set the affected Eloquent model and instance ids.
*
* @param string $model
* @param int|array $ids
* @return $this
*/
public function setModel($model, $ids = [])
{
$this->model = $model;
$this->ids = array_wrap($ids);
$this->message = "No query results for model [{$model}]";
if (count($this->ids) > 0) {
$this->message .= ' '.implode(', ', $this->ids);
} else {
$this->message .= '.';
}
return $this;
}
这个是不是就很明显了,这里是ModelNotFoundException
异常类中的方法。其中的message就是上图的No query results for model [App/user]
。
那为什么抛出一个异常,就会有这个页面呢?
在Exception/Handler.php
中的render
方法中会捕获所有的异常,所以这个抛出的异常被捕获后进行了处理。
/**
* 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);
}
在继续看这个里面的方法,找出返回页面的地方。
/**
* Create a Symfony response for the given exception.
*
* @param \Exception $e
* @return \Symfony\Component\HttpFoundation\Response
*/
protected function convertExceptionToResponse(Exception $e)
{
$e = FlattenException::create($e);
$handler = new SymfonyExceptionHandler(config('app.debug', false));
return SymfonyResponse::create($handler->getHtml($e), $e->getStatusCode(), $e->getHeaders());
}
终于找到,经过个个方法的筛选(不同异常会不同处理),最终会返回SymfonyResponse
其中第一个参数就是异常的页面。
阶段总结一下
Laravel的异常:创建一个自定义异常->在适合的地方进行抛出->被全局的render方法捕获->判断异常类型进行不同处理。
以上就简单的说了一下Laravel最普通的异常是怎么显示出来的。
接下来说一说怎么用起来。
举个例子:
积分商城系统下单逻辑
1、检测用户是否登陆
2、检测用户是否黑名单
3、检测商品是否有货
4、检测商品该用户是否可以购买
5、检测用户积分是否充足
6、下单
经过这么多步骤才能进行一个完成的逻辑。并且每一个检测点如果不通过,就要对每个点进行特殊的处理。比如未登录就跳转登陆页、商品没有货则跳转到商品库存不足页面等等。。
如果按照正常的逻辑,一个一个判断,然后进行处理,不是不可以。但是这样处理起来controller会特别大。而且代码读起来很困难。
这时候就需要抛出异常了。
现在咱们按照上面Laravel抛出异常的方式去做。
1、创建一个自定义异常
2、在适合的地方进行抛出
3、被全局的render方法捕获
4、判断异常类型进行不同处理。
现在先创建一个 下单异常
<?php
namespace App\Exceptions;
use Exception;
class MakeOrderException extends Exception
{
/**
* 报告这个异常。
*
* @return void
*/
public function report()
{
}
/**
* 将异常渲染至 HTTP 响应值中。
*
* @param \Illuminate\Http\Request
* @return \Illuminate\Http\Response
*/
public function render($request)
{
//这里是对异常的处理
return response()->json(['code' => $this->getCode(), 'message' => $this->getMessage()],400);
}
}
在适合的地方进行抛出
function makeOrder (Request $request) {
//商品无货
if (count($request->get('thing'))) {
throw(new MakeOrderException('商品无货', 1001) );
}
}
被全局的render方法捕获
/**
* 将异常渲染至 HTTP 响应值中。
*
* @param \Illuminate\Http\Request $request
* @param \Exception $exception
* @return \Illuminate\Http\Response
*/
public function render($request, Exception $exception)
{
if ($exception instanceof \App\Exceptions\MakeOrderException) {
return $exception->render($request);
}
return parent::render($request, $exception);
}
在全局render中捕获这个异常,并调用这个异常类中的处理方法。
思考一个问题。为什么抛出异常就好了呢?
因为需求是一直在变的。今天商品无货可能是返回到404页面,明天老板突发奇想就要跳转到首页(举个例子而已)如果再去controller中去修改代码未免显得有些繁琐。
所以单独写异常,并单独处理。出现问题只需要修改该部分代码就可以,不会影响正常的业务流程。
而且在正常用户流程中是很难触发这些问题的。并不属于常规错误,所以写在controller会显得臃肿。
完。