api laravel 统一返回方法_Laravel API 错误处理:当异常时,如何返回消息

image

基于 API 的项目开发越来越受欢迎,并且使用 Laravel 就能很容易实现。但是在针对如何处理各种异常的话题很少被提及。所以 API 的使用者们经常会抱怨除了收到 Server error ,很少有更多的错误信息。那么,我们该如何优雅的处理 API 错误让其变得更具有可读性呢?

目标:状态码 + 错误消息

对于 API 开发来讲,正确的错误描述甚至比仅基于 Web 浏览器的项目更为重要。作为使用者,我们也可以通过浏览器消息提示清楚地了解错误以及该怎么解决。但对于 API 本身来说,它们是由软件而非人员使用的,因此返回的结果应 readable by machines 。这意味着HTTP状态代码就必不可少。

API 给每个请求都会返回一个状态码,请求成功通常是 200,或者是以 2 开头的其他状态码。

如果返回错误响应,则该响应不应包含2xx代码,以下是最常见的错误代码:

| 状态码 | 描述 |

| 404 | 未找到(请求资源不存在) |

| 401 | 未认证 (需要登录) |

| 403 | 没有权限 |

| 400 | 错误的请求(URL或参数不正确) |

| 422 | 验证失败 |

| 500 | 服务器错误 |

注意:返回响应时,如果没有添加状态码,Laravel 会自动指定状态码,但并不能保证所指定的状态码正确。所以最好还是自己手动添加正确的状态码。

除此之外,我们还要考虑到 human-readable messages。因此,典型的响应应包含 HTTP 错误代码和 JSON 结果,如下所示:

{

"error": "Resource not found"

}

理想情况下,它应该包含更多详细信息,以帮助API使用者处理错误。这是Facebook API如何返回错误的示例:

{

"error": {

"message": "Error validating access token: Session has expired on Wednesday, 14-Feb-18 18:00:00 PST. The current time is Thursday, 15-Feb-18 13:46:35 PST.",

"type": "OAuthException",

"code": 190,

"error_subcode": 463,

"fbtrace_id": "H2il2t5bn4e"

}

}

通常情况下,错误内容就是需要在浏览器或移动端显示的内容。因此最好根据需要提供尽可能的细节。

现在,让我们了解如何更好地改善 API 的错误提示。

提示1.即使在本地也要切换 APP_DEBUG=false

Laravel 的 .env 文件有一个重要的设置 APP_DEBUG ,它的值可以为 false or true。

如果设置为 true, 则将显示所有错误以及详细信息,包括类名称,数据库表等。

image

这是一个巨大的安全问题,因此在生产环境中,强烈建议将其设置为 false。

但是,我建议即使在本地也要针对 API 项目将其关闭,原因如下。

关闭实际错误后,您将被迫像 API 使用者那样思考,因为他们只会收到服务器错误(返回 Server error)而没有更多的信息。换句话说,这时候你就需要考虑如何处理错误并提供合适的响应消息。

提示2:未处理的路由-回退方法

第一种情况-如果有人调用不存在的 API 怎么办,有人甚至在 URL 中输入错误的地址。默认情况下,您从 API 获得以下响应:

Request URL: http://q1.test/api/v1/offices

Request Method: GET

Status Code: 404 Not Found

{

"message": ""

}

至少 404 响应成功。其实可以做得更好,可以通过一些消息来解释错误。

为此你可以在 routes/api.php 的末尾指定 Route::fallback() 方法, 处理所有访问不存在路由的请求。

Route::fallback(function(){

return response()->json([

'message' => 'Page Not Found. If error persists, contact info@website.com'], 404);

});

结果还是相同的404响应,但现在出现了错误消息,提供了有关如何处理此错误的更多信息。

提示3.覆盖404 ModelNotFoundException

最常见就是找不到某些模型对象,通常由 Model :: findOrFail($ id) 抛出。以下是你的 API 会显示的典型消息:

{

"message": "No query results for model [App\\Office] 2",

"exception": "Symfony\\Component\\HttpKernel\\Exception\\NotFoundHttpException",

...

}

这是正确的,但向最终用户显示的消息不是很漂亮,因此,我的建议是重写对该特定异常的处理。

我们可以在 app/Exceptions/Handler.php (请记住该文件,我们将在以后多次返回它)中使用 render() 方法:

// Don't forget this in the beginning of file

use Illuminate\Database\Eloquent\ModelNotFoundException;

// ...

public function render($request, Exception $exception)

{

if ($exception instanceof ModelNotFoundException) {

return response()->json([

'error' => 'Entry for '.str_replace('App\\', '', $exception->getModel()).' not found'], 404);

}

return parent::render($request, $exception);

}

我们可以在这种方法中捕获任意数量的异常。在本例中,我们将返回相同的404代码,但可读性更高:

{

"error": "Entry for Office not found"

}

注意: 你有没有注意到一个有趣的方法?$exception->getModel() ?我们可以从 $Exception 对象中获得很多非常有用的信息,下面是 PhpStorm 自动完成的屏幕截图::

image

提示4:在验证中尽可能多捕获信息

开发人员一般不会考虑过多的验证规则,而是坚持使用诸如 required,date,emai 之类的简单规则。但是对于 API 而言,实际上错误的最典型原因是-消费者提交无效数据。

如果我们不花更多的精力来收集未通过验证的数据,那么 API 将通过后端验证,并抛出简单的 Server error,而没有任何详细信息(实际上原因是数据库查询错误)。

让我们看一下这个示例–我们在 Controller 中有一个 store() 方法:

public function store(StoreOfficesRequest $request)

{

$office = Office::create($request->all());

return (new OfficeResource($office))

->response()

->setStatusCode(201);

}

我们的 FormRequest 文件 app/Http/Requests/StoreOfficesRequest.php 包含两个规则:

public function rules()

{

return [

'city_id' => 'required|integer|exists:cities,id',

'address' => 'required'

];

}

如果我们遗漏了这两个参数并在其中传递空值,API 将返回一个相当易读的错误,带有 **422 ** 状态码(此状态码默认是由于 Laravel 验证失败而产生):

{

"message": "The given data was invalid.",

"errors": {

"city_id": ["The city id must be an integer.", "The city id field is required."],

"address": ["The address field is required."]

}

}

它列出了所有字段错误,还提到了每个字段的所有错误,而不仅仅是捕获到的第一个错误。

现在,如果我们不指定那些验证规则并允许验证通过,以下是 API 返回:

{

"message": "Server Error"

}

仅仅是服务器错误,没有其他有用的信息,什么是错误的,什么字段是缺失或不正确的。因此 API 使用者会懵逼。

所以我将在这里重复我的观点-请尝试在验证规则中捕获尽可能多的可能情况。检查字段是否存在、类型、最小-最大值、重复等

提示5 通常使用 Try-Catch 可以避免空的 500 服务器错误

继续上面的示例,使用 API 时,最糟糕的事情就是空错误。但是任何事情都会出错,尤其是在大型项目中,我们无法修复或预测随机错误。

想象一下这个控制器代码:

public function store(StoreOfficesRequest $request)

{

$admin = User::find($request->email);

$office = Office::create($request->all() + ['admin_id' => $admin->id]);

(new UserService())->assignAdminToOffice($office);

return (new OfficeResource($office))

->response()

->setStatusCode(201);

}

这是一个虚构的例子,也很常见。用电子邮件搜索用户,然后创建一条记录,对该记录进行操作。并且在任何步骤上,都可能发生错误。电子邮件可能为空,可能找不到管理员(或发现错误的管理员),服务方法可能会引发任何其他错误或异常等。

有很多处理和使用 try-catch 的方法,但是最流行的方法之一就是只捕获一个大的try-catch,然后对应是哪个异常类抛出的:

