Laravel如何实现适合Api的异常处理响应格式


Posted in PHP onJune 14, 2020

前言

Laravel全局捕获异常后,会把异常转为相应的数据格式返回给用户。如果想要规定的数据格式相应,那我们只需重写异常捕获后的处理方法即可。

异常处理流程

Illuminate\Foundation\Exception\Handler 中的 render 方法用来将异常转化为响应。

public function render($request, Exception $e)
{
 if (method_exists($e, 'render') && $response = $e->render($request)) {
 return Router::toResponse($request, $response);
 } elseif ($e instanceof Responsable) {
 return $e->toResponse($request);
 }

 $e = $this->prepareException($e);

 if ($e instanceof HttpResponseException) {
 return $e->getResponse();
 } elseif ($e instanceof AuthenticationException) {
 return $this->unauthenticated($request, $e);
 } elseif ($e instanceof ValidationException) {
 return $this->convertValidationExceptionToResponse($e, $request);
 }

 return $request->expectsJson()
   ? $this->prepareJsonResponse($request, $e)
   : $this->prepareResponse($request, $e);
}

render() 中又调用了 prepareException() 对部分异常进行预处理,但并未执行转化为响应的操作。

ModelNotFoundException 一般在模型查找不到抛出,prepareException() 中它被转为 Symfony 包中NotFoundHttpException,默认状态码404;

AuthorizationException 在 Policy 权限未通过时抛出,prepareException() 中它被转为 Symfony 包中 AccessDeniedHttpException,默认状态码403;

TokenMismatchException 在 CSRF 验证未通过时抛出,prepareException() 中它被转为 Symfony 包中 HttpException,给定状态码419;

其他异常直接返回。

protected function prepareException(Exception $e)
{
 if ($e instanceof ModelNotFoundException) {
 $e = new NotFoundHttpException($e->getMessage(), $e);
 } elseif ($e instanceof AuthorizationException) {
 $e = new AccessDeniedHttpException($e->getMessage(), $e);
 } elseif ($e instanceof TokenMismatchException) {
 $e = new HttpException(419, $e->getMessage(), $e);
 }

 return $e;
}

在回到 render() ,预处理异常之后,又分别对 HttpResponseException、AuthenticationException 和 ValidationException 单独处理,并转为响应返回。

除此以外的异常,都在 prepareJsonResponse() 或 prepareResponse() 处理 ,expectsJson() 用来判断返回 json 响应还是普通响应。

修改异常响应格式

了解了异常处理流程,接下来就处理异常响应格式。

修改登录认证异常格式

由上文可知,AuthenticationException 被捕获后,调用 unauthenticated() 来处理。

protected function unauthenticated($request, AuthenticationException $exception)
{
 return $request->expectsJson()
    ? response()->json(['message' => $exception->getMessage()], 401)
    : redirect()->guest($exception->redirectTo() ?? route('login'));
}

在 appExceptionsHandler.php 中重写 unauthenticated() 使其返回我们想要的数据格式。

protected function unauthenticated($request, AuthenticationException $exception)
{
 return $request->expectsJson()
  ? response()->json([
   'code' => 0,
   'data' => $exception->getMessage(),
  ], 401)
  : redirect()->guest($exception->redirectTo() ?? route('login'));
}

修改验证异常格式

同样由上文可知,ValidationException 被捕获后交由 convertValidationExceptionToResponse() 处理,进入此方法后我们需要继续追踪,若是需要 json 响应,最终交由 invalidJson() 处理。

protected function convertValidationExceptionToResponse(ValidationException $e, $request)
{
 if ($e->response) {
  return $e->response;
 }

 return $request->expectsJson()
    ? $this->invalidJson($request, $e)
    : $this->invalid($request, $e);
}
protected function invalidJson($request, ValidationException $exception)
{
 return response()->json([
  'message' => $exception->getMessage(),
  'errors' => $exception->errors(),
 ], $exception->status);
}

我们继续在 appExceptionsHandler.php 重写 invalidJson() 即可自定义返回格式。

protected function invalidJson($request, ValidationException $exception)
{
 return response()->json([
  'code' => 0,
  'data' => $exception->errors(),
 ], $exception->status);
}

修改其他异常格式

其他异常是调用 prepareJsonResponse() 来处理,此方法又调用 convertExceptionToArray() 来处理响应格式。

protected function prepareJsonResponse($request, Exception $e)
{
 return new JsonResponse(
  $this->convertExceptionToArray($e),
  $this->isHttpException($e) ? $e->getStatusCode() : 500,
  $this->isHttpException($e) ? $e->getHeaders() : [],
  JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES
 );
}
protected function convertExceptionToArray(Exception $e)
{
 return config('app.debug') ? [
  'message' => $e->getMessage(),
  'exception' => get_class($e),
  'file' => $e->getFile(),
  'line' => $e->getLine(),
  'trace' => collect($e->getTrace())->map(function ($trace) {
   return Arr::except($trace, ['args']);
  })->all(),
 ] : [
  'message' => $this->isHttpException($e) ? $e->getMessage() : 'Server Error',
 ];
}

在 appExceptionsHandler.php 中重写 convertExceptionToArray() 来自定义其他异常响应格式。

protected function convertExceptionToArray(Exception $e)
{
 return config('app.debug') ? [
  'code' => 0,
  'data' => $e->getMessage(),
  'exception' => get_class($e),
  'file' => $e->getFile(),
  'line' => $e->getLine(),
  'trace' => collect($e->getTrace())->map(function ($trace) {
   return Arr::except($trace, ['args']);
  })->all(),
 ] : [
  'code' => 0,
  'data' => $this->isHttpException($e) ? $e->getMessage() : 'Server Error',
 ];
}

