针对PHP框架的Exception和Validate的处理

312 阅读2分钟

1. Exception处理缘由

想必总有过这种情况,你的前端小伙有时收到一个框架自带的报错,一脸懵逼,然后找你,XXXX... 好吧,我们 PHP 虽然开发敏捷,但也要严谨。...于是乎,查询,嗯,得判空;保存,得判断返回的结果。 哇,发现一个方法里要判断好多好多次呀,类似下面这种伪代码:(举个例子,实际中可能不多)

//以下代码写控制器里了

//我要添加一个商品,得先查看该分类是否存在吧
$category = Category::find($category_id);

if (! $category) {
    // 返回自定义错误json给前端...
}

//
$product = Product::create(['name' => 'iphone', ...]);
if (! $product) {
    // 返回自定义错误json给前端...
}

if (isset($product['...'])) {
    // 返回自定义错误json给前端
}
.
.
.
// 对这个才是我们想要的结果

以上放在控制器里本身并无大碍,但是控制器里,只该呈现大体逻辑代码,查询数据库应写在Model不是么? 是的
Fuck 于是我想了一下该咋办呢 于是有了后面的代码

// 模型里的代码
public static function handleProduct()
{
    //... 省略
    if (! $category) {
    return 1;
    }
    
    if (! $production) {
        return 2;
    }
    
    if (! $others) {
        return 3;
    }
// .. 后面可能还有
}

...

// 控制器里的代码
$result = Product::handleProduct(..);
switch($result) {
    case 1 : 
    // 处理这种情况的报错
    break;
    case 2:
    // 处理第二种情况的报错
    break;
    case 3: 
    // 处理其他情况的报错
    break
}

我们公司很多地方都有这种为了严谨一点,不得不写出这种恶心的代码......

很长一段时间,突然看到了一种很好很好解决这个问题的办法了... 于是乎,重要关头来了。

2. 处理异常类

以laravel 框架作为实例,TP5还有其他框架都有的~

  • 创建 /app/Exceptions/BaseException.php

namespace App\Exception;

class BaseException extends \Exception
{
    /** 自定义http 状态码
     * @var int 
     */
    public $code = 400;
    
    /** 自定义错误类型码
     * @var int 
     */
    public $errCode = 10000;
    
    /** 自定义错误消息
     * @var string 
     */
    public $msg = '';
    
    public function __construct($config = [])
    {
        if (array_key_exists('code', $config)) {
            $this->code = $config['code']
        }
        
        if (array_key_exists('errCode', $config)) {
            $this->code = $config['errCode']
        }
        
        if (array_key_exists('msg', $config)) {
            $this->code = $config['msg']
        }
    }
}

// 这个类主要用于作为错误基类,主要为了可以在throw new MyException($config),可以自定义错误内容。
  • 打开 /app/Exceptions/Handle.php
// 这里关注在render方法,可以重写覆盖render方法

public function render($request, Exception $exception)
{
    // 首先我们得判断 抛出的Exception 是不是继承我们的基类BaesException
    if ($exception instanceof BaseException) {
        return Response()->json([
            'errCode' => $exception->errCode,
            'msg' => $exception->msg
        ], $exception->code);
    }
    
    // 如果不是我们想要定义的错误 不在生产环境的话,还是返回错误页面
    if (env('APP_DEBUG')) {
        parent::render();
    }
    
    // 生产环境
    return Response()->json([
            'errCode' => 999,
            'msg' => '服务器内部错误'
        ], 501);
    
}

有了这个,我们再写代码就舒服啦~ 如下

// 某个模型
public function ManyErrorMethod()
{
    if (! $category) {
        throw new CategoryNotFoundException();
    }
    
    if (! $product) {
        throw new SaveDataErrorException(['errCode' => 12001, 'msg' => '保存商品失败'])
    }
}

3. 处理验证类

有了以上的异常类,在验证类这一块可以说是非常爽的~

  • 创建 /app/Validates/BaseValidate.php
class BaseValidate 
{
    /** 规则
     * @var int 
     */
    protected $rule = [];
    
    /** 根据规则返回的错误消息
     * @var int 
     */
    protected message = [];
    
    // 以上都是和laravel 自带的 make:request 差不多的。
    
    public functio check($params = [])
    {
        // 如果传了,那么就只验证传过来的参数,否则验证所有参数。
        $params = empty($params) ? request()->all() : $params;
        
        $validate = Validator::make($param, $this->rule);
        
        // 如果验证没通过
        if ($validate->fails()) {
            // 抛出自定义的异常,记得继承上面说的 BaseException
            throw new CustomException([
                'code' => '400',
                'errCode' => $validate->messages()->first()
            ]);
        }
        return;
    }
    
    // 差不多就这样完成了,不过有个问题,就是不同的参数错误,应该对应不同的错误码。
    // 所以,可以更改一下上面 错误类的 render 方法
}
  • 更改 /app/Exception/Handle.php

 public function render($request, Exception $exception)
    {
         // 首先我们得判断 抛出的Exception 是不是继承我们的基类BaesException
    if ($exception instanceof BaseException) {
        // 自定义一个错误类Error  里面有一个错误的属性 
        // public static $errCodes = ['501' => '操作数据错误']
        // 然后还有个 根据错误码, 获取错误信息的方法 getErrorMsg
        $msg = Error::getErrorMsg($exception->errCode);
        return response()->json([
            'code' => $exception->code,
            'errCode' => $errCode,
            'msg' => $msg
        ]);
    }
    
    // 如果不是我们想要定义的错误 不在生产环境的话,还是返回错误页面
    if (env('APP_DEBUG')) {
        parent::render();
    }
    
    // 生产环境
    return Response()->json([
            'errCode' => 999,
            'msg' => '服务器内部错误'
        ], 501);
    
    }

  • 最后创建Validate类继承BaseValidate
// createDeviceValidate.php

class CreateDeviceValidate extends BaseValidate
{
    // 还是填以前的规则
    protected $rule = [
        'gateway_sn' => 'required|exists:gateways,sn',
        'sn' => 'required|unique:devices,sn',
        'type' => 'required|positiveIntger',
        'mac' => 'required'
    ];

    // 信息填错误码就好了~
    protected $message = [
        'gateway_sn.required' => '213001',
        'gateway_sn.exists' => '213005',
        'sn.required' => '214001',
        'sn.unique' => '214002',
        'type.required' => '214005',
        'type.positive_intger' => '214006',
        'mac.required' => '214007'
    ];
}

  • 最后控制器的写法
public function create(CreateDeviceValidate $createDeviceValidate)
    {
        // 就这样check咯 简单
        $createDeviceValidate->check();
    }


结束 洒花