Laravel cung cấp một API nhỏ, rõ ràng dựa trên thư viện Guzzle HTTP client, cho phép bạn nhanh chóng thực hiện các HTTP request giao tiếp với các ứng dụng web khác. API này tập trung vào các trường hợp sử dụng phổ biến và giúp tăng trải nghiệm tuyệt vời dành cho nhà phát triển.
Trước khi bắt đầu, bạn nên đảm bảo là bạn đã cài đặt package Guzzle vào trong ứng dụng của bạn. Mặc định, Laravel đã chứa thư viện này. Tuy nhiên, nếu trước đó bạn đã gỡ package này ra rồi, thì bạn có thể cài đặt lại package này qua Composer:
composer require guzzlehttp/guzzle
Để tạo request, bạn có thể sử dụng các phương thức head
, get
, post
, put
, patch
, và delete
được cung cấp bởi facade Http
. Đầu tiên, hãy xem cách tạo ra một request GET
cơ bản to another URL:
use Illuminate\Support\Facades\Http;
$response = Http::get('http://example.com');
Phương thức get
sẽ trả về một instance của Illuminate\Http\Client\Response
và cung cấp nhiều phương thức có thể được sử dụng để kiểm tra response:
$response->body() : string;
$response->json($key = null) : array|mixed;
$response->object() : object;
$response->collect($key = null) : Illuminate\Support\Collection;
$response->status() : int;
$response->ok() : bool;
$response->successful() : bool;
$response->redirect(): bool;
$response->failed() : bool;
$response->serverError() : bool;
$response->clientError() : bool;
$response->header($header) : string;
$response->headers() : array;
Đối tượng Illuminate\Http\Client\Response
cũng được implement từ interface ArrayAccess
của PHP, cho phép bạn truy cập trực tiếp vào dữ liệu JSON trong response:
return Http::get('http://example.com/users/1')['name'];
Nếu bạn muốn dump ra instance request trước khi nó được gửi đi và dừng quá trình chạy lại, bạn có thể thêm phương thức dd
vào đầu định nghĩa request của bạn:
return Http::dd()->get('http://example.com');
Tất nhiên, thông thường khi tạo request POST
, PUT
và PATCH
bạn sẽ cần gửi thêm dữ liệu vào request của bạn, vì thế, các phương thức này sẽ chấp nhận thêm một mảng dữ liệu làm tham số thứ hai của chúng. Mặc định, dữ liệu sẽ được gửi theo kiểu application/json
:
use Illuminate\Support\Facades\Http;
$response = Http::post('http://example.com/users', [
'name' => 'Steve',
'role' => 'Network Administrator',
]);
Khi thực hiện các request GET
, bạn có thể muốn nối một chuỗi query vào sau URL hoặc truyền một mảng gồm các cặp khóa và giá trị làm tham số thứ hai cho phương thức get
:
$response = Http::get('http://example.com/users', [
'name' => 'Taylor',
'page' => 1,
]);
Nếu bạn muốn gửi dữ liệu theo kiểu application/x-www-form-urlencoded
, bạn nên gọi phương thức asForm
trước khi tạo request của bạn:
$response = Http::asForm()->post('http://example.com/users', [
'name' => 'Sara',
'role' => 'Privacy Consultant',
]);
Bạn có thể sử dụng phương thức withBody
nếu bạn muốn đưa vào một nội dung request thô khi tạo request. Content type cũng có thể được cung cấp thông qua tham số thứ hai của phương thức:
$response = Http::withBody(
base64_encode($photo), 'image/jpeg'
)->post('http://example.com/photo');
Nếu bạn muốn gửi một file dưới dạng request multi-part, bạn nên gọi phương thức attach
trước khi tạo request của bạn. Phương thức này chấp nhận tên của file và nội dung của file đó. Nếu cần, bạn cũng có thể cung cấp thêm tham số thứ ba sẽ được coi là filename của file:
$response = Http::attach(
'attachment', file_get_contents('photo.jpg'), 'photo.jpg'
)->post('http://example.com/attachments');
Thay vì truyền nội dung thô của một file, bạn có thể truyền một stream resource:
$photo = fopen('photo.jpg', 'r');
$response = Http::attach(
'attachment', $photo, 'photo.jpg'
)->post('http://test.com/attachments');
Header có thể được thêm vào các request bằng phương thức withHeaders
. Phương thức withHeaders
này chấp nhận một mảng các cặp khóa và giá trị:
$response = Http::withHeaders([
'X-First' => 'foo',
'X-Second' => 'bar'
])->post('http://example.com/users', [
'name' => 'Taylor',
]);
Bạn có thể sử dụng phương thức accept
để chỉ định content type nào mà ứng dụng của bạn mong đợi để đáp ứng yêu cầu của bạn:
$response = Http::accept('application/json')->get('http://example.com/users');
Để thuận tiện, bạn có thể sử dụng phương thức acceptJson
để nhanh chóng xác định rằng ứng dụng của bạn mong đợi content type application/json
sẽ đáp ứng yêu cầu của bạn:
$response = Http::acceptJson()->get('http://example.com/users');
Bạn có thể chỉ định thông tin xác thực là basic authentication hay digest authentication bằng cách sử dụng các phương thức withBasicAuth
và withDigestAuth
:
// Basic authentication...
$response = Http::withBasicAuth('[email protected]', 'secret')->post(...);
// Digest authentication...
$response = Http::withDigestAuth('[email protected]', 'secret')->post(...);
Nếu bạn muốn thêm nhanh header Authorization
bearer token vào trong header Authorization
của request, bạn có thể sử dụng phương thức withToken
:
$response = Http::withToken('token')->post(...);
Phương thức timeout
có thể được sử dụng để chỉ định số giây tối đa có thể chờ một response:
$response = Http::timeout(3)->get(...);
Nếu thời gian chờ bị vượt quá, một instance của Illuminate\Http\Client\ConnectionException
sẽ được đưa ra.
Nếu bạn muốn HTTP client tự động thử lại request nếu xảy ra lỗi ở phía client hoặc ở phía server, bạn có thể sử dụng phương thức retry
. Phương thức retry
sẽ chấp nhận hai tham số: một là số lần request tối đa có thể được thử lại và hai là số mili giây mà Laravel sẽ đợi giữa các lần thử:
$response = Http::retry(3, 100)->post(...);
Nếu cần, bạn có thể truyền tham số thứ ba cho phương thức retry
. Tham số thứ ba phải là một tham số callable để xác định xem có thực sự nên thử lại hay không. Ví dụ: bạn có thể chỉ muốn thử lại request nếu request ban đầu gặp phải lỗi ConnectionException
:
$response = Http::retry(3, 100, function ($exception) {
return $exception instanceof ConnectionException;
})->post(...);
Nếu tất cả các request đều thất bại, thì một instance của Illuminate\Http\Client\RequestException
sẽ được đưa ra.
Không giống như hành vi mặc định của thư viện Guzzle, HTTP client wrapper của Laravel sẽ không đưa ra các ngoại lệ đối với các lỗi client hoặc server (như response 400
và500
từ server). Bạn có thể xác định xem một trong những lỗi này có được trả về hay không bằng cách sử dụng các phương thức successful
, clientError
, hoặc serverError
:
// Determine if the status code is >= 200 and < 300...
$response->successful();
// Determine if the status code is >= 400...
$response->failed();
// Determine if the response has a 400 level status code...
$response->clientError();
// Determine if the response has a 500 level status code...
$response->serverError();
// Immediately execute the given callback if there was a client or server error...
$response->onError(callable $callback);
Nếu bạn có một instance response và muốn đưa ra một instance Illuminate\Http\Client\RequestException
nếu response status code trả về là một lỗi của client hoặc là của server, bạn có thể sử dụng phương thức throw
hoặc throwIf
:
$response = Http::post(...);
// Throw an exception if a client or server error occurred...
$response->throw();
// Throw an exception if an error occurred and the given condition is true...
$response->throwIf($condition);
return $response['user']['id'];
Instance Illuminate\Http\Client\RequestException
có một thuộc tính public là $response
sẽ cho phép bạn kiểm tra response được trả về.
Phương thức throw
sẽ trả về một instance response nếu như không có lỗi xảy ra, cho phép bạn kết hợp các thao tác khác nhau vào phương thức throw
:
return Http::post(...)->throw()->json();
Nếu bạn muốn thực hiện một số logic bổ sung trước khi đưa ra exception, bạn có thể truyền một closure cho phương thức throw
. Exception này sẽ được đưa ra tự động sau khi closure được gọi, do đó bạn không cần phải đưa lại exception này từ bên trong closure:
return Http::post(...)->throw(function ($response, $e) {
//
})->json();
Bạn có thể chỉ định thêm các tuỳ chọn Guzzle request bằng cách sử dụng phương thức withOptions
. Phương thức withOptions
sẽ chấp nhận một mảng gồm các cặp khóa và giá trị:
$response = Http::withOptions([
'debug' => true,
])->get('http://example.com/users');
Thỉnh thoảng, bạn có thể muốn thực hiện nhiều request HTTP cùng một lúc. Nói cách khác, bạn muốn một số request được gửi đi cùng lúc thay vì gửi đi các request một cách tuần tự. Điều này có thể giúp cải thiện hiệu suất đáng kể khi tương tác với các API HTTP chậm.
Rất may, bạn có thể thực hiện việc này bằng phương thức pool
. Phương thức pool
chấp nhận một closure nhận instance Illuminate\Http\Client\Pool
, cho phép bạn dễ dàng thêm request vào một nhóm request để gửi đi:
use Illuminate\Http\Client\Pool;
use Illuminate\Support\Facades\Http;
$responses = Http::pool(fn (Pool $pool) => [
$pool->get('http://localhost/first'),
$pool->get('http://localhost/second'),
$pool->get('http://localhost/third'),
]);
return $responses[0]->ok() &&
$responses[1]->ok() &&
$responses[2]->ok();
Như bạn có thể thấy, mỗi instance response có thể được truy cập dựa trên thứ tự của nó được thêm vào nhóm. Nếu muốn, bạn có thể đặt tên cho các request của bạn bằng phương thức as
, phương thức này cho phép bạn truy cập các response tương ứng theo tên:
use Illuminate\Http\Client\Pool;
use Illuminate\Support\Facades\Http;
$responses = Http::pool(fn (Pool $pool) => [
$pool->as('first')->get('http://localhost/first'),
$pool->as('second')->get('http://localhost/second'),
$pool->as('third')->get('http://localhost/third'),
]);
return $responses['first']->ok();
Laravel HTTP client cho phép bạn định nghĩa "macro", cái mà có thể hoạt động như một cơ chế hiệu quả tuyệt vời để cấu hình các path và các header cho một request chung khi nó tương tác với các service trong ứng dụng của bạn. Để bắt đầu, bạn có thể định nghĩa một macro trong phương thức boot
của class App\Providers\AppServiceProvider
trong ứng dụng của bạn:
use Illuminate\Support\Facades\Http;
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
Http::macro('github', function () {
return Http::withHeaders([
'X-Example' => 'example',
])->baseUrl('https://github.com');
});
}
Khi macro của bạn đã được cấu hình xong, bạn có thể gọi nó từ bất kỳ đâu trong ứng dụng của bạn để tạo ra một pending request với cấu hình đã chỉ định:
$response = Http::github()->get('/');
Nhiều service của Laravel cung cấp các chức năng giúp bạn viết các bài test một cách dễ dàng và rõ ràng, và HTTP client wrapper của Laravel cũng không phải là một ngoại lệ. Phương thức fake
của facade Http
cho phép bạn hướng dẫn HTTP client trả về một response stubbed / dummy khi một request được tạo.
Ví dụ: để hướng dẫn HTTP client trả về một response trống và có status code 200
cho mọi request, bạn có thể gọi phương thức fake
với không tham số truyền vào:
use Illuminate\Support\Facades\Http;
Http::fake();
$response = Http::post(...);
{note} Khi fake một request, HTTP client middleware sẽ không được chạy. Bạn nên định nghĩa các kỳ vọng của bạn đối với các fake request như thể là các middleware này đã được chạy chính xác.
Ngoài ra, bạn cũng có thể truyền một mảng cho phương thức fake
. Các khóa của mảng phải đại diện cho các pattern URL mà bạn muốn làm fake còn các giá trị là các response tương ứng với chúng. Ký tự *
có thể được sử dụng làm ký tự đại diện. Bất kỳ request nào được tạo với một URL không bị fake sẽ được thực thi ngay lập tức. Bạn có thể sử dụng phương thức response
của facade Http
để tạo các response stub / fake cho các endpoint này:
Http::fake([
// Stub a JSON response for GitHub endpoints...
'github.com/*' => Http::response(['foo' => 'bar'], 200, $headers),
// Stub a string response for Google endpoints...
'google.com/*' => Http::response('Hello World', 200, $headers),
]);
Nếu bạn muốn chỉ định một pattern URL dự phòng sẽ được dùng cho tất cả các URL mà chưa khớp với các pattern URL đã cho, bạn có thể sử dụng một ký tự *
để cài đặt chuyện đó:
Http::fake([
// Stub a JSON response for GitHub endpoints...
'github.com/*' => Http::response(['foo' => 'bar'], 200, ['Headers']),
// Stub a string response for all other endpoints...
'*' => Http::response('Hello World', 200, ['Headers']),
]);
Đôi khi bạn có thể cần chỉ định là một URL sẽ trả về một loạt các response fake theo một trình tự cụ thể. Bạn có thể thực hiện điều này bằng cách sử dụng phương thức Http::sequence
để xây dựng các response:
Http::fake([
// Stub a series of responses for GitHub endpoints...
'github.com/*' => Http::sequence()
->push('Hello World', 200)
->push(['foo' => 'bar'], 200)
->pushStatus(404),
]);
Khi tất cả các response trong một trình tự response đã được sử dụng xong, thì bất kỳ request nào khác sẽ khiến trình tự response sẽ đưa ra một ngoại lệ. Nếu bạn muốn chỉ định một response mặc định sẽ được trả về khi một trình chạy xong, bạn có thể sử dụng phương thức whenEmpty
:
Http::fake([
// Stub a series of responses for GitHub endpoints...
'github.com/*' => Http::sequence()
->push('Hello World', 200)
->push(['foo' => 'bar'], 200)
->whenEmpty(Http::response()),
]);
Nếu bạn muốn fake một trình tự response nhưng không muốn chỉ định pattern URL nào sẽ được làm fake, bạn có thể sử dụng phương thức Http::fakeSequence
:
Http::fakeSequence()
->push('Hello World', 200)
->whenEmpty(Http::response());
Nếu bạn yêu cầu một logic phức tạp hơn để xác định response nào sẽ trả về cho một số endpoint nhất định, bạn có thể truyền voà một lệnh closure cho phương thức fake
. Lệnh closure này sẽ nhận vào một instance của Illuminate\Http\Client\Request
và sẽ trả về một instance response. Trong closure của bạn, bạn có thể thực hiện bất kỳ logic nào cần thiết để xác định loại response nào sẽ trả về:
Http::fake(function ($request) {
return Http::response('Hello World', 200);
});
Khi fake response, đôi khi bạn có thể muốn kiểm tra các request mà client nhận được để đảm bảo là ứng dụng của bạn đang gửi dữ liệu hoặc tiêu đề chính xác. Bạn có thể thực hiện điều này bằng cách gọi phương thức Http::assertSent
sau khi gọi Http::fake
.
Phương thức assertSent
sẽ chấp nhận một closure sẽ nhận vào một instance Illuminate\Http\Client\Request
và sẽ trả về một giá trị boolean cho biết là request có phù hợp với mong đợi của bạn hay không. Để pass qua bài test, thì ít nhất một request phải phù hợp với các kỳ vọng mà bạn đưa ra:
use Illuminate\Http\Client\Request;
use Illuminate\Support\Facades\Http;
Http::fake();
Http::withHeaders([
'X-First' => 'foo',
])->post('http://example.com/users', [
'name' => 'Taylor',
'role' => 'Developer',
]);
Http::assertSent(function (Request $request) {
return $request->hasHeader('X-First', 'foo') &&
$request->url() == 'http://example.com/users' &&
$request['name'] == 'Taylor' &&
$request['role'] == 'Developer';
});
Nếu cần thiết, bạn có thể kiểm tra rằng một request sẽ không được gửi đi bằng phương thức assertNotSent
:
use Illuminate\Http\Client\Request;
use Illuminate\Support\Facades\Http;
Http::fake();
Http::post('http://example.com/users', [
'name' => 'Taylor',
'role' => 'Developer',
]);
Http::assertNotSent(function (Request $request) {
return $request->url() === 'http://example.com/posts';
});
Bạn có thể sử dụng phương thức assertSentCount
để kiểm tra có bao nhiêu request đã được "gửi" trong quá trình test:
Http::fake();
Http::assertSentCount(5);
Hoặc, bạn có thể sử dụng phương thức assertNothingSent
để kiểm tra không có request nào được gửi đi trong quá trình test:
Http::fake();
Http::assertNothingSent();
Laravel kích hoạt ba event trong quá trình gửi request HTTP. Event RequestSending
sẽ được kích hoạt trước khi request được gửi đi, trong khi event ResponseReceived
sẽ được kích hoạt sau khi nhận được phản hồi cho một request nhất định. Và event ConnectionFailed
sẽ được kích hoạt nếu không nhận được phản hồi nào cho một request nhất định.
Cả hai event RequestSending
và ConnectionFailed
đều chứa thuộc tính public $request
mà bạn có thể sử dụng để kiểm tra instance Illuminate\Http\Client\Request
. Tương tự, event ResponseReceived
cũng chứa thuộc tính $request
cũng như thuộc tính $response
có thể được sử dụng để kiểm tra instance Illuminate\Http\Client\Response
. Bạn cũng có thể đăng ký event listener cho event này trong service provider App\Providers\EventServiceProvider
của bạn:
/**
* The event listener mappings for the application.
*
* @var array
*/
protected $listen = [
'Illuminate\Http\Client\Events\RequestSending' => [
'App\Listeners\LogRequestSending',
],
'Illuminate\Http\Client\Events\ResponseReceived' => [
'App\Listeners\LogResponseReceived',
],
'Illuminate\Http\Client\Events\ConnectionFailed' => [
'App\Listeners\LogConnectionFailed',
],
];
entry