一、版本介绍
PS C:\phpstudy_pro\WWW\api.demo.test> php artisan --version
Laravel Framework 10.40.0
PS C:\phpstudy_pro\WWW\api.demo.test>
二、希望情况
-
希望正常情况,返回:
即:统一http状态为200,采用自定义code、message、data方式实现。
image.png
{
"code": 200,
"message": "请求成功",
"data": {
"token": "125|tZmR9SOUq946dwxdRM7hlrvvGDOnnmeUaA63MYiv3519f747"
}
}
- 不希望异常情况返回
虽然加了"X-Requested-With":"XMLHttpRequest"
请求头,可以避免返回html页面的错误,这样浏览器请求时,可以快速定位错误。但是,这样返回的数据,状态码依然是http的,不是自定义的,碰到有些需要自定义认证的异常消息时,接口总是不一致,前端处理起来非常麻烦,因此统一一下。


{
"message": "Device name 不能为空。",
"errors": {
"device_name": [
"Device name 不能为空。"
]
}
}
-
或者希望这样的错误保持原样。有利于开发过程中排查错误。
image.png
{
"message": "The GET method is not supported for route api/user/token. Supported methods: POST.",
"exception": "Symfony\\Component\\HttpKernel\\Exception\\MethodNotAllowedHttpException",
"file": "C:\\phpstudy_pro\\WWW\\api.demo.test\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\AbstractRouteCollection.php",
"line": 122,
"trace": [
{
"file": "C:\\phpstudy_pro\\WWW\\api.demo.test\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\AbstractRouteCollection.php",
"line": 107,
"function": "requestMethodNotAllowed",
"class": "Illuminate\\Routing\\AbstractRouteCollection",
"type": "->"
},
{
"file": "C:\\phpstudy_pro\\WWW\\api.demo.test\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\AbstractRouteCollection.php",
"line": 41,
"function": "getRouteForMethods",
"class": "Illuminate\\Routing\\AbstractRouteCollection",
"type": "->"
},
{
"file": "C:\\phpstudy_pro\\WWW\\api.demo.test\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\RouteCollection.php",
"line": 162,
"function": "handleMatchedRoute",
"class": "Illuminate\\Routing\\AbstractRouteCollection",
"type": "->"
},
{
"file": "C:\\phpstudy_pro\\WWW\\api.demo.test\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Router.php",
"line": 761,
"function": "match",
"class": "Illuminate\\Routing\\RouteCollection",
"type": "->"
},
{
"file": "C:\\phpstudy_pro\\WWW\\api.demo.test\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Router.php",
"line": 748,
"function": "findRoute",
"class": "Illuminate\\Routing\\Router",
"type": "->"
},
{
"file": "C:\\phpstudy_pro\\WWW\\api.demo.test\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Router.php",
"line": 737,
"function": "dispatchToRoute",
"class": "Illuminate\\Routing\\Router",
"type": "->"
},
{
"file": "C:\\phpstudy_pro\\WWW\\api.demo.test\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Kernel.php",
"line": 200,
"function": "dispatch",
"class": "Illuminate\\Routing\\Router",
"type": "->"
},
{
"file": "C:\\phpstudy_pro\\WWW\\api.demo.test\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
"line": 144,
"function": "Illuminate\\Foundation\\Http\\{closure}",
"class": "Illuminate\\Foundation\\Http\\Kernel",
"type": "->"
},
{
"file": "C:\\phpstudy_pro\\WWW\\api.demo.test\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest.php",
"line": 21,
"function": "Illuminate\\Pipeline\\{closure}",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "C:\\phpstudy_pro\\WWW\\api.demo.test\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Middleware\\ConvertEmptyStringsToNull.php",
"line": 31,
"function": "handle",
"class": "Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest",
"type": "->"
},
{
"file": "C:\\phpstudy_pro\\WWW\\api.demo.test\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
"line": 183,
"function": "handle",
"class": "Illuminate\\Foundation\\Http\\Middleware\\ConvertEmptyStringsToNull",
"type": "->"
},
{
"file": "C:\\phpstudy_pro\\WWW\\api.demo.test\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest.php",
"line": 21,
"function": "Illuminate\\Pipeline\\{closure}",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "C:\\phpstudy_pro\\WWW\\api.demo.test\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Middleware\\TrimStrings.php",
"line": 40,
"function": "handle",
"class": "Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest",
"type": "->"
},
{
"file": "C:\\phpstudy_pro\\WWW\\api.demo.test\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
"line": 183,
"function": "handle",
"class": "Illuminate\\Foundation\\Http\\Middleware\\TrimStrings",
"type": "->"
},
{
"file": "C:\\phpstudy_pro\\WWW\\api.demo.test\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Middleware\\ValidatePostSize.php",
"line": 27,
"function": "Illuminate\\Pipeline\\{closure}",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "C:\\phpstudy_pro\\WWW\\api.demo.test\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
"line": 183,
"function": "handle",
"class": "Illuminate\\Foundation\\Http\\Middleware\\ValidatePostSize",
"type": "->"
},
{
"file": "C:\\phpstudy_pro\\WWW\\api.demo.test\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Middleware\\PreventRequestsDuringMaintenance.php",
"line": 99,
"function": "Illuminate\\Pipeline\\{closure}",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "C:\\phpstudy_pro\\WWW\\api.demo.test\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
"line": 183,
"function": "handle",
"class": "Illuminate\\Foundation\\Http\\Middleware\\PreventRequestsDuringMaintenance",
"type": "->"
},
{
"file": "C:\\phpstudy_pro\\WWW\\api.demo.test\\vendor\\laravel\\framework\\src\\Illuminate\\Http\\Middleware\\HandleCors.php",
"line": 62,
"function": "Illuminate\\Pipeline\\{closure}",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "C:\\phpstudy_pro\\WWW\\api.demo.test\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
"line": 183,
"function": "handle",
"class": "Illuminate\\Http\\Middleware\\HandleCors",
"type": "->"
},
{
"file": "C:\\phpstudy_pro\\WWW\\api.demo.test\\vendor\\laravel\\framework\\src\\Illuminate\\Http\\Middleware\\TrustProxies.php",
"line": 39,
"function": "Illuminate\\Pipeline\\{closure}",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "C:\\phpstudy_pro\\WWW\\api.demo.test\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
"line": 183,
"function": "handle",
"class": "Illuminate\\Http\\Middleware\\TrustProxies",
"type": "->"
},
{
"file": "C:\\phpstudy_pro\\WWW\\api.demo.test\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
"line": 119,
"function": "Illuminate\\Pipeline\\{closure}",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "C:\\phpstudy_pro\\WWW\\api.demo.test\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Kernel.php",
"line": 175,
"function": "then",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "C:\\phpstudy_pro\\WWW\\api.demo.test\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Kernel.php",
"line": 144,
"function": "sendRequestThroughRouter",
"class": "Illuminate\\Foundation\\Http\\Kernel",
"type": "->"
},
{
"file": "C:\\phpstudy_pro\\WWW\\api.demo.test\\public\\index.php",
"line": 51,
"function": "handle",
"class": "Illuminate\\Foundation\\Http\\Kernel",
"type": "->"
}
]
}
三、实现处理
- 添加异常处理类
PS C:\phpstudy_pro\WWW\api.demo.test> php artisan make:exception ApiException
INFO Exception [C:\phpstudy_pro\WWW\api.demo.test\app\Exceptions\ApiException.php] created successfully.
修改ApiException.php
<?php
namespace App\Exceptions;
use Exception;
class ApiException extends Exception
{
public function __construct(string $message = "", int $code = 422, ?Throwable $previous = null)
{
parent::__construct($message, $code, $previous);
}
public function render()
{
return response()->json([
'code' => $this->code ?? 422,
'message' => $this->message,
]);
}
}
测试效果:

