Laravel异常上下文解决方案分享

目录
  • 前言
  • 开始改造
  • 优化实现
  • 逻辑复用
  • 总结

前言

异常时我们通常希望在用户侧给一个友好的提示,但默认使用框架的异常处理方案是不 OK 的。

最近项目遇到一个情况,我们在遇到用户访问某个信息没有权限的时候,希望提示详细的原因,比如当访问一个团队资源时非成员访问的场景下会提示一个:您不是 [xxxxxx] 团队的成员,暂时无法查看,可<申请加入>,同时需要显示打码后的团队名称,以及加入按钮,可是接口方的逻辑是当没有权限时直接 abort 了:

abort_if(!$user->isMember($resouce->team), 403, '您无权访问该资源');

得到的响应结果如下:

HTTP/1.0 403 Forbidden

{
	"message": "您无权访问该资源"
}

我们不可能将 message 用 html 来完成前端提示页的展示,这样耦合性太强,违背了前后端分离的原则。我们的目标是返回如下的格式即可解决:

HTTP/1.0 403 Forbidden

{
	"message": "您无权访问该资源",
	"team": {
		"id": "abxT8sioa0Ms",
		"name": "CoDesign****"
	}
}

通过携带上下文的方法传递数据,方便了前端同学自由组合。

开始改造

当然这并不是什么复杂的事情,直接修改原来的 abort_if 即可解决:

- abort_if(!$user->isMember($resouce->team), 403, '您无权访问该资源');
+ if (!$user->isMember($resouce->team)) {
+	return response()->json([
+		'message' => '您无权访问该资源',
+		'team' => [
+			'id' => $resouce->team_id,
+			'name'=> $resouce->team->desensitised_name,
+		]
+	], 403);
+ }

这样看起来解决了问题,可是试想一下,如果是在闭包里面检测到异常想要退出,上面这种 return 式的写法就会比较难搞了,毕竟 return 只会终止最近的上下文环境,我们还是希望像 abort 一样能终止整个应用的执行,再进行另一番改造。

优化实现

看了 abort 源码,我发现它的第一个参数其实支持 \Symfony\Component\HttpFoundation\Response 实例,而上面我们 return 的结果就是它的实例,所以我们只需要改成这样就可以了:

 if (!$user->isMember($resouce->team)) {
	abort(response()->json([
		'message' => '您无权访问该资源',
		'team' => [
			'id' => $resouce->team_id,
			'name'=> $resouce->team->desensitised_name,
		]
	], 403));
 }

新的问题来了,如果需要复用的时候还是比较尴尬,这段代码将会重复出现在各种有此权限判断的地方,这并不是我们想要的。

逻辑复用

为了达到逻辑复用,我认证看了 \App\Exceptions\Handler 的实现,发现父类的 render 方法还有这么一个设计:

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

    //...

所以,我们可以将这个逻辑抽离为一个独立的异常类,实现 render 方法即可:

$ ./artisan make:exception NotTeamMemberException

代码如下:

<?php

namespace App\Exceptions;

use App\Team;

class NotTeamMemberException extends \Exception
{
    public Team $team;

    public function __construct(Team $team, $message = "")
    {
        $this->team = $team;
        parent::__construct($message, 403);
    }

    public function render()
    {
        return response()->json(
            [
                'message' => !empty($this->message) ? $this->message : '您无权访问该资源',
                'team' => [
                    'id' => $this->team->id,
                    'name' => $this->team->desensitised_name,
                ],
            ],
            403
        );
    }
}

这样一来,我们的逻辑就变成了:

if (!$user->isMember($resouce->team)) {
 	throw new NotTeamMemberException($resouce->team, '您无权访问该资源');
}

当然也可以简化为:

\throw_if(!$user->isMember($resouce->team), NotTeamMemberException::class, $resouce->team, '您无权访问该资源');

问题到这里总算以一个比较完美的方式解决了,如果你有更好的方案欢迎评论探讨。

总结