强制 json 响应

代码中多次出现了 expectsJson() ,此方法是用来判断返回 json 响应还是普通响应。

public function expectsJson()
{
 return ($this->ajax() && ! $this->pjax() && $this->acceptsAnyContentType()) || $this->wantsJson();
}

以下两种条件下,会返回json响应。

非XML请求、非pjax并且 Headers 中 Accept 设置为接收所有格式响应;

Headers Accept 设置为 /json、+json。如:Accept:application/json。

除此之外的情况,将不会响应json。我们可以利用中间件强制追加 Accept:application/json,使异常响应时都返回json。(参考教程 L03 6.0 中提到的方法)

创建中间件 AcceptHeader

<?php

namespace App\Http\Middleware;

use Closure;

class AcceptHeader
{
 public function handle($request, Closure $next)
 {
  $request->headers->set('Accept', 'application/json');

  return $next($request);
 }
}

在 app/Http/Kernel.php 中,将中间件加入路由组即可。

protected $middlewareGroups = [
 'web' => [
  .
  .
  .
 'api' => [
  \App\Http\Middleware\AcceptHeader::class,
  'throttle:60,1',
  'bindings',
 ],
];

大功告成。

总结

到此这篇关于Laravel如何实现适合Api的异常处理响应格式的文章就介绍到这了,更多相关Laravel适合Api的异常处理响应格式内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

PHP 相关文章推荐
使用php发送有附件的电子邮件-(PHPMailer使用的实例分析)
Apr 26 PHP
深入解析PHP内存管理之谁动了我的内存
Jun 20 PHP
浅析php中json_encode()和json_decode()
May 25 PHP
php比较相似字符串的方法
Jun 05 PHP
试用php中oci8扩展
Jun 18 PHP
ThinkPHP中order()使用方法详解
Apr 19 PHP
PHP基于GD库实现的生成图片缩略图函数示例
Jul 05 PHP
PHP实现求两个字符串最长公共子串的方法示例
Nov 17 PHP
PHP调用接口用post方法传送json数据的实例
May 31 PHP
php 实现银联商务H5支付的示例代码
Oct 12 PHP
Laravel框架数据库迁移操作实例详解
Apr 06 PHP
php判断IP地址是否在多个IP段内
Aug 18 PHP
Yii redis集合的基本使用教程
Jun 14 #PHP
php获取小程序码的实现代码(B类接口)
Jun 13 #PHP
laravel admin实现分类树/模型树的示例代码
Jun 10 #PHP
详解php反序列化
Jun 10 #PHP
实例讲解PHP表单
Jun 10 #PHP
如何在PHP中使用数组
Jun 09 #PHP
详解PHP中curl_multi并发的实现
Jun 08 #PHP
You might like
php获取参数的几种方法总结
2014/02/18 PHP
ThinkPHP的L方法使用简介
2014/06/18 PHP
php中convert_uuencode()与convert_uuencode函数用法实例
2014/11/22 PHP
php一个解析字符串排列数组的方法
2015/05/12 PHP
php实现模拟post请求用法实例
2015/07/11 PHP
【消息提示组件】,兼容IE6/7&amp;&amp;FF2
2007/09/04 Javascript
初窥JQuery-Jquery简介 入门了解篇
2010/11/25 Javascript
30个最好的jQuery 灯箱插件分享
2011/04/25 Javascript
JS中的substring和substr函数的区别说明
2013/05/07 Javascript
JavaScript实现url地址自动检测并添加URL链接示例代码
2013/11/12 Javascript
SinaEditor使用方法详解
2013/12/28 Javascript
jquery获取html元素的绝对位置和相对位置的方法
2014/06/20 Javascript
js实现的二级横向菜单条实例
2015/08/22 Javascript
jquery插件jquery.LightBox.js实现点击放大图片并左右点击切换效果(附demo源码下载)
2016/02/25 Javascript
bootstrap datetimepicker2.3.11时间插件使用
2016/11/19 Javascript
js实现简易垂直滚动条
2017/02/22 Javascript
jQuery插件echarts实现的去掉X轴、Y轴和网格线效果示例【附demo源码下载】
2017/03/04 Javascript
Node.js微信 access_token ( jsapi_ticket ) 存取与刷新的示例
2017/09/30 Javascript
详解JavaScript事件循环机制
2018/09/07 Javascript
如何去除富文本中的html标签及vue、react、微信小程序中的过滤器
2018/11/21 Javascript
[04:16]完美世界DOTA2联赛PWL S2 集锦第一期
2020/11/23 DOTA
Python实现导出数据生成excel报表的方法示例
2017/07/12 Python
python cv2读取rtsp实时码流按时生成连续视频文件方式
2019/12/25 Python
浅谈ROC曲线的最佳阈值如何选取
2020/02/28 Python
python:批量统计xml中各类目标的数量案例
2020/03/10 Python
Anconda环境下Vscode安装Python的方法详解
2020/03/29 Python
Python切片列表字符串如何实现切换
2020/08/06 Python
维多利亚的秘密官方旗舰店:VICTORIA’S SECRET
2018/04/02 全球购物
请说出几个常用的异常类
2013/01/08 面试题
应届本科毕业生求职信
2014/07/23 职场文书
2015年保安个人工作总结
2015/04/02 职场文书
教师节班会开场白
2015/06/01 职场文书
大学生活委员竞选稿
2015/11/21 职场文书
2019年怎样写好导游词?
2019/07/02 职场文书
CSS3 实现的图片悬停的切换按钮
2021/04/13 HTML / CSS
MySQL 查询速度慢的原因
2021/05/25 MySQL