Optimize exception stack of Ajax error

This commit is contained in:
Pig Fang 2019-07-08 12:01:26 +08:00
parent 83330c0dfd
commit 4196a952e0
6 changed files with 80 additions and 7 deletions

View File

@ -3,8 +3,7 @@
namespace App\Exceptions;
use Exception;
use Illuminate\Validation\ValidationException;
use Symfony\Component\HttpKernel\Exception\HttpException;
use Illuminate\Support\Arr;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
class Handler extends ExceptionHandler
@ -13,8 +12,34 @@ class Handler extends ExceptionHandler
* A list of the exception types that should not be reported.
*/
protected $dontReport = [
HttpException::class,
ValidationException::class,
\Illuminate\Auth\AuthenticationException::class,
\Illuminate\Auth\Access\AuthorizationException::class,
\Symfony\Component\HttpKernel\Exception\HttpException::class,
\Illuminate\Database\Eloquent\ModelNotFoundException::class,
\Illuminate\Validation\ValidationException::class,
PrettyPageException::class,
];
protected function convertExceptionToArray(Exception $e)
{
return [
'message' => $e->getMessage(),
'exception' => true,
'trace' => collect($e->getTrace())
->map(function ($trace) {
return Arr::only($trace, ['file', 'line']);
})
->filter(function ($trace) {
return Arr::has($trace, 'file');
})
->map(function ($trace) {
$trace['file'] = str_replace(base_path().DIRECTORY_SEPARATOR, '', $trace['file']);
return $trace;
})
->filter(function ($trace) {
return \Illuminate\Support\Str::startsWith($trace['file'], 'app');
})
->values(),
];
}
}

View File

@ -37,20 +37,28 @@ export async function walkFetch(request: Request): Promise<any> {
if (response.ok) {
return body
}
let message = body.message
// Process validation errors from Laravel.
if (response.status === 422) {
// Process validation errors from Laravel.
const { errors }: { message: string, errors: { [field: string]: string[] } } = body
return {
code: 1,
message: Object.keys(errors).map(field => errors[field][0])[0],
}
} else if (response.status === 403) {
showModal(body.message, undefined, 'warning')
showModal(message, undefined, 'warning')
return
}
throw new HTTPError(body.message || body, cloned)
if (body.exception && Array.isArray(body.trace)) {
const trace = (body.trace as { file: string, line: number }[])
.map((t, i) => `[${i + 1}] ${t.file}#L${t.line}`)
.join('\n')
message = `${message}\n<details>${trace}</details>`
}
throw new HTTPError(message || body, cloned)
} catch (error) {
emit('fetchError', error)
showAjaxError(error)

View File

@ -148,6 +148,21 @@ test('process backend errors', async () => {
},
clone: () => ({}),
})
.mockResolvedValueOnce({
status: 500,
headers: new Map([['Content-Type', 'application/json']]),
json() {
return Promise.resolve({
message: 'fake exception',
exception: true,
trace: [
{ file: 'k.php', line: 2 },
{ file: 'v.php', line: 3 },
],
})
},
clone: () => ({}),
})
const result: {
code: number,
@ -158,6 +173,11 @@ test('process backend errors', async () => {
await net.walkFetch({ headers: new Headers() } as Request)
expect(showModal).toBeCalledWith('forbidden', undefined, 'warning')
await net.walkFetch({ headers: new Headers() } as Request)
expect(showAjaxError.mock.calls[0][0].message).toBe(
'fake exception\n<details>[1] k.php#L2\n[2] v.php#L3</details>'
)
})
test('inject to Vue instance', () => {

View File

@ -5,6 +5,7 @@
## Tweaked
- Push notifications to queue for performance.
- Optimized exception stack of Ajax error.
## Fixed

View File

@ -5,6 +5,7 @@
## 调整
- 发送通知时将任务推送到队列
- 优化 Ajax 中的错误显示
## 修复

View File

@ -0,0 +1,18 @@
<?php
namespace Tests;
use Illuminate\Support\Str;
class HandlerTest extends TestCase
{
public function testRenderAjaxException()
{
$json = $this->get('/abc', ['Accept' => 'application/json'])->decodeResponseJson();
$this->assertIsString($json['message']);
$this->assertTrue($json['exception']);
$this->assertTrue(collect($json['trace'])->every(function ($trace) {
return Str::startsWith($trace['file'], 'app') && is_int($trace['line']);
}));
}
}