使用 Laravel HTTP 客户端的 5 个技巧和窍门
最后更新于 作者: Paul Redmond
作为 Web 开发人员,我们经常需要从 Laravel 应用程序中与 API 交互。在版本 7 中引入的 Laravel HTTP 客户端 提供了围绕 Guzzle HTTP 库的便捷且直观的包装器。在本文中,我们将探讨五个使用 Laravel HTTP 客户端的宝贵技巧,这些技巧可以使您的开发体验更加高效和愉快。
这些技巧包括使用 HTTP 宏、为容器服务配置 HTTP 客户端、可移植的基本 URL 配置、防止测试中的意外请求以及监听 HTTP 事件。通过掌握这些技巧,您可以简化 API 交互并创建更健壮、更易于维护的 Laravel 应用程序。
HTTP 宏
Laravel 的许多服务都具有“宏”功能,允许您为应用程序定义自定义方法。您可以将这些宏添加到服务提供者的 boot()
方法中,而不是从 Laravel 框架扩展核心类。
HTTP 文档 显示了宏的示例,您可以使用它来定义常见的设置
public function boot(): void{ Http::macro('github', function () { return Http::withHeaders([ 'X-Example' => 'example', ])->baseUrl('https://github.com'); });} // Usageresponse = Http::github()->get('/');
宏可以定义您希望在应用程序中定义和重用的任何便捷方法。文档中关于宏的示例涉及到另一个用于配置 HTTP 客户端以便在其他服务中使用的技巧。
我们将在下一节中重新讨论将宏与将客户端传递到其他容器服务相结合。
为容器服务配置 HTTP 客户端
当从 Laravel 应用程序与 API 交互时,您可能希望为客户端设置各种可配置设置。例如,如果 API 具有多个环境,您将需要可配置的基本 URL、令牌、超时设置等。
我们可以利用宏来定义客户端,将客户端表示为我们可以注入到其他服务中的独立服务,或者两者兼而有之。
首先,让我们看看如何在服务提供者的 register()
方法中定义客户端设置
public function register(): void{ $this->app->singleton(ExampleService::class, function (Application $app) { $client = Http::withOptions([ 'base_uri' => config('services.example.base_url'), 'timeout' => config('services.example.timeout', 10), 'connect_timeout' => config('services.example.connect_timeout', 2), ])->withToken(config('services.example.token')); return new ExampleService($client); });}
在单例服务定义中,我们已经连接了一些调用来配置客户端。结果是一个 PendingRequest
实例,我们可以像下面这样将其传递给我们的服务构造函数
class ExampleService{ public function __construct( private PendingRequest $client ) {} public function getWidget(string $uid) { $response = $this->client ->withUrlParameters(['uid' => $uid]) ->get('widget/{uid}'); return new Widget($response->json()); }}
该服务使用 withOptions()
方法直接配置 Guzzle 选项,但我们也可以使用 HTTP 客户端提供的某些便捷方法
$this->app->singleton(ExampleService::class, function (Application $app) { $client = Http::baseUrl(config('services.example.base_url')) ->timeout(config('services.example.timeout', 10)) ->connectTimeout(config('services.example.connect_timeout', 2)) ->withToken(config('services.example.token')); return new ExampleService($client);});
或者,如果您希望将宏与服务结合使用,您可以使用在 AppServiceProvider 的 boot()
方法中定义的宏
$this->app->singleton(ExampleService::class, function (Application $app) { return new ExampleService(Http::github());});
可移植的基本 URL 配置
您可能已经看到默认的基本 URL 包含一个尾部的 /
,因为它根据 RFC 3986 提供了我认为最可移植的选项。
以以下示例服务配置为例(注意默认的 base_url)
return [ 'example' => [ 'base_url' => env('EXAMPLE_BASE_URI', 'https://api.example.com/v1/'), 'token' => env('EXAMPLE_SERVICE_TOKEN'), 'timeout' => env('EXAMPLE_SERVICE_TIMEOUT', 10), 'connect_timeout' => env('EXAMPLE_SERVICE_TIMEOUT', 2), ],];
如果我们的 API 在生产环境和预发布环境中有一个路径前缀 /v1/
,也许它只是 https://stg-api.example.com/
;使用尾部的斜杠可以使 URL 按预期工作,而无需代码更改。与配置尾部的 /
相结合,请注意我的代码中的所有 API 调用都使用相对路径
$this->client ->withUrlParameters(['uid' => $uid]) // Example: // Staging - https://stg-api.example.com/widget/123 // Production - https://api.example.com/v1/widget/123 ->get('widget/{uid}');
查看 Guzzle 的 创建客户端 文档,了解不同的 base_uri 样式如何影响 URI 的解析方式。
防止测试中的意外请求
Laravel 的 HTTP 客户端提供了出色的测试工具,使编写测试变得轻而易举。当我编写与 API 交互的代码时,我感到不安的是,我的测试以某种方式产生了实际的网络请求。使用 Laravel 的 HTTP 客户端 防止意外请求 来解决这个问题
Http::preventStrayRequests(); Http::fake([ 'github.com/*' => Http::response('ok'),]); // Run test code// If any other code triggers an HTTP call via Laravel's client// an exception is thrown.
在我看来,使用 preventStrayRequests()
的最佳方式是在您期望与 API 交互的测试类中定义一个 setUp()
方法。也许您还可以将其添加到应用程序的基本 TestCase
类中
namespace Tests; use Illuminate\Foundation\Testing\TestCase as BaseTestCase;use Illuminate\Support\Facades\Http; abstract class TestCase extends BaseTestCase{ use CreatesApplication; public function setUp(): void { parent::setUp(); Http::preventStrayRequests(); }}
这样做将确保在您的测试套件中触发的每个 HTTP 客户端调用都具有一个假的请求作为支持。使用此方法,我极大地增强了信心,即我已经使用等效的假请求覆盖了测试中的所有出站请求。
HTTP 事件的日志处理程序
Laravel 的 HTTP 客户端具有宝贵的事件,您可以使用这些事件快速进入请求/响应生命周期的重要阶段。在撰写本文时,会触发三个事件
-
Illuminate\Http\Client\Events\RequestSending
-
Illuminate\Http\Client\Events\ResponseReceived
-
Illuminate\Http\Client\Events\ConnectionFailed
假设您希望可视化应用程序发出的每个 URL 请求。我们可以轻松地利用 RequestSending
事件并记录每个请求
namespace App\Listeners; use Illuminate\Contracts\Queue\ShouldQueue;use Illuminate\Queue\InteractsWithQueue;use Illuminate\Support\Facades\Log; class LogRequestSending{ public function handle(object $event): void { Log::debug('HTTP request is being sent.', [ 'url' => $event->request->url(), ]); }}
要使事件处理程序正常工作,请将以下内容添加到 EventServiceProvider
类中
use App\Listeners\LogRequestSending;use Illuminate\Http\Client\Events\RequestSending;// ...protected $listen = [ Registered::class => [ SendEmailVerificationNotification::class, ], RequestSending::class => [ LogRequestSending::class, ],];
一切准备就绪后,您将在日志中看到类似以下内容,用于使用 HTTP 客户端尝试的每个请求
[2023-03-17 04:06:03] local.DEBUG: HTTP request is being sent. {"url":"https://api.example.com/v1/widget/123"}
了解更多
官方的 Laravel HTTP 文档 包含您入门所需的一切。我希望本教程能给您一些灵感和技巧,您可以在 Laravel 应用程序中使用这些技巧