offspring/laravel-responder

一个用于构建API响应的Laravel Fractal包,结合了Fractal的强大功能和Laravel的优雅。


README

Latest Stable Version Packagist Downloads Software License Build Status Code Quality Test Coverage Donate

Laravel Responder是一个用于构建API响应的包,它将Fractal集成到Laravel和Lumen中。它可以使用转换器转换数据,创建和序列化成功和错误响应,处理异常,并协助您测试响应。

目录

简介

Laravel允许您直接从控制器方法返回模型以将其转换为JSON。这是一种快速构建API的方法,但会暴露您的数据库列。来自The PHP League的流行PHP包Fractal通过引入转换器解决了这个问题。然而,将其集成到框架中可能会有些繁琐,如下所示

 public function index()
 {
    $resource = new Collection(User::all(), new UserTransformer());

    return response()->json((new Manager)->createData($resource)->toArray());
 }

并不算太糟糕,但我们都被Laravel的魔法所宠坏了。如果我们可以将其重构为

public function index()
{
    return responder()->success(User::all())->respond();
}

该包将允许您做到这一点以及更多。目标是创建一个高质量、感觉像原生Laravel的包。一个让您能够拥抱Fractal的强大功能,同时在美丽抽象的后面隐藏它的包。还投入了大量的关注和思考在文档上。祝您探索愉快!

要求

此包需要

  • PHP 7.0+
  • Laravel 5.1+ 或 Lumen 5.1+

安装

要开始使用,请通过Composer安装该包

composer require flugger/laravel-responder

Laravel

注册服务提供者

将以下行追加到config/app.php中的providers键以注册包

Flugg\Responder\ResponderServiceProvider::class,

该包支持自动发现,因此如果您使用Laravel 5.5或更高版本,您可能可以跳过注册服务提供者和外观,因为它们将自动注册。

注册外观 (可选)

如果您喜欢外观,您还可以将ResponderTransformation外观追加到aliases

'Responder' => Flugg\Responder\Facades\Responder::class,
'Transformation' => Flugg\Responder\Facades\Transformation::class,

发布包资产 (可选)

您还可以使用vendor:publish Artisan命令发布包配置和语言文件

php artisan vendor:publish --provider="Flugg\Responder\ResponderServiceProvider"

这将发布一个位于您的config文件夹中的responder.php配置文件。它还将发布一个位于您的lang/en文件夹中的errors.php文件,可以用于存储错误消息。

Lumen

注册服务提供者

将以下行添加到app/bootstrap.php以注册包

$app->register(Flugg\Responder\ResponderServiceProvider::class);

注册外观 (可选)

您还可以将以下行添加到app/bootstrap.php以注册外观

class_alias(Flugg\Responder\Facades\Responder::class, 'Responder');
class_alias(Flugg\Responder\Facades\Transformation::class, 'Transformation');

发布包资产 (可选)

由于Lumen中没有vendor:publish命令,如果您想配置该包,您将不得不自己创建config/responder.php文件。

使用

本文档假设您对Fractal的工作方式有一些了解。

创建响应

该包有一个Responder服务类,它具有用于分别构建成功和错误响应的successerror方法。要使用该服务并开始创建响应,请选择以下选项之一

选项1:注入Responder服务

您可以直接将 Flugg\Responder\Responder 服务类注入到您的控制器方法中

public function index(Responder $responder)
{
    return $responder->success();
}

您还可以使用 error 方法来创建错误响应

return $responder->error();

选项 2:使用 responder 辅助函数

如果您喜欢 Laravel 的 response 辅助函数,您可能会喜欢 responder 辅助函数

return responder()->success();
return responder()->error();

选项 3:使用 Responder 门面

可选地,您可以使用 Responder 门面来创建响应

return Responder::success();
return Responder::error();

选项 4:使用 MakesResponses 特性

最后,该包提供了一个可以在您的控制器中使用 Flugg\Responder\Http\MakesResponses 特性

return $this->success();
return $this->error();

您选择哪种方式取决于您,它们都是等效的,重要的是保持一致性。辅助函数(选项 2)将在本说明书的剩余部分中使用。

构建响应

successerror 方法分别返回 SuccessResponseBuilderErrorResponseBuilder,这两个都扩展了一个抽象的 ResponseBuilder,赋予它们共同的行为。它们将在从控制器返回时转换为 JSON,但您可以使用 respond 方法显式创建 Illuminate\Http\JsonResponse 实例