到此这篇关于Laravel异常上下文解决方案的文章就介绍到这了,更多相关Laravel异常上下文解决内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Laravel中错误与异常处理的用法示例

    前言 在本文中,我们将探讨 Laravel Web 框架中最重要和最少讨论的功能之一 - 异常处理. Laravel 带有一个内置的异常处理程序,可以让您轻松地以友好的方式报告和呈现异常. Laravel 自带错误和异常处理,App\Exceptions\Handler 负责上报异常和如何返回内容,以及未登录的处理. App\Exceptions\Handler 位于 app\Exceptions\Handler.php,下面介绍这个类的属性和用法.好了,话不多说了,来一起看看详细的介绍吧 忽略

  • Laravel如何使用数据库事务及捕获事务失败后的异常详解

    前言 如果大家在Laravel中要想在数据库事务中运行一组操作,则可以在 DB facade 中使用 transaction 方法.如果在事务的闭包内抛出异常,事务将会被自动还原.如果闭包运行成功,事务将被自动提交. 你不需要担心在使用 transaction 方法时还需要亲自去手动还原或提交事务: DB::transaction(function () { DB::table('users')->update(['votes' => 1]); DB::table('posts')->d

  • 如何重写Laravel异常处理类详解

    现在开发前后端分离变得越来越流行了,后端只提供接口返回json格式的数据,即使是错误信息也要以json格式来返回,然而目前无论是Laravel框架还是ThinkPHP框架,都只提供了返回json数据的方法,对异常的处理并不是以json格式来返回给我们,所以这里就需要我们自己来改写. 首先我们在app/Exceptions目录新建一个ExceptionHandler.php继承自Handler.php namespace App\Exceptions; class ExceptionHandler

  • laravel 框架配置404等异常页面

    在Laravel中所有的异常都由Handler类处理,该类包含两个方法:report和render,其中render方法将异常渲染到http响应中.laravel的Handler类文件位置:app/Exceptions/Handler,由于render方法时间异常渲染到http响应中,所以我们只需要修改下render方法即可 网上很多的方法是将render方法修改成: public function render($request, Exception $exception) { if ($ex

  • Laravel异常上下文解决方案分享

    目录 前言 开始改造 优化实现 逻辑复用 总结 前言 异常时我们通常希望在用户侧给一个友好的提示,但默认使用框架的异常处理方案是不 OK 的. 最近项目遇到一个情况,我们在遇到用户访问某个信息没有权限的时候,希望提示详细的原因,比如当访问一个团队资源时非成员访问的场景下会提示一个:您不是 [xxxxxx] 团队的成员,暂时无法查看,可<申请加入>,同时需要显示打码后的团队名称,以及加入按钮,可是接口方的逻辑是当没有权限时直接 abort 了: abort_if(!$user->isMem

  • Spring应用抛出NoUniqueBeanDefinitionException异常的解决方案

    前言 我们在开发Spring应用时可能会不小心注入两个相同类型的Bean,比如实现了两个相同Service接口的类,示例伪代码如下: interface SampleService {   String getName(); } class ServiceA implements SampleService{    String getName(){      return "john";    } } class ServiceB implements SampleService{

  • java.lang.NullPointerException异常问题解决方案

    java.lang.NullPointerException异常原因是因为创建了一个引用类型的变量却没有指向任何对象而又去通过这个引用类型变量加点的形式去访问非静态的方法及属性. 给出三种情况, 第一种情况,在启动类中定义了引用类型变量,赋值为空: /** * 引用类型变量没有指向对象所引起的空指针异常 * @author Superhero * @version 2018年12月16日上午10:32:43 */ //图书类 class Books { private String name;

  • 跨域(CORS)问题的解决方案分享

    目录 前后端分离项目,如何解决跨域问题 什么是跨域问题 跨域问题演示及解决 点击前端登录按钮 覆盖默认的CorsFilter来解决该问题 重新运行代码,点击登录按钮 设置SpringSecurity允许OPTIONS请求访问 重新运行代码,点击登录按钮 一次完整的跨域请求 先发起一次OPTIONS请求进行预检 发起真实的跨域请求 前后端分离项目,如何解决跨域问题 跨域资源共享(CORS)是前后端分离项目很常见的问题,本文主要介绍当SpringBoot应用整合SpringSecurity以后如何解

  • C#中跨线程访问控件问题解决方案分享

    net 原则上禁止跨线程访问控件,因为这样可能造成错误的发生,推荐的解决方法是采用代理,用代理方法来间接操作不是同一线程创建的控件. 第二种方法是禁止编译器对跨线程访问作检查,可以实现访问,但是出不出错不敢保证Control.CheckForIllegalCrossThreadCalls = false; 最近我在做一个项目,遇到了跨线程要去访问页面控件.但是总是提示出错,不能在其它线程中修改创建控件的线程的控件的值,后来采用了匿名代理,结果很轻松地解决了.解决过程如下:首先在窗体上,创建一个l

  • VS2015在升级到Update2之后运行Cordova项目异常的解决方案

    VS2015在升级到Update2之后,有可能出现如下异常,在运行Cordova项目时提示: 查看输出面板会有乱码错误信息: 出现此问题的原因是在于npm程序损坏了.vs调用的npm程序并不是在node安装目录下的npm,而是在: C:\Users\用户名\AppData\Roaming\Microsoft\VisualStudio\MDA\vs-npm\版本号\ 升级完VS之后会发现npm.cmd已经无法启动运行了.所以Cordova项目在运行时,无法使用npm来解析包导致程序无法运行. 解决

  • asp.net服务器上几种常见异常的解决方案.

    如下 (1)配置Asp.net站点ISS报出:服务器应用程序不可用.具体异常信息如下: 服务器应用程序不可用 您试图在此 Web 服务器上访问的 Web 应用程序当前不可用.请点击 Web 浏览器中的"刷新"按钮重试您的请求. 管理员注意事项: 详述此特定请求失败原因的错误信息可在 Web 服务器的系统事件日志中找到.请检查此日志项以查明导致该错误发生的原因. 我检查ISS上其他的配置.发现全部都是Asp编写的网站.属性中查看运行的环境竟是Asp.net Framework 1.1版本

  • MySQL字符集乱码及解决方案分享

    前言 字符集是一套符号和编码的规则,不论是在oracle数据库还是在mysql数据库,都存在字符集的选择问题,而且如果在数据库创建阶段没有正确选择字符集,那么可能在后期需要更换字符集,而字符集的更换是代价比较高的操作,也存在一定的风险,所以,我们推荐在应用开始阶段,就按照需求正确的选择合适的字符集,避免后期不必要的调整. 实战 1.安装MySQL数据库 2.乱码演示 mysql> show variables like 'character_set%'; +-------------------

  • MongoDB查询字段没有创建索引导致的连接超时异常解案例分享

    今天在现场的哥们发来异常,让我解决,错误信息如下: 复制代码 代码如下: HTTP Status 500 - Read operation to server 192.168.1.110:20001 failed on database wpdb; nested exception is com.mongodb.MongoException$Network: Read operation to server 192.168.1.110:20001 failed on database wpdb

  • java程序中的延时加载异常及解决方案

    所谓延时加载就是懒加载(lazy),延迟加载. 什么时候用懒加载呢,我只能回答要用懒加载的时候就用懒加载. 至于为什么要用懒加载呢,就是当我们要访问的数据量过大时,明显用缓存不太合适,因为内存容量有限 ,为了减少并发量,减少系统资源的消耗, 我们让数据在需要的时候才进行加载,这时我们就用到了懒加载.懒加载的异常有很多种,你指的是不是session已经关闭的异常?因为那个初学者最容易遇到情况是当你加载出一个一对多关系的一这端的对象,然后在出了事务之后的地方,比如页面上,还想再获取多端对象里面的除了

随机推荐