flugger/laravel-responder

一个用于构建API响应的Laravel Fractal包,结合了Fractal的力量和Laravel的优雅。

v3.4.0 2024-03-04 18:09 UTC

README

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

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

目录

简介

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

 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,
        ];
    }
}

它将自动解析提供的名称中的模型名称。例如,该包将从ProductTransformer中提取Product,并假设模型直接位于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服务

《变换》类使用《TransformBuilder》类通过《NoopSerializer》构建变换。您可以注入《变换》类并调用《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上创建一个pull request。您可以使用以下命令运行测试

vendor/bin/phpunit

如果您发现错误或对改进有建议,请随时在GitHub上提交问题。但是,如果它是安全相关的问题,请通过电子邮件发送到 flugged@gmail.com

捐赠

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

许可

laravel-responder 是在 MIT 许可证下免费分发的软件。有关更多信息,请参阅 license.md