try {

$admin = User::find($request->email);

$office = Office::create($request->all() + ['admin_id' => $admin->id]);

(new UserService())->assignAdminToOffice($office);

} catch (ModelNotFoundException $ex) { // User not found

abort(422, 'Invalid email: administrator not found');

} catch (Exception $ex) { // Anything that went wrong

abort(500, 'Could not create office or assign it to administrator');

}

这样,我们可以随时调用 abort() 并添加所需的错误消息。如果我们在每个控制器(或其中的大多数控制器)中执行此操作,那么我们的 API 将返回与 Server error 相同的500,但包含更多可操作的错误消息。

提示6 通过捕获异常来处理第三方 API 错误

如今,Web 项目使用大量外部 API,它们也可能会失败。如果他们的 API 不错,那么他们将提供适当的异常和错误机制,因此我们需要在应用程序中使用它。

例如,对某些 URL进行 Guzzle curl 请求并捕获异常。

代码很简单:

$client = new \GuzzleHttp\Client();

$response = $client->request('GET', 'https://api.github.com/repos/guzzle/guzzle123456');

// ... 用该响应做点什么

您可能已经注意到,Github URL 无效,并且该存储库不存在。而且,如果我们将代码保持原样,我们的 API 将抛出 500 Server error,没有其他详细信息。但是我们可以捕获异常,并向消费者提供更多详细信息:

// 在顶部

use GuzzleHttp\Exception\RequestException;

// ...

try {

$client = new \GuzzleHttp\Client();

$response = $client->request('GET', 'https://api.github.com/repos/guzzle/guzzle123456');

} catch (RequestException $ex) {

abort(404, 'Github Repository not found');

}

提示6.1 创建自己的异常

我们甚至可以更进一步,创建我们自己的异常,特别是与一些第三方 API 错误相关的异常。

php artisan make:exception GithubAPIException

然后,我们新生成的文件 app/Exceptions/GithubAPIException.php将如下所示:

namespace App\Exceptions;

use Exception;

class GithubAPIException extends Exception

{

public function render()

{

// ...

}

}

我们甚至可以让它为空,但还是把它当作异常抛出。即使是异常 name,也可以帮助 API 用户避免将来的错误。所以我们这样做:

try {

$client = new \GuzzleHttp\Client();

$response = $client->request('GET', 'https://api.github.com/repos/guzzle/guzzle123456');

} catch (RequestException $ex) {

throw new GithubAPIException('Github API failed in Offices Controller');

}

不仅如此-我们可以将错误处理移至 app / Exceptions / Handler.php 文件中(还记得上面吗?),如下所示:

public function render($request, Exception $exception)

{

if ($exception instanceof ModelNotFoundException) {

return response()->json(['error' => 'Entry for '.str_replace('App\\', '', $exception->getModel()).' not found'], 404);

} else if ($exception instanceof GithubAPIException) {

return response()->json(['error' => $exception->getMessage()], 500);

} else if ($exception instanceof RequestException) {

return response()->json(['error' => 'External API call failed.'], 500);

}

return parent::render($request, $exception);

}

最后的注意事项

以上就是我处理 API 错误的技巧,但这不是严格的规则。每个人都可以有自己的想法,如果你有自己的一些看法,可以在下面发表评论并进行讨论。

最后,除了错误处理之外,我想鼓励你做两件事:

为用户提供详细的 API 文档,请使用类似如下的包 API Generator;

返回 api 错误时,使用第三方服务 Bugsnag / Sentry / Rollbar。它们不是免费的,但是在调试时可以节省大量时间。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/489385.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

计算机电力英语翻译,电力专业英语阅读与翻译..doc

电力专业英语阅读与翻译.电力专业英语阅读与翻译Summary of glossary 术语电力系统 (electric) power systempower generation 发电transmission system(network) 输电系统(网络)distribution system 配电系统发电 power generationpower plant 发电厂powerhouse 发电站hydropo…