{
"code": 422,
"message": "用户名或密码不正确!"
}

一般情况,都可以使用throw new ApiException('用户名或密码不正确!');
的形式进行处理。但是系统的ValidationException
就不能处理了,那么能不能统一一下呢?当然可以。重写 public function render($request, Throwable $e)
即可。
<?php
namespace App\Exceptions;
use Illuminate\Auth\AuthenticationException;
use Illuminate\Contracts\Support\Responsable;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Illuminate\Http\Exceptions\HttpResponseException;
use Illuminate\Routing\Router;
use Illuminate\Validation\ValidationException;
use Throwable;
class Handler extends ExceptionHandler
{
/**
* The list of the inputs that are never flashed to the session on validation exceptions.
*
* @var array<int, string>
*/
protected $dontFlash = [
'current_password',
'password',
'password_confirmation',
];
/**
* Register the exception handling callbacks for the application.
*/
public function register(): void
{
$this->reportable(function (Throwable $e) {
//
});
}
/**
* 重写render实现
* @param $request
* @param Throwable $e
* @return \Illuminate\Http\JsonResponse|\Illuminate\Http\RedirectResponse|\Illuminate\Http\Response|mixed|\Symfony\Component\HttpFoundation\Response
* @throws ApiException
* @throws \ReflectionException
*/
public function render($request, Throwable $e)
{
if ($e instanceof ValidationException) {// 处理验证异常信息
throw new ApiException($e->getMessage(), $e->status);
} else {// 保持原来的实现
$e = $this->mapException($e);
if (method_exists($e, 'render') && $response = $e->render($request)) {
return Router::toResponse($request, $response);
}
if ($e instanceof Responsable) {
return $e->toResponse($request);
}
$e = $this->prepareException($e);
if ($response = $this->renderViaCallbacks($request, $e)) {
return $response;
}
return match (true) {
$e instanceof HttpResponseException => $e->getResponse(),
$e instanceof AuthenticationException => $this->unauthenticated($request, $e),
$e instanceof ValidationException => $this->convertValidationExceptionToResponse($e, $request),
default => $this->renderExceptionResponse($request, $e),
};
}
}
}
实现效果:
有处理部分

