walkor / webman-framework

webman-framework
116 stars 57 forks source link

BusinessException自定义返回Response #59

Closed ichynul closed 2 years ago

ichynul commented 2 years ago

控制器不重用后,控制器的__construct()方法里面的错误无法被中间件捕获到,不怎么方便。 BusinessException不属于程序错误,应该方便用户直接返回一个业务方面的提示信息。 用户也可在不自定义ExceptionHandler的情况下,以继承BusinessException实现其他业务方面的功能。 在程序的任何地方抛出错误,简单粗暴。

    throw new \support\exception\BusinessException('无权限操作', 403);
walkor commented 2 years ago

开发者可以实现自己的handler,决定BusinessException是否当作程序错误,并记录日志。 目前默认handler里没有把 BusinessException 当错程序错误,不记录日志。 如果强行规定死BusinessException的行为感觉不妥。

ichynul commented 2 years ago

那改一下,render方法。默认返回null,需要自定义输出的就继承并重写此方法,不重写的话就跟以前的一样。

class BusinessException extends Exception
{
    public function render(Request $request): ?Response
    {
        return null; //可返回Response或null
        //$code = $this->getCode();
        //if ($request->expectsJson()) {
        //    $json = ['code' => $code ? $code : 500, 'msg' => $this->getMessage()];
        //    return new Response(200, ['Content-Type' => 'application/json'],
        //        \json_encode($json, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
        // }
        //return new Response(200, [], $this->getMessage());
    }
}

开发者既要自己定义Exception,又要自定义ExceptionHandler,看似灵活,其实很麻烦。 目前我是通过中间件根据$response->exception() instanceof来判断并处理返回内容。唯一的不足__construct里面抛出的错误无法在中间件里面捕获。 业务错误,我觉得主要是方便在程序里面任何位置中断执行,在其他fpm框架框架,倒有很多替代方案,可以是exit,die之类的。 它有两个关键点:1、不需要记录日志,2、要给显示给用户提示。解决了这两个问题,真没必要去自定义。

ichynul commented 2 years ago

或者controller_reuse的时候,实列化控制器也trycatch 一下。 不复用控制器的情况下,有些人可能通过继承控制器,在基类__construct里面进行一些检查,然后抛出业务异常。 这个异常仍然在经过中间件返回,就可以在里面捕获了。 https://github.com/walkor/webman-framework/blob/1860a6a4c670b5ac85a1b44d019466f2ea49742e/src/App.php#L260

        $controller_reuse = static::config($plugin, 'app.controller_reuse', true);
        if (\is_array($call) && \is_string($call[0])) {
            if (!$controller_reuse) {
                $call = function ($request, ...$args) use ($call, $plugin) {
                    try {
                        $call[0] = static::container($plugin)->make($call[0]);
                        return $call($request, ...$args);
                    } catch (Throwable $e) {
                        return static::exceptionResponse($e, $request);
                    }
                };
            } else {
                $call[0] = static::container($plugin)->get($call[0]);
            }
        }
walkor commented 2 years ago

开发者既要自己定义Exception,又要自定义ExceptionHandler,看似灵活,其实很麻烦。

开发者什么都不需要做,目前BusinessException默认就是不算程序错误,也不会记录日志,只会给前端提示。

controller_reuse为false的时候,中间件是可以捕捉到__construct的异常的。但是controller_reuse为true的时候,因为控制器实例化时机问题,中间件没办法捕获__construct的异常。

ichynul commented 2 years ago

1.在不自定义ExceptionHandler的情况下,BusinessException的渲染输出和其他程序错误都用的是同一个render方法。

$json = ['code' => $code ? $code : 500, 'msg' => $this->_debug ? $exception->getMessage() : 'Server internal error'];

在未开启debug模式时提示给用户的是 Server internal error

2.中间件是可以捕捉到__construct的异常的问题我再研究一下。

walkor commented 2 years ago

我懂你的意思了,pr还原成第一版吧,我合并下。

walkor commented 2 years ago

非常感谢。已经合并,等过几天发版本

ichynul commented 2 years ago

OK

$json = ['code' => $code ? $code : 500, 'msg' => $this->getMessage()];
//这里可能需要再优化一下,有的会以 :0/1作为成功与否,0会被换成500。