donhatptit / laravel-responder
一个用于构建API响应的Laravel Fractal包,为您提供Fractal的强大功能和Laravel的优雅。
Requires
- php: ^7.0
- illuminate/contracts: 5.1.* || 5.2.* || 5.3.* || 5.4.* || 5.5.* || 5.6.* || 5.7.* || 5.8.* || ^6.0 || ^7.0
- illuminate/support: 5.1.* || 5.2.* || 5.3.* || 5.4.* || 5.5.* || 5.6.* || 5.7.* || 5.8.* || ^6.0 || ^7.0
- league/fractal: ^0.17.0
Requires (Dev)
- doctrine/dbal: ^2.5
- fzaninotto/faker: ^1.6
- illuminate/database: 5.1.* || 5.2.* || 5.3.* || 5.4.* || 5.5.* || 5.6.* || 5.7.* || 5.8.* || ^6.0 || ^7.0
- mockery/mockery: ^0.9.5 || ^1.0
- orchestra/testbench: ~4.0 || ~5.0
- phpunit/phpunit: ^8.5 || ^9.0
- dev-master
- v3.1.1
- v3.1.0
- v3.0.7
- v3.0.6
- v3.0.5
- v3.0.4
- v3.0.3
- v3.0.2
- v3.0.1
- v3.0.0
- v2.x-dev
- v2.0.14
- v2.0.13
- v2.0.12
- v2.0.11
- v2.0.10
- v2.0.9
- v2.0.8
- v2.0.7
- v2.0.6
- v2.0.5
- v2.0.4
- v2.0.3
- v2.0.2
- v2.0.1
- v2.0.0
- v1.2.25
- v1.2.24
- v1.2.23
- v1.2.22
- v1.2.21
- v1.2.20
- v1.2.19
- v1.2.18
- v1.2.17
- v1.2.16
- v1.2.15
- v1.2.14
- v1.2.13
- v1.2.12
- v1.2.11
- v1.2.10
- v1.2.9
- v1.2.8
- v1.2.7
- v1.2.6
- v1.2.5
- v1.2.4
- v1.2.3
- v1.2.2
- v1.2.1
- v1.2.0
- v1.1.3
- v1.1.2
- v1.1.1
- v1.1.0
- v1.0.18
- v1.0.17
- v1.0.16
- v1.0.15
- v1.0.14
- v1.0.13
- v1.0.12
- v1.0.11
- v1.0.10
- v1.0.9
- v1.0.8
- v1.0.7
- v1.0.6
- v1.0.5
- v1.0.4
- v1.0.3
- v1.0.2
- v1.0.1
- v1.0.0
- v0.3.0
- v0.2.26
- v0.2.25
- v0.2.24
- v0.2.23
- v0.2.22
- v0.2.21
- v0.2.20
- v0.2.19
- v0.2.18
- v0.2.17
- v0.2.16
- v0.2.15
- v0.2.14
- v0.2.13
- v0.2.12
- v0.2.11
- v0.2.10
- v0.2.9
- v0.2.8
- v0.2.7
- v0.2.6
- v0.2.5
- v0.2.4
- v0.2.3
- v0.2.2
- v0.2.1
- v0.2.0
- v0.1.9
- v0.1.8
- v0.1.7
- v0.1.6
- v0.1.5
- v0.1.4
- v0.1.3
- v0.1.2
- v0.1.1
- v0.1.0
- dev-feature/api-resources
- dev-dev
- dev-feature-resolve-resource-key
This package is not auto-updated.
Last update: 2024-09-28 04:54:08 UTC
README
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 donhatptit/laravel-responder
Laravel
注册服务提供者
将以下行追加到 config/app.php
中的 providers
键以注册包
Flugg\Responder\ResponderServiceProvider::class,
该包支持自动发现,因此如果您使用Laravel 5.5或更高版本,您可能可以跳过注册服务提供者和外观,因为它们将自动注册。
注册外观 (可选)
如果您喜欢外观,您还可以将 Responder
和 Transformation
外观追加到 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
服务类,它具有 success
和 error
方法,分别用于构建成功和错误响应。要使用该服务并开始创建响应,请选择以下选项之一
选项 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)将在文档的其余部分中使用。
构建响应
success
和 error
方法分别返回 SuccessResponseBuilder
和 ErrorResponseBuilder
,它们都扩展了一个抽象的 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();
您还可以使用 toCollection
和 toJson
方法。
装饰响应
响应装饰器允许在返回响应之前进行最后时刻的更改。该软件包自带两个响应装饰器,将 status
和 success
字段添加到响应输出中。配置文件中的 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
装饰器基于“净化输入,转义输出”的概念,将所有字符串返回的 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不同,您不需要担心创建资源对象,如Item
和Collection
。该软件包将根据数据类型为您创建一个,但是,您可以将数据包装在资源对象中以覆盖此设置。
设置资源键
如果您发送到响应的数据是模型或包含模型列表,则会隐式地从模型的表名解析出资源键。您可以通过向模型添加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();
所有关系都将自动预加载,就像您使用with
或load
预加载Eloquent一样,您可以使用回调来指定额外的查询约束。如下面的示例所示,我们只包括尚未发货的相关运货
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还提供了ArraySerializer
和DataArraySerializer
类。如果您对以上都不满意,可以创建自己的序列化器,通过扩展League\Fractal\Serializer\SerializerAbstract
。您可以在Fractal的文档中了解更多信息。
创建转换器
专门的转换器类为您提供了一个方便的位置来转换数据,并允许您在多个地方重用转换器。它还允许您包含和转换关系。您可以使用Artisan的make:transformer
命令创建转换器
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不同,你可以直接返回数据,而无需将其包装在item
或collection
方法中。
在include方法中执行数据库调用时要小心,因为你可能会对数据库进行意外的查询。
使用Include参数
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来公开数据。你只想返回转换后的数据,而不是整个响应。
可以通过new一个转换器并调用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
方法外,您还可以访问其他转换方法,如without
、only
、meta
和serializer
。
使用toArray
在Responder
服务上几乎与上面的代码相同,但是它还将包括响应装饰器,这可能不是所需的。
不序列化转换
当使用TransformBuilder
转换数据时,它仍将使用配置的序列化器对数据进行序列化。Fractal要求使用序列化器来转换数据,但有时我们只是对原始转换后的数据感兴趣。该包包含一个Flugg\Responder\Serializers\NoopSerializer
来解决这个问题,这是一个无操作序列化器,它不会更改转换后的数据
return $transformation->resource(Product::all())->serializer(NoopSerializer::class)->transform();
如果你认为这看起来很乱,不要担心,有一个更快的方法。事实上,你可能永远都不需要手动利用NoopSerializer
或TransformBuilder
,但了解它是如何工作的会很有帮助。Flugg\Responder\Transformation
是一个可以用于快速不序列化转换数据的类。
选项 1:转换服务
转换类使用 TransformBuilder
类和 NoopSerializer
构建转换。您可以注入转换类并调用 make
来获取一个 TransformBuilder
的实例,这样您就可以访问所有链式方法,包括 with
,如下所示:
public function __construct(Transformation $transformation) { $transformation->make(Product::all())->with('shipments')->transform(); }
选项 2:转换助手
您可以使用 transformation
助手函数进行数据转换而不进行序列化
transformation(Product::all())->with('shipments')->transform();
选项 3:转换外观
您还可以使用转换外观来实现相同的功能
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请求。您可以使用以下命令运行测试
vendor/bin/phpunit
如果您发现错误或有改进建议,请随时在Github上提交issue。但是,如果它与安全问题相关,请通过电子邮件发送到flugged@gmail.com。
捐赠
该包完全免费使用,但是已经投入了很多时间。如果您想通过留下小额捐赠来表示您的感激,您可以点击这里。谢谢!
许可证
Laravel Responder是在MIT许可条款下免费软件。有关详细信息,请参阅license.md。