• Время чтения ~2 мин
  • 17.03.2023

Как веб-разработчикам, нам часто приходится взаимодействовать с API из наших приложений Laravel. HTTP-клиент Laravel, представленный в версии 7, предоставляет удобную и интуитивно понятную оболочку вокруг библиотеки HTTP Guzzle. В этой статье мы рассмотрим пять ценных трюков для работы с HTTP-клиентом Laravel, которые могут сделать ваш опыт разработки более эффективным и приятным.

Эти трюки включают использование http-макросов, настройку HTTP-клиента для служб контейнеров, конфигурацию переносимого базового URL-адреса, предотвращение случайных запросов в тестах и прослушивание событий HTTP. Освоив эти советы, вы сможете упростить взаимодействие с API и создать более надежные и удобные в обслуживании приложения Laravel.

Http Macros Многие сервисы

Laravel имеют функцию «макросов», которая позволяет определять пользовательские методы для вашего приложения. Вместо расширения основных классов из Laravel Framework можно добавить эти макросы boot() в метод поставщика услуг.

В документах HTTP приведен пример макроса, который можно использовать для определения общих параметров:

public function boot(): void
{
    Http::macro('github', function () {
        return Http::withHeaders([
            'X-Example' => 'example',
        ])->baseUrl('https://github.com');
    });
}
// Usage
response = Http::github()->get('/');

Макросы могут определять любые удобные методы, которые вы хотите определить и повторно использовать в приложении. В примере документации по макросам приведен еще один совет по настройке HTTP-клиентов для использования в других службах.

В следующем разделе мы рассмотрим объединение макросов с передачей клиентов другим контейнерным сервисам.

Настройка HTTP-клиента для служб контейнеров

При взаимодействии с API из приложения Laravel вам, вероятно, понадобятся различные настраиваемые параметры для клиента. Например, если 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);
    });
}

в определении службы singleton мы связали несколько вызовов для настройки клиента. В результате получается экземпляр, который мы можем 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);
});

Или, если вы хотите объединить макросы со службами, вы можете использовать макросы, определенные в методе AppServiceProviderboot():

$this->app->singleton(ExampleService::class, function (Application $app) {
    return new ExampleService(Http::github());
});

Конфигурация

переносимого базового URL-адреса Возможно, вы видели, что базовый URL-адрес по умолчанию включал замыкание/, потому что он обеспечивает наибольшую переносимость в моем варианте, согласно RFC 3986.

Возьмем следующий пример конфигурации службы (обратите внимание на base_url по умолчанию):

eturn [
    '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.

Предотвращение случайных запросов в тестах

HTTP-клиент Laravel предоставляет отличные инструменты тестирования, чтобы сделать написание тестов легким. Когда я пишу код, который взаимодействует с API, я чувствую себя неловко, что мои тесты каким-то образом имеют реальные сетевые запросы. Введите предотвращение случайных запросов с помощью HTTP-клиента Laravel:

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() — определить setUp() метод в тестовых классах, которые вы ожидаете взаимодействовать с API. Возможно, вы также можете добавить его в базовый 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-событий

HTTP-клиент Laravel имеет ценные события, которые вы можете использовать для быстрого доступа к основным этапам жизненного цикла запроса /ответа. На момент написания статьи запускаются три события:

  • Illuminate\Http\Client\Events\RequestSending

Illuminate\Http\Client\Events\ResponseReceivedIlluminate\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

Comments

No comments yet
Yurij Finiv

Yurij Finiv

Full stack

Про мене

Professional Fullstack Developer with extensive experience in website and desktop application development. Proficient in a wide range of tools and technologies, including Bootstrap, Tailwind, HTML5, CSS3, PUG, JavaScript, Alpine.js, jQuery, PHP, MODX, and Node.js. Skilled in website development using Symfony, MODX, and Laravel. Experience: Contributed to the development and translation of MODX3 i...

Об авторе CrazyBoy49z
WORK EXPERIENCE
Контакты
Ukraine, Lutsk
+380979856297