python基础笔记_python基础学习笔记

一、Python四种类型的数据格式 整数----2、3、 长整数:指的是比较大一点的整数 浮点数----3.23、52.3EE:标记表示10的幂。 复数----(-54J)、(2.3-4.6J) 二、Python字符串 a 单引号:单引号输出字符串,字符串…

Nature好文:过去150年,科学与产业经历四段情缘!

来源:Nature 574, 481-485 (2019) doi: 10.1038/d41586-019-03172-5Nature(《自然》)创刊 150 周年之际,历史学家保罗卢西尔(Paul Lucier)特别撰写系列文章,回顾了这 150 年来科学体系的塑造。本…

eclipse导入项目pom文件报错_eclipse导入maven管理的项目时,pom.xml第一行报错。错误如图1。而且dubbo的xml也不识别。...

展开全部dubbo的问题是因为他的官网关了 对应的xsd无法获取到对于 eclipse 来说这个文件只能改为从62616964757a686964616fe59b9ee7ad9431333337383239本地读取才能正常修改方法是提取dubbo.jar META-INF 中的dubbo.xsd文件到随意一个目录,建议放Eclipse目录下就好打…

python爬取网页有乱码怎么解决_Python爬取网页requests乱码

**之前有在裁判文书上爬取数据,这段时间重新运行爬虫后发现无法获取网页数据, 找了一下发现requests网页源码返回的是乱码** (如下截取一部分返回的数据:不知道是不是网站对网页内容进行了加密,请问如何解决这个问题&a…

北京联合大学计算机学院在哪个校区,北京联合大学各校区联系地址大全

出国留学网考研院校频道为大家提供北京联合大学各校区联系地址大全,希望能帮助到大家。北京联合大学下设14所学院:北京联合大学应用文理学院 地址:海淀区北土城西路197号 邮编:100191北京联合大学师范学院 地址:朝阳区…

spring基础整理

spring基础教程&#xff1a;https://www.tutorialspoint.com/spring/spring_overview.htm 注入实例 <bean id"" class""> <constructor-arg ref"beanId" /> </bean> <bean id"beanId" class/> 转载于:https:/…

Gartner 2019年超融合魔力象限:新增深信服,国内华为、华云在榜

来源&#xff1a;云头条近日&#xff0c;国际权威研究分析机构Gartner公布了2019全球《超融合基础设施魔力象限》。报告显示国内仅有三家云计算厂商进入2019超融合基础设施魔力象限&#xff0c;分别是深信服、华为、华云数据。深信服超融合&#xff08;sangfor aCloud&#xff…

go结构体初始化_golang中结构体的初始化方法

目录1、自定义一个结构体type Vertex struct {X, Y float64}2、初始化方法-指针&#xff1a;rect1 : new(Vertex )rect2 : &Vertex {}rect3 : &Vertex {1, 2}rect4 : &Vertex {X:100, Y:200}注意: 这几个变量全部为指向Rect结构的指针(指针变量)&#xff0c;因为使…

python脚本转换成apk_apktool反编译apk并回编译

apktool反编译apk并回编译 下载apktool工具&#xff1a; apktool官方网站&#xff1a;https://ibotpeaches.github.io/Apktool/ 安装apktool并配置apktool环境变量 安装方法&#xff1a;https://ibotpeaches.github.io/Apktool/install 用apktool反编译apk文件 apktool d apkna…

计算机主机机箱面板辐射,电脑机箱如何防辐射----给大家科普一下

该楼层疑似违规已被系统折叠 隐藏此楼查看此楼对于天天坐在电脑前的玩家和用户来说&#xff0c;防辐射已经成为一个不可忽视的问题&#xff0c;如果对于孕妇和小孩来说&#xff0c;则更值得重视。我们知道&#xff0c;在显示器已经过渡到液晶时代之后&#xff0c;电脑辐射就主要…

分享三个非常适合新手的网站

http://www.jq22.com/&#xff08;jquery插件库里面很多个性插件。个人很喜欢&#xff0c;以前一些插件以为都要自己写现在可以借鉴别人的了。还好看好用&#xff09;、http://www.yiibai.com/和http://www.runoob.com/&#xff08;资料很全的网站&#xff09;,http://www.w3ch…

万维网之父公布拯救网络计划:保护互联网免遭滥用,惠及人类

来源&#xff1a;澎湃新闻万维网之父蒂姆伯纳斯-李爵士&#xff08;Sir Tim Berners-Lee&#xff09;公布了一项拯救网络的全球计划&#xff0c;呼吁政府和企业能够阻止对互联网的滥用&#xff0c;保护互联网免受政治操纵、假新闻、侵犯隐私等其他威胁。这项计划名为《互联网契…

mybatis insert 重复数据2条_Mybatis框架lt;增gt;:添加一条数据到数据库中,insert...

在以上框架中&#xff0c;前面所搭建好的框架全部固定好&#xff0c;接下来&#xff0c;我们在此基础上实现功能使用insert添加一条数据到数据库中(1)在UserMapper接口中添加对应方法&#xff0c;//在数据库表中增添一条数据&#xff0c;返回为int类型&#xff0c;参数传递Usee…

python语言程序设计基础嵩天答案第二章_python语言程序设计基础(嵩天版),第二章程序练习题...

python语言程序设计基础&#xff08;嵩天版&#xff09;&#xff0c;第二章程序练习题 欢迎访问江南烧酒的博客 2.2汇率兑换程序。按照1美元6人民币汇率编写一个美元和人民币的双向兑换程序。 """ 2.2汇率兑换程序&#xff0c;美元和人民币 """ …

边缘数据中心维护的4个基本组件

来源&#xff1a;外电翻译边缘数据中心设施运行与数据中心相同的通用计算硬件&#xff0c;其中包括存储和网络设备。企业经常将边缘数据中心部署在非传统位置&#xff0c;例如移动通信基站、工厂车间&#xff0c;甚至是在野外。边缘数据中心为现场多个设备提供管理层&#xff0…

计算机组成说明,计算机组成与系统结构考试说明.ppt

《计算机组成与系统结构考试说明.ppt》由会员分享&#xff0c;可在线阅读&#xff0c;更多相关《计算机组成与系统结构考试说明.ppt(11页珍藏版)》请在人人文库网上搜索。1、计算机组成与系统结构考试说明,考试时间大家关注教务通知,考试题型,1选择题 10个共10分 2填空 5 个 共…

ubuntun系统mysql数据库同步_Ubutun 14.10下mysql 主从同步详细操作

d)配置MySQL从服务器的my.cnf文件vi /etc/my.cnf #编辑配置文件&#xff0c;在[mysqld]部分添加下面内容server-id2 #配置文件中已经有一行server-id1&#xff0c;修改其值为2&#xff0c;表示为从数据库log_binmysql-bin #启动MySQ二进制日志系统&#xff0c;注意&#xff1a;…

XidianOJ 1195 Industry of Orz Pandas

--正文 贪心 排序好慢慢找就好 #include <iostream> #include <cstring> #include <cstdio> #include <algorithm> using namespace std; typedef long long LL;int a[20001],b[20001]; int n,m,x; int main(){while (scanf("%d %d %d",&…

js设计一个带开关的时钟_数电题:三个按键一个灯

有这样一道数电题&#xff1a;一个LED发光二极管由A、B、C三个独立按键控制&#xff0c;要求按压A、B、C三个按键中的任意一个都能对LED实现亮、灭控制(即每按压一次按键&#xff0c;LED发光状态改变一次)。请用D触发器或JK触发器实现&#xff0c;画出逻辑电路图&#xff0c;说…