不处理部分:

- 总结:
1.新增ApiException.php
php artisan make:exception ApiException
并实现如下:
<?php
namespace App\Exceptions;
use Exception;
class ApiException extends Exception
{
public function __construct(string $message = "", int $code = 422, ?Throwable $previous = null)
{
parent::__construct($message, $code, $previous);
}
public function render()
{
return response()->json([
'code' => $this->code ?? 422,
'message' => $this->message,
]);
}
}
2.修改Exceptions 下的Handler.php
/**
* 重写render实现
* @param $request
* @param Throwable $e
* @return \Illuminate\Http\JsonResponse|\Illuminate\Http\RedirectResponse|\Illuminate\Http\Response|mixed|\Symfony\Component\HttpFoundation\Response
* @throws ApiException
* @throws \ReflectionException
*/
public function render($request, Throwable $e)
{
if ($request->ajax() && $e instanceof ValidationException) {// 处理验证异常信息
throw new ApiException($e->getMessage(), $e->status);
} else {// 保持原来的实现
$e = $this->mapException($e);
if (method_exists($e, 'render') && $response = $e->render($request)) {
return Router::toResponse($request, $response);
}
if ($e instanceof Responsable) {
return $e->toResponse($request);
}
$e = $this->prepareException($e);
if ($response = $this->renderViaCallbacks($request, $e)) {
return $response;
}
return match (true) {
$e instanceof HttpResponseException => $e->getResponse(),
$e instanceof AuthenticationException => $this->unauthenticated($request, $e),
$e instanceof ValidationException => $this->convertValidationExceptionToResponse($e, $request),
default => $this->renderExceptionResponse($request, $e),
};
}
}
至此,处理完毕。
网友评论