return responder()->success()->respond();
return responder()->error()->respond();

默认情况下,状态码设置为 200,但可以通过设置第一个参数来更改。您还可以将一组标题作为第二个参数传递

return responder()->success()->respond(201, ['x-foo' => true]);
return responder()->error()->respond(404, ['x-foo' => false]);

出于一致性的考虑,始终使用 respond 方法。

响应数据类型转换

除了使用 respond 方法将响应转换为 JsonResponse 之外,您还可以将响应数据转换为几种其他类型,例如数组

return responder()->success()->toArray();
return responder()->error()->toArray();

您还有 toCollectiontoJson 方法可供使用。

响应装饰

响应装饰器允许在响应返回之前进行最后时刻的更改。该包自带两个响应装饰器,为响应输出添加了 statussuccess 字段。配置文件中的 decorators 键定义了所有启用的响应装饰器的列表

'decorators' => [
    \Flugg\Responder\Http\Responses\Decorators\StatusCodeDecorator::class,
    \Flugg\Responder\Http\Responses\Decorators\SuccessFlagDecorator::class,
],

您可以通过从列表中删除它来禁用装饰器,或添加自己的装饰器,该装饰器扩展了抽象类 Flugg\Responder\Http\Responses\Decorators\ResponseDecorator。您还可以为每个响应添加额外的装饰器

return responder()->success()->decorator(ExampleDecorator::class)->respond();
return responder()->error()->decorator(ExampleDecorator::class)->respond();

该包还附带了一些默认禁用的情境装饰器,但可以添加到装饰器列表中

  • PrettyPrintDecorator 装饰器将美化 JSON 输出;
\Flugg\Responder\Http\Responses\Decorators\PrettyPrintDecorator::class,
  • EscapeHtmlDecorator 装饰器基于“清理输入,转义输出”的概念,将在您的 API 返回的所有字符串中转义 HTML 实体。您可以将输入数据“原样”安全存储(甚至恶意 HTML 标签),并确信它将被输出为无害的字符串。请注意,使用此装饰器,将数据作为文本打印会导致错误的表示,您必须将其作为 HTML 打印才能检索原始值。
\Flugg\Responder\Http\Responses\Decorators\EscapeHtmlDecorator::class,

创建成功响应

如上简要演示,成功响应是通过使用 success 方法创建的

return responder()->success()->respond();

假设没有更改配置,上面的代码将输出以下 JSON

{
    "status": 200,
    "success": true,
    "data": null
}

设置响应数据

success 方法将响应数据作为第一个参数

return responder()->success(Product::all())->respond();

它接受您通常从控制器返回的相同数据类型,但是它还支持查询构建器和关系实例

return responder()->success(Product::where('id', 1))->respond();
return responder()->success(Product::first()->shipments())->respond();

该包将在幕后运行查询并将它们转换为集合。

转换响应数据

如果已将转换器附加到响应,则响应数据将使用 Fractal 转换。有两种方法可以附加转换器;要么是 显式 在响应上设置它,要么是 隐式 将它绑定到模型。让我们更详细地看看这两种方法。

在响应上设置转换器

您可以通过向 success 方法发送第二个参数来将变压器附加到响应中。例如,下面我们附加了一个简单的闭包变压器,将产品列表转换为只输出其名称。

return responder()->success(Product::all(), function ($product) {
    return ['name' => $product->name];
})->respond();

您还可以使用专门的变压器类进行转换。

return responder()->success(Product::all(), ProductTransformer::class)->respond();
return responder()->success(Product::all(), new ProductTransformer)->respond();

您可以在《创建变压器》章节中了解更多关于创建专用变压器类的内容。

将变压器绑定到模型

如果没有设置变压器,该软件包将在响应数据中搜索实现 Flugg\Responder\Contracts\Transformable 接口的对象,以从其中解析变压器。您可以通过在您的模型中实现 Transformable 接口来利用这一点。

class Product extends Model implements Transformable {}

您可以通过添加返回相应变压器的 transformer 方法来满足此合同。

/**
 * Get a transformer for the class.
 *
 * @return \Flugg\Responder\Transformers\Transformer|string|callable
 */
public function transformer()
{
    return ProductTransformer::class;
}

您不仅可以返回类名字符串,还可以返回变压器实例或闭包变压器,就像 success 方法的第二个参数一样。

不是为所有模型实现 Transformable 合同,另一种方法是使用 TransformerResolver 类上的 bind 方法绑定变压器。您可以将以下代码放置在 AppServiceProvider 或全新的 TransformerServiceProvider 中。

use Flugg\Responder\Contracts\Transformers\TransformerResolver;

public function boot()
{
    $this->app->make(TransformerResolver::class)->bind([
        \App\Product::class => \App\Transformers\ProductTransformer::class,
        \App\Shipment::class => \App\Transformers\ShipmentTransformer::class,
    ]);
}

在将变压器绑定到模型后,您可以省略第二个参数并仍然转换数据。

return responder()->success(Product::all())->respond();

如您所注意到的,与 Fractal 不同,您无需担心创建资源对象,如 ItemCollection。该软件包将根据数据类型为您创建一个,但是,您可以将数据包装在资源对象中以覆盖此设置。

设置资源键

如果您发送到响应中的数据是模型或包含模型列表,则资源键将隐式从模型的表名解析。您可以通过向模型添加 getResourceKey 方法来覆盖此设置。

public function getResourceKey(): string {
    return 'products';
}

您还可以通过向 success 方法发送第三个参数来显式设置资源键。

return responder()->success(Product::all(), ProductTransformer::class, 'products')->respond();

分页响应数据

将分页器发送到 success 方法将设置分页元数据并自动转换数据,以及将任何查询字符串参数附加到分页器链接中。

return responder()->success(Product::paginate())->respond();

假设没有产品并且使用默认配置,JSON 输出将如下所示:

{
    "success": true,
    "status": 200,
    "data": [],
    "pagination": {
        "total": 0,
        "count": 0,
        "perPage": 15,
        "currentPage": 1,
        "totalPages": 1,
        "links": []
    }
}

在响应上设置分页器

您可以将数据和分页器分别设置,就像传统上使用 Fractal 一样。您可以使用 paginator 方法手动设置分页器,该方法期望 League\Fractal\Pagination\IlluminatePaginatorAdapter 实例。

$paginator = Product::paginate();
$adapter = new IlluminatePaginatorAdapter($paginator);

return responder()->success($paginator->getCollection())->paginator($adapter)->respond();

在响应上设置游标

您还可以使用 cursor 方法设置游标,该方法期望 League\Fractal\Pagination\Cursor 实例。

if ($request->has('cursor')) {
    $products = Product::where('id', '>', request()->cursor)->take(request()->limit)->get();
} else {
    $products = Product::take(request()->limit)->get();
}

$cursor = new Cursor(request()->cursor, request()->previous, $products->last()->id ?? null, Product::count());

return responder()->success($products)->cursor($cursor)->respond();

包含关系

如果将变压器类附加到响应中,您可以使用 with 方法包含关系。

return responder()->success(Product::all())->with('shipments')->respond();

您可以使用点表示法发送多个参数并指定嵌套关系。

return responder()->success(Product::all())->with('shipments', 'orders.customer')->respond();

所有关系将自动预加载,就像您使用 Eloquent 的 withload 预加载一样,您可以使用回调来指定额外的查询约束。例如,在下面的示例中,我们只包括尚未发货的相关运输

return responder()->success(Product::all())->with(['shipments' => function ($query) {
    $query->whereNull('shipped_at');
}])->respond();

从查询字符串中包含

如果将 load_relations_parameter 配置键设置为字符串,则关系将从查询字符串参数中加载。默认情况下,它设置为 with,允许您自动从查询字符串中包含关系。

GET /products?with=shipments,orders.customer

排除默认关系

在您的变压器类中,您可以指定要自动加载的关系。您可以使用 without 方法禁用这些关系中的任何一个。

return responder()->success(Product::all())->without('comments')->respond();

过滤转换数据

仅返回所需内容的转换数据过滤技术称为稀疏字段集,可以使用 only 方法指定。

return responder()->success(Product::all())->only('id', 'name')->respond();

当包含关系时,您可能还需要对相关资源上的字段进行筛选。这可以通过指定一个数组来实现,其中每个键代表要筛选的资源的关键字

return responder()->success(Product::all())->with('shipments')->only([
    'products' => ['id', 'name'],
    'shipments' => ['id']
])->respond();

从查询字符串中进行筛选

如果将 filter_fields_parameter 配置键设置为字符串,字段将自动进行筛选。默认值为 only,允许您从查询字符串中筛选字段

GET /products?only=id,name

您可以通过设置参数为基于键的数组来自动筛选相关资源

GET /products?with=shipments&only[products]=id,name&only[shipments]=id

添加元数据

您可能希望将额外的元数据附加到您的响应中。您可以使用 meta 方法来完成此操作

return responder()->success(Product::all())->meta(['count' => Product::count()])->respond();

当使用默认序列化程序时,元数据将简单地追加到响应数组中

{
    "success": true,
    "status": 200,
    "data": [],
    "count": 0
}

序列化响应数据

数据被转换后,它将使用配置文件中指定的成功序列化程序进行序列化,默认为包的 Flugg\Responder\Serializers\SuccessSerializer。您可以使用 serializer 方法在响应中覆盖此设置

return responder()->success()->serializer(JsonApiSerializer::class)->respond();
return responder()->success()->serializer(new JsonApiSerializer())->respond();

上面我们使用了 Fractal 的 JsonApiSerializer 类。Fractal 还附带了一个 ArraySerializerDataArraySerializer 类。如果这些都不符合您的口味,您可以自由地创建自己的序列化程序,通过扩展 League\Fractal\Serializer\SerializerAbstract。您可以在Fractal 的文档中了解更多信息。

创建转换器

一个专门的转换器类为您提供了一个方便的位置来转换数据,并允许您在多个位置重用转换器。它还允许您包含和转换关系。您可以使用 make:transformer Artisan 命令创建转换器

php artisan make:transformer ProductTransformer

该命令将在 app/Transformers 文件夹中生成一个新的 ProductTransformer.php 文件

<?php

namespace App\Transformers;

use App\User;
use Flugg\Responder\Transformers\Transformer;

class ProductTransformer extends Transformer
{
    /**
     * List of available relations.
     *
     * @var string[]
     */
    protected $relations = [];

    /**
     * A list of autoloaded default relations.
     *
     * @var array
     */
    protected $load = [];

    /**
     * Transform the model.
     *
     * @param  \App\Product $product
     * @return array
     */
    public function transform(Product $product): array
    {
        return [
            'id' => (int) $product->id,
        ];
    }
}

它将自动从提供的名称解析模型名称。例如,包将提取 ProductProductTransformer 并假设模型直接位于 app 文件夹中(按照 Laravel 的约定)。如果您将它们存储在其他地方,可以使用 --model(或 -m)选项来覆盖它

php artisan make:transformer ProductTransformer --model="App\Models\Product"

创建普通转换器

上面生成的转换器文件是一个模型转换器,它期望 transform 方法使用 App\Product 模型。然而,我们可以通过应用 --plain(或 -p)修饰符创建一个普通转换器

php artisan make:transformer ProductTransformer --plain

这将从 transform 方法中删除类型提示并添加较少的样板代码。

设置关系

转换器中的 $relations$load 属性与 Fractal 的 $availableIncludes$defaultIncludes 相当。除了轻微的名称更改外,该包使用 $relations$load 属性来映射所有可用的关系以进行预加载,因此与 Fractal 相比,您应该将关系映射到相应的转换器

protected $relations = [
    'shipments' => ShipmentTransformer::class,
];

您可以选择跳过映射,只传递像 Fractal 一样的字符串,但这意味着该包无法自动预加载关系。

设置白名单关系

$relations 属性指定了一个可以包含的关系列表。您可以设置一个映射到相应转换器的关系列表

protected $relations = [
    'shipments' => ShipmentTransformer::class,
    'orders' => OrderTransformer::class,
];

设置自动加载关系

$load 属性指定了一个每次使用转换器转换数据时都要自动加载的关系列表

protected $load = [
    'shipments' => ShipmentTransformer::class,
    'orders' => OrderTransformer::class,
];

您不需要在 $relations$load 中添加关系,$load 中的所有关系都会自然地可用。

包含关系

与 Fractal 要求您为每个包含的关系在转换器中创建一个方法相比,此包允许您在转换模型时跳过此操作,因为它将自动从模型中获取关系。当然,您可以通过创建一个 "include" 方法来覆盖此功能

/**
 * Include related shipments.
 *
 * @param  \App\Product $product
 * @return mixed
 */
public function includeShipments(Product $product)
{
    return $product->shipments;
}

与 Fractal 不同,您可以直接返回数据而无需将其包装在 itemcollection 方法中。

在包含方法中执行数据库调用时,您应该小心,因为您可能会遇到意外的大量数据库访问。

使用包含参数

Fractal 可以解析查询字符串参数,这些参数在包含关系时可以使用。有关如何格式化参数的更多信息,请参阅 Fractal 的参数文档。您可以通过向 "include" 方法添加第二个参数来访问这些参数。

public function includeShipments(Product $product, Collection $parameters)
{
    return $product->shipments->take($parameters->get('limit'));
}

为了尽可能地与 Fractal 解耦,参数(通常使用 League\Fractal\ParamBag 访问)被作为 Laravel 集合访问。

添加查询约束

与使用 with 方法包含关系时可以指定查询约束一样,您也可以在转换器上作为 "load" 方法添加查询约束。这将自动应用于提取预加载关系时。

/**
 * Load shipments with constraints.
 *
 * @param  \Illuminate\Database\Eloquent\Builder $query
 * @return \Illuminate\Database\Eloquent\Builder
 */
public function loadShipments($query)
{
    return $query->whereNull('shipped_at');
}

注意:您不能混合使用 "include" 和 "load" 方法,因为该包不会预加载使用 "include" 方法包含的关系。

过滤关系

在包含一个关系后,您可以使用过滤方法对其进行最后的修改。例如,下面我们正在过滤相关的运输列表,只包括尚未运输的运输

/**
 * Filter included shipments.
 *
 * @param  \Illuminate\Database\Eloquent\Collection $shipments
 * @return \Illuminate\Support\Collection
 */
public function filterShipments($shipments)
{
    return $shipments->filter(function ($shipment) {
        return is_null($shipment->shipped_at);
    });
}

转换数据

我们已经介绍了如何转换成功响应的数据响应,然而,您可能还希望在控制器以外的其他地方转换数据。一个例子是广播事件,您使用 WebSockets 而不是 HTTP 来公开数据。您只需要返回转换后的数据,而不是整个响应。

可以通过创建转换器并调用 transform 来简单地转换数据

return (new ProductTransformer)->transform(Product::first());

但是,当构建包含关系的转换时,这种方法可能会变得有些杂乱。

return array_merge((new ProductTransformer)->transform($product = Product::first()), [
    'shipments' => $product->shipments->map(function ($shipment) {
        return (new ShipmentTransformer)->transform($shipment);
    })
]);

真糟糕!想象一下有多个嵌套关系。让我们探讨一种更简单的方法来处理这个问题。

构建转换

SuccessResponseBuilder 实际上把所有的转换工作委托给了专门的 Flugg\Responder\TransformBuilder 类。我们自己可以使用这个类来转换数据。例如,如果产品运输转换器被绑定到模型上,我们可以以上述方式复制上述代码

public function index(TransformBuilder $transformation)
{
    return $transformation->resource(Product::all())->with('shipments')->transform();
}

我们不是在 Responder 服务上使用 success 方法,而是使用具有相同签名的方法在 TransformBuilder 上使用 resource 方法。我们还使用 transform 来执行转换,而不是像创建响应时那样使用 respond。除了 with 方法,您还可以访问其他转换方法,如 withoutonlymetaserializer

Responder 服务上使用 toArray 几乎与上面的代码相同,但是它还会包括响应装饰器,这可能不是所需的。

不序列化转换

当使用 TransformBuilder 来转换数据时,它仍然会使用配置的序列化程序来序列化数据。Fractal 要求使用序列化程序来转换数据,但有时我们只是对原始转换后的数据感兴趣。该包包含一个 Flugg\Responder\Serializers\NoopSerializer 来解决这个问题,这是一个无操作序列化程序,它不会更改转换后的数据。

return $transformation->resource(Product::all())->serializer(NoopSerializer::class)->transform();

如果您觉得这看起来很乱,不要担心,有一个更快的方法。实际上,您可能永远不需要手动使用 NoopSerializerTransformBuilder,但了解它是如何工作的总是有帮助的。Flugg\Responder\Transformation 是一个类,可以用于快速转换数据而不进行序列化。

选项 1: Transformation 服务

《变换》类使用《变换构建器》类和《空序列化器》构建变换。您可以注入《变换》类并调用《make》来获取《TransformBuilder》实例,该实例提供访问所有链式方法,包括《with》,如下所示

public function __construct(Transformation $transformation)
{
    $transformation->make(Product::all())->with('shipments')->transform();
}

选项 2:使用《transformation》辅助函数

您可以使用《transformation》辅助函数对数据进行变换,而不进行序列化

transformation(Product::all())->with('shipments')->transform();

选项 3:使用《Transformation》外观

您还可以使用《Transformation》外观来实现相同的功能

Transformation::make(Product::all())->with('shipments')->transform();

转换为驼峰式

模型属性通常以蛇形命名,但是,您可能更喜欢在响应字段中使用驼峰式。变换器是转换字段的理想位置,如下例中的《soldOut》字段所示

return responder()->success(Product::all(), function ($product) {
    return ['soldOut' => (bool) $product->sold_out];    
})->respond();

变换请求参数

在以驼峰式响应后,您可能还想让人们使用驼峰式参数发送请求数据。该软件包提供了一个名为《Flugg\Responder\Http\Middleware\ConvertToSnakeCase》的中间件,您可以将其附加到《app/Http/Kernel.php》中的《$middleware》数组中,以自动将所有参数转换为蛇形

protected $middleware = [
    // ...
    \Flugg\Responder\Http\Middleware\ConvertToSnakeCase::class,
];

该中间件将在请求验证之前运行,因此您应该将验证规则也指定为蛇形。

创建错误响应

当您的API消费者做出意外行为时,您可以返回一个描述问题的错误响应。正如前一章简要所示,可以使用《error》方法创建错误响应

return responder()->error()->respond();

错误响应了解错误代码、相应的错误消息以及可选的错误数据。使用默认配置,上述代码将输出以下JSON

{
    "success": false,
    "status": 500,
    "error": {
        "code": null,
        "message": null
    }
}

设置错误代码和消息

您可以将《error》方法的第一参数填充以设置错误代码

return responder()->error('sold_out_error')->respond();

您可以使用整数作为错误代码。

此外,您可以将第二个参数设置为描述错误的错误消息

return responder()->error('sold_out_error', 'The requested product is sold out.')->respond();

在语言文件中设置消息

您可以在语言文件中设置错误消息,这允许以不同语言返回消息。配置文件有一个名为《error_message_files》的键,它定义了一个包含错误消息的语言文件列表。默认情况下,它设置为《['errors']》,这意味着它将在《resources/lang/en》中查找《errors.php》文件。您可以使用这些文件将错误代码映射到相应的错误消息

return [
    'sold_out_error' => 'The requested product is sold out.',
];

使用《ErrorMessageResolver》注册消息

除了使用语言文件之外,您还可以直接在《ErrorMessageResolver》类上设置错误消息。您可以将以下代码放在《AppServiceProvider》或一个全新的《TransformerServiceProvider》中

use Flugg\Responder\ErrorMessageResolver;

public function boot()
{
    $this->app->make(ErrorMessageResolver::class)->register([
        'sold_out_error' => 'The requested product is sold out.',
    ]);
}

添加错误数据

您可能想将其他数据设置在错误响应中。如下例所示,我们正在返回一个包含《sold_out》错误响应的装运列表,向消费者提供有关产品的新装运可能何时到达的信息。

return responder()->error('sold_out')->data(['shipments' => Shipment::all()])->respond();

错误数据将被追加到响应数据中。假设我们正在使用默认序列化器并且数据库中没有装运,上述代码将如下所示

{
    "success": false,
    "status": 500,
    "error": {
        "code": "sold_out",
        "message": "The requested product is sold out.",
        "shipments": []
    }
}

序列化响应数据

类似于成功响应,错误响应将使用配置文件中指定的错误序列化器进行序列化。默认情况下,它默认为包自己的《Flugg\Responder\Serializers\ErrorSerializer》,但当然可以通过使用《serializer》方法进行更改

return responder()->error()->serializer(ExampleErrorSerializer::class)->respond();
return responder()->success()->serializer(new ExampleErrorSerializer())->respond();

您可以通过实现《Flugg\Responder\Contracts\ErrorSerializer》协议来创建自己的错误序列化器。

处理异常

无论我们如何努力避免它们,异常总会发生。优雅地响应异常将提高您的API用户体验。该软件包可以增强您的异常处理器,自动将异常转换为错误响应。如果您想使用此功能,您可以使用软件包的异常处理器或包括以下更详细说明的特质。

选项 1:替换 Handler

要使用该包的异常处理器,您需要替换 app/Exceptions/Handler.php 中的默认导入

use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;

使用包的处理器类

use Flugg\Responder\Exceptions\Handler as ExceptionHandler;

这不能与 Lumen 一起工作,因为它的异常处理器与 Laravel 不兼容。请查看下面的第二个选项。

选项 2:使用 ConvertsExceptions 特性

包的异常处理器使用 Flugg\Responder\Exceptions\ConvertsExceptions 特性来完成大部分工作。您不需要替换异常处理器,而是可以在自己的处理器类中使用该特性。要复制异常处理器的行为,您还必须在 render 方法中添加以下代码

public function render($request, Exception $exception)
{
    $this->convertDefaultException($exception);

    if ($exception instanceof HttpException) {
        return $this->renderResponse($exception);
    }

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

如果您只想在实际请求 JSON 的请求上返回 JSON 错误响应,您可以将上面的代码包裹在 wantsJson 检查中,如下所示

if ($request->wantsJson()) {
    $this->convertDefaultException($exception);

    if ($exception instanceof HttpException) {
        return $this->renderResponse($exception);
    }
}

转换异常

一旦您实现了上述选项之一,该包将把一些 Laravel 异常转换为扩展 Flugg\Responder\Exceptions\Http\HttpException 的异常。然后,它将这些转换为错误响应。下表显示了哪些 Laravel 异常被转换以及它们被转换成什么。所有右侧的异常都在 Flugg\Responder\Exceptions\Http 命名空间下,并扩展 Flugg\Responder\Exceptions\Http\HttpException。所有扩展 HttpException 类的异常都将自动转换为错误响应。

您可以使用 $dontConvert 属性禁用上述某些异常的转换

/**
 * A list of default exception types that should not be converted.
 *
 * @var array
 */
protected $dontConvert = [
    ModelNotFoundException::class,
];

如果您使用的是特性选项,您可以通过在 render 方法中移除对 convertDefaultException 的调用来禁用所有默认转换。

转换自定义异常

除了让包转换 Laravel 异常外,您还可以使用 render 方法中的 convert 方法来转换您的自定义异常

$this->convert($exception, [
    InvalidValueException => PageNotFoundException,
]);

您可以可选地提供一个抛出新异常的闭包,如果您想给它构造函数参数

$this->convert($exception, [
    MaintenanceModeException => function ($exception) {
        throw new ServerDownException($exception->retryAfter);
    },
]);

创建 HTTP 异常

异常类是存储错误信息的一个方便的地方。该包提供了一个抽象异常类 Flugg\Responder\Exceptions\Http\HttpException,它了解状态码、错误代码和错误消息。继续上面的产品示例,我们可以创建自己的 HttpException

<?php

namespace App\Exceptions;

use Flugg\Responder\Exceptions\Http\HttpException;

class SoldOutException extends HttpException
{
    /**
     * The HTTP status code.
     *
     * @var int
     */
    protected $status = 400;

    /**
     * The error code.
     *
     * @var string|null
     */
    protected $errorCode = 'sold_out_error';

    /**
     * The error message.
     *
     * @var string|null
     */
    protected $message = 'The requested product is sold out.';
}

您还可以添加一个返回附加错误数据的 data 方法

/**
 * Retrieve additional error data.
 *
 * @return array|null
 */
public function data()
{
    return [
        'shipments' => Shipment::all()
    ];
}

如果您让包处理异常,现在您可以在应用程序的任何地方抛出异常,它将被自动渲染为错误响应。

throw new SoldOutException();

贡献

欢迎贡献力量,您可以在 Github 上自由创建拉取请求。您可以使用以下命令运行测试

vendor/bin/phpunit

如果您发现错误或有改进建议,请随时在 Github 上提交问题。然而,如果它与安全问题相关,请发送电子邮件到 flugged@gmail.com

捐赠

该包完全免费使用,但是,已经投入了大量的时间来制作它。如果您想通过留下小额捐款来表达感谢,可以通过点击 这里 来做。谢谢!

许可

Laravel Responder 是在 MIT 许可证下免费分发的软件。有关详细信息,请参阅 license.md