1.简单异常抛出
<?php
namespace app\api\model;
use think\Exception;
class Banner
{
public static function getBannerByID($id){
//TODO 根据Banner ID号 获取Banner信息
try{
1 / 0;
}
catch (Exception $ex)
{
//TODO;可以记录日志,正式项目将异常抛给客户端意义不大,最终还是需要服务器端做分析处理
throw $ex;
}
return "This is banner test";
}
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<?php
namespace app\api\controller\v1;
use think\Exception;
//use think\Validate;
//use app\api\validate\BaseValidate;
//use app\api\validate\TestValidate;
use app\api\validate\IdIsInt;
use app\api\model\Banner as BannerModel;
class Banner {
/**
* 获取指定id的banner信息
* @url /banner/:id
* @http GET
* @id banner的id的号
*
*/
public function getBanner($id)
{
//正常情况下,3句话搞定
(new IdIsInt())->goCheck();
//直白的异常处理方式,但是代码不能复用,不能完美的服务大型项目
try
{
$banner = BannerModel::getBannerByID($id);
}catch (Exception $ex)
{
$err = [
'error_code' => 10001,
'msg' => $ex->getMessage()
];
return json($err,400);
//tp5需要使用json()转为json格式再返回
//Postman 出现200状态,不是指成功的响应,而是成功的拿到了你想要的结果
}
}
2.异常分类
由用户行为导致 (通常没用通过验证器,没有查询到结果) | 服务器自身异常 (代码错误,调用外部接口错误) |
---|---|
通常不需要记录日志 | 通常记录日志 |
需要向用户返回具体信息 | 不向用户返回具体信息 |
3.自定义异常处理
<?php
/**
* Created by PhpStorm.
* User: M_jh
* Date: 2019/1/6
* Time: 21:39
*/
namespace app\api\lib\exception;
use Exception;
use think\exception\Handle;
class ExceptionHandler extends Handle
{
public function render(Exception $e)
{
return json('~~~~~~~~~~~~~~~~~~~~~~~~~~~');
// return parent::render($e); // TODO: Change the autogenerated stub
}
}
测试已通,需要注意的是自定义异常处理时候需要修改配置项exception_handle为自定义异常处理类路径。
<?php
/**
* Created by PhpStorm.
* User: M_jh
* Date: 2019/1/6
* Time: 21:29
*/
namespace app\api\lib\exception;
class BannerMissException extends BaseException
{
public $code = 404;
public $msg = 'banner未找见';
public $errorCode = 40000;
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<?php
/**
* Created by PhpStorm.
* User: M_jh
* Date: 2019/1/6
* Time: 21:39
*/
namespace app\api\lib\exception;
use Exception;
use think\exception\Handle;
use think\Request;
class ExceptionHandler extends Handle
{
private $code;
private $msg;
private $errorCode;
//需要返回客户端当前的请求URL路径
public function render(Exception $e)
{
// return json('~~~~~~~~~~~~~~~~~~~~~~~~~~~');
// return parent::render($e); // TODO: Change the autogenerated stub
if ($e instanceof BaseException){
//instanceof:判断其左边对象是否为其右边类的实例
//判断是否自定义异常
$this->code = $e->code;
$this->msg = $e->msg;
$this->errorCode = $e->errorCode;
}
else{
$this->code = 500;
$this->$msg = '服务器内部错误,不想你看到具体信息';
$this->$errorCode = 999;
}
$request = Request::instance();
$result = [
'msg' => $this->msg,
'errorCode' => $this->errorCode,
'request_url' => $request->url()
];
return json($result, $this->code);
}
}
自定义异常处理和自定义验证器类似。
这里需要注意,仔细仔细再仔细!
4.日志
重定义日志目录
define('LOG_PATH', __DIR__ . '/../log/');
<?php
/**
* Created by PhpStorm.
* User: M_jh
* Date: 2019/1/6
* Time: 21:39
*/
namespace app\api\lib\exception;
use Exception;
use think\exception\Handle;
use think\Log;
use think\Request;
class ExceptionHandler extends Handle
{
private $code;
private $msg;
private $errorCode;
//需要返回客户端当前的请求URL路径
public function render(Exception $e)
{
// return json('~~~~~~~~~~~~~~~~~~~~~~~~~~~');
// return parent::render($e); // TODO: Change the autogenerated stub
if ($e instanceof BaseException){
//instanceof:判断其左边对象是否为其右边类的实例
//判断是否自定义异常
$this->code = $e->code;
$this->msg = $e->msg;
$this->errorCode = $e->errorCode;
}
else{
$this->code = 500;
$this->msg = '服务器内部错误,不想你看到具体信息';
$this->errorCode = 999;
$this->recordErrorLog($e);
}
$request = Request::instance();
$result = [
'msg' => $this->msg,
'errorCode' => $this->errorCode,
'request_url' => $request->url()
];
return json($result, $this->code);
}
private function recordErrorLog(Exception $e){
//需要重新开启日志
Log::init([
'type' => 'File',
'path' => LOG_PATH,
'level' => ['error']
]);
Log::record($e->getMessage(), 'error');
}
}
5.全局异常处理应用
//当程序或者bug遇到不方便时候,我们需要做一些特殊的处理,可提高开发效率
//自定义异常加判断无意义
// Config::get(app_debug);读取配置文件变量,配置文件不建议做存储
if (config('app_debug')){
return parent::render($e);
}else{
$this->code = 500;
$this->msg = '服务器内部错误,不想你看到具体信息';
$this->errorCode = 999;
$this->recordErrorLog($e);
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<?php
/**
* Created by PhpStorm.
* User: M_jh
* Date: 2019/1/8
* Time: 19:31
*/
namespace app\api\lib\exception;
class ParamterException extends BannerMissException
{
public $code = 400;
public $msg = '参数错误';
public $errorCode = 10000;
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<?php
namespace app\api\validate;
use app\api\lib\exception\ParamterException;
use think\Validate;
use think\Request;
use think\Exception;
class BaseValidate extends Validate
{
public function goCheck(){
$req = Request::instance()->param();
$result = $this->Check($req);
if (!$result) {
//抛出参数错误异常
$e = new ParamterException('');
throw $e;
// $err = $this->error;
// throw new Exception($err);//tp5默认异常
}else{
return true;
}
}
}
借助构造函数
//构造函数
public function __construct($params = [])
{
//先判断参数是否为数组
if (!is_array($params)){
return;//默认通过不处理
// throw new Exception('参数必须为数组');
}
if (array_key_exists('code', $params)){
$this->code = $params['code'];
}
if (array_key_exists('msg', $params)){
$this->msg = $params['msg'];
}
if (array_key_exists('errorCode', $params)){
$this->errorCode = $params['errorCode'];
}
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
public function goCheck(){
$params = Request::instance()->param();
$result = $this->Check($params);
if (!$result) {
//抛出参数错误异常
$e = new ParamterException([
'msg' => $this->error,
'errorCode' => 44444
]);
// $e->msg = $this->error;
// $e->errorCode = 44444;
throw $e;
// $err = $this->error;
// throw new Exception($err);//tp5默认异常
}else{
return true;
}
}
完善重构
批量验证
class IdIsInt extends BaseValidate
{
protected $rule = [
'name' => 'require|max:16',
'email' => 'require|email',
'id' => 'require|isInteger'
];
protected function isInteger($value,$rule = '',$data = '',$field = '')
{
if (is_numeric($value) && is_int($value + 0) && ($value + 0) > 0) {
return true;
}
else{
return $field.'必须是正整数';
}
}
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
(new IdIsInt())->batch()->goCheck();
总结:
- 构建参数校验层和全局异常处理层非常重要。
- 编程思路很重要。
- AOP 面向切面编程(本章笔记就是一个典型的应用,可以找找感觉),和面向对象不搭边。