1897 lines
48 KiB
Markdown
1897 lines
48 KiB
Markdown
# HTTP Client
|
||
|
||
* Introduction
|
||
* Making Requests
|
||
* Request Data
|
||
* Headers
|
||
* Authentication
|
||
* Timeout
|
||
* Retries
|
||
* Error Handling
|
||
* Guzzle Middleware
|
||
* Guzzle Options
|
||
* Concurrent Requests
|
||
* Macros
|
||
* Testing
|
||
* Faking Responses
|
||
* Inspecting Requests
|
||
* Preventing Stray Requests
|
||
* Events
|
||
|
||
## Introduction
|
||
|
||
Laravel provides an expressive, minimal API around the [Guzzle HTTP
|
||
client](http://docs.guzzlephp.org/en/stable/), allowing you to quickly make
|
||
outgoing HTTP requests to communicate with other web applications. Laravel's
|
||
wrapper around Guzzle is focused on its most common use cases and a wonderful
|
||
developer experience.
|
||
|
||
## Making Requests
|
||
|
||
To make requests, you may use the `head`, `get`, `post`, `put`, `patch`, and
|
||
`delete` methods provided by the `Http` facade. First, let's examine how to
|
||
make a basic `GET` request to another URL:
|
||
|
||
|
||
|
||
1use Illuminate\Support\Facades\Http;
|
||
|
||
2
|
||
|
||
3$response = Http::get('http://example.com');
|
||
|
||
|
||
use Illuminate\Support\Facades\Http;
|
||
|
||
$response = Http::get('http://example.com');
|
||
|
||
The `get` method returns an instance of `Illuminate\Http\Client\Response`,
|
||
which provides a variety of methods that may be used to inspect the response:
|
||
|
||
|
||
|
||
1$response->body() : string;
|
||
|
||
2$response->json($key = null, $default = null) : mixed;
|
||
|
||
3$response->object() : object;
|
||
|
||
4$response->collect($key = null) : Illuminate\Support\Collection;
|
||
|
||
5$response->resource() : resource;
|
||
|
||
6$response->status() : int;
|
||
|
||
7$response->successful() : bool;
|
||
|
||
8$response->redirect(): bool;
|
||
|
||
9$response->failed() : bool;
|
||
|
||
10$response->clientError() : bool;
|
||
|
||
11$response->header($header) : string;
|
||
|
||
12$response->headers() : array;
|
||
|
||
|
||
$response->body() : string;
|
||
$response->json($key = null, $default = null) : mixed;
|
||
$response->object() : object;
|
||
$response->collect($key = null) : Illuminate\Support\Collection;
|
||
$response->resource() : resource;
|
||
$response->status() : int;
|
||
$response->successful() : bool;
|
||
$response->redirect(): bool;
|
||
$response->failed() : bool;
|
||
$response->clientError() : bool;
|
||
$response->header($header) : string;
|
||
$response->headers() : array;
|
||
|
||
The `Illuminate\Http\Client\Response` object also implements the PHP
|
||
`ArrayAccess` interface, allowing you to access JSON response data directly on
|
||
the response:
|
||
|
||
|
||
|
||
1return Http::get('http://example.com/users/1')['name'];
|
||
|
||
|
||
return Http::get('http://example.com/users/1')['name'];
|
||
|
||
In addition to the response methods listed above, the following methods may be
|
||
used to determine if the response has a specific status code:
|
||
|
||
|
||
|
||
1$response->ok() : bool; // 200 OK
|
||
|
||
2$response->created() : bool; // 201 Created
|
||
|
||
3$response->accepted() : bool; // 202 Accepted
|
||
|
||
4$response->noContent() : bool; // 204 No Content
|
||
|
||
5$response->movedPermanently() : bool; // 301 Moved Permanently
|
||
|
||
6$response->found() : bool; // 302 Found
|
||
|
||
7$response->badRequest() : bool; // 400 Bad Request
|
||
|
||
8$response->unauthorized() : bool; // 401 Unauthorized
|
||
|
||
9$response->paymentRequired() : bool; // 402 Payment Required
|
||
|
||
10$response->forbidden() : bool; // 403 Forbidden
|
||
|
||
11$response->notFound() : bool; // 404 Not Found
|
||
|
||
12$response->requestTimeout() : bool; // 408 Request Timeout
|
||
|
||
13$response->conflict() : bool; // 409 Conflict
|
||
|
||
14$response->unprocessableEntity() : bool; // 422 Unprocessable Entity
|
||
|
||
15$response->tooManyRequests() : bool; // 429 Too Many Requests
|
||
|
||
16$response->serverError() : bool; // 500 Internal Server Error
|
||
|
||
|
||
$response->ok() : bool; // 200 OK
|
||
$response->created() : bool; // 201 Created
|
||
$response->accepted() : bool; // 202 Accepted
|
||
$response->noContent() : bool; // 204 No Content
|
||
$response->movedPermanently() : bool; // 301 Moved Permanently
|
||
$response->found() : bool; // 302 Found
|
||
$response->badRequest() : bool; // 400 Bad Request
|
||
$response->unauthorized() : bool; // 401 Unauthorized
|
||
$response->paymentRequired() : bool; // 402 Payment Required
|
||
$response->forbidden() : bool; // 403 Forbidden
|
||
$response->notFound() : bool; // 404 Not Found
|
||
$response->requestTimeout() : bool; // 408 Request Timeout
|
||
$response->conflict() : bool; // 409 Conflict
|
||
$response->unprocessableEntity() : bool; // 422 Unprocessable Entity
|
||
$response->tooManyRequests() : bool; // 429 Too Many Requests
|
||
$response->serverError() : bool; // 500 Internal Server Error
|
||
|
||
#### URI Templates
|
||
|
||
The HTTP client also allows you to construct request URLs using the [URI
|
||
template specification](https://www.rfc-editor.org/rfc/rfc6570). To define the
|
||
URL parameters that can be expanded by your URI template, you may use the
|
||
`withUrlParameters` method:
|
||
|
||
|
||
|
||
1Http::withUrlParameters([
|
||
|
||
2 'endpoint' => 'https://laravel.com',
|
||
|
||
3 'page' => 'docs',
|
||
|
||
4 'version' => '12.x',
|
||
|
||
5 'topic' => 'validation',
|
||
|
||
6])->get('{+endpoint}/{page}/{version}/{topic}');
|
||
|
||
|
||
Http::withUrlParameters([
|
||
'endpoint' => 'https://laravel.com',
|
||
'page' => 'docs',
|
||
'version' => '12.x',
|
||
'topic' => 'validation',
|
||
])->get('{+endpoint}/{page}/{version}/{topic}');
|
||
|
||
#### Dumping Requests
|
||
|
||
If you would like to dump the outgoing request instance before it is sent and
|
||
terminate the script's execution, you may add the `dd` method to the beginning
|
||
of your request definition:
|
||
|
||
|
||
|
||
1return Http::dd()->get('http://example.com');
|
||
|
||
|
||
return Http::dd()->get('http://example.com');
|
||
|
||
### Request Data
|
||
|
||
Of course, it is common when making `POST`, `PUT`, and `PATCH` requests to
|
||
send additional data with your request, so these methods accept an array of
|
||
data as their second argument. By default, data will be sent using the
|
||
`application/json` content type:
|
||
|
||
|
||
|
||
1use Illuminate\Support\Facades\Http;
|
||
|
||
2
|
||
|
||
3$response = Http::post('http://example.com/users', [
|
||
|
||
4 'name' => 'Steve',
|
||
|
||
5 'role' => 'Network Administrator',
|
||
|
||
6]);
|
||
|
||
|
||
use Illuminate\Support\Facades\Http;
|
||
|
||
$response = Http::post('http://example.com/users', [
|
||
'name' => 'Steve',
|
||
'role' => 'Network Administrator',
|
||
]);
|
||
|
||
#### GET Request Query Parameters
|
||
|
||
When making `GET` requests, you may either append a query string to the URL
|
||
directly or pass an array of key / value pairs as the second argument to the
|
||
`get` method:
|
||
|
||
|
||
|
||
1$response = Http::get('http://example.com/users', [
|
||
|
||
2 'name' => 'Taylor',
|
||
|
||
3 'page' => 1,
|
||
|
||
4]);
|
||
|
||
|
||
$response = Http::get('http://example.com/users', [
|
||
'name' => 'Taylor',
|
||
'page' => 1,
|
||
]);
|
||
|
||
Alternatively, the `withQueryParameters` method may be used:
|
||
|
||
|
||
|
||
1Http::retry(3, 100)->withQueryParameters([
|
||
|
||
2 'name' => 'Taylor',
|
||
|
||
3 'page' => 1,
|
||
|
||
4])->get('http://example.com/users');
|
||
|
||
|
||
Http::retry(3, 100)->withQueryParameters([
|
||
'name' => 'Taylor',
|
||
'page' => 1,
|
||
])->get('http://example.com/users');
|
||
|
||
#### Sending Form URL Encoded Requests
|
||
|
||
If you would like to send data using the `application/x-www-form-urlencoded`
|
||
content type, you should call the `asForm` method before making your request:
|
||
|
||
|
||
|
||
1$response = Http::asForm()->post('http://example.com/users', [
|
||
|
||
2 'name' => 'Sara',
|
||
|
||
3 'role' => 'Privacy Consultant',
|
||
|
||
4]);
|
||
|
||
|
||
$response = Http::asForm()->post('http://example.com/users', [
|
||
'name' => 'Sara',
|
||
'role' => 'Privacy Consultant',
|
||
]);
|
||
|
||
#### Sending a Raw Request Body
|
||
|
||
You may use the `withBody` method if you would like to provide a raw request
|
||
body when making a request. The content type may be provided via the method's
|
||
second argument:
|
||
|
||
|
||
|
||
1$response = Http::withBody(
|
||
|
||
2 base64_encode($photo), 'image/jpeg'
|
||
|
||
3)->post('http://example.com/photo');
|
||
|
||
|
||
$response = Http::withBody(
|
||
base64_encode($photo), 'image/jpeg'
|
||
)->post('http://example.com/photo');
|
||
|
||
#### Multi-Part Requests
|
||
|
||
If you would like to send files as multi-part requests, you should call the
|
||
`attach` method before making your request. This method accepts the name of
|
||
the file and its contents. If needed, you may provide a third argument which
|
||
will be considered the file's filename, while a fourth argument may be used to
|
||
provide headers associated with the file:
|
||
|
||
|
||
|
||
1$response = Http::attach(
|
||
|
||
2 'attachment', file_get_contents('photo.jpg'), 'photo.jpg', ['Content-Type' => 'image/jpeg']
|
||
|
||
3)->post('http://example.com/attachments');
|
||
|
||
|
||
$response = Http::attach(
|
||
'attachment', file_get_contents('photo.jpg'), 'photo.jpg', ['Content-Type' => 'image/jpeg']
|
||
)->post('http://example.com/attachments');
|
||
|
||
Instead of passing the raw contents of a file, you may pass a stream resource:
|
||
|
||
|
||
|
||
1$photo = fopen('photo.jpg', 'r');
|
||
|
||
2
|
||
|
||
3$response = Http::attach(
|
||
|
||
4 'attachment', $photo, 'photo.jpg'
|
||
|
||
5)->post('http://example.com/attachments');
|
||
|
||
|
||
$photo = fopen('photo.jpg', 'r');
|
||
|
||
$response = Http::attach(
|
||
'attachment', $photo, 'photo.jpg'
|
||
)->post('http://example.com/attachments');
|
||
|
||
### Headers
|
||
|
||
Headers may be added to requests using the `withHeaders` method. This
|
||
`withHeaders` method accepts an array of key / value pairs:
|
||
|
||
|
||
|
||
1$response = Http::withHeaders([
|
||
|
||
2 'X-First' => 'foo',
|
||
|
||
3 'X-Second' => 'bar'
|
||
|
||
4])->post('http://example.com/users', [
|
||
|
||
5 'name' => 'Taylor',
|
||
|
||
6]);
|
||
|
||
|
||
$response = Http::withHeaders([
|
||
'X-First' => 'foo',
|
||
'X-Second' => 'bar'
|
||
])->post('http://example.com/users', [
|
||
'name' => 'Taylor',
|
||
]);
|
||
|
||
You may use the `accept` method to specify the content type that your
|
||
application is expecting in response to your request:
|
||
|
||
|
||
|
||
1$response = Http::accept('application/json')->get('http://example.com/users');
|
||
|
||
|
||
$response = Http::accept('application/json')->get('http://example.com/users');
|
||
|
||
For convenience, you may use the `acceptJson` method to quickly specify that
|
||
your application expects the `application/json` content type in response to
|
||
your request:
|
||
|
||
|
||
|
||
1$response = Http::acceptJson()->get('http://example.com/users');
|
||
|
||
|
||
$response = Http::acceptJson()->get('http://example.com/users');
|
||
|
||
The `withHeaders` method merges new headers into the request's existing
|
||
headers. If needed, you may replace all of the headers entirely using the
|
||
`replaceHeaders` method:
|
||
|
||
|
||
|
||
1$response = Http::withHeaders([
|
||
|
||
2 'X-Original' => 'foo',
|
||
|
||
3])->replaceHeaders([
|
||
|
||
4 'X-Replacement' => 'bar',
|
||
|
||
5])->post('http://example.com/users', [
|
||
|
||
6 'name' => 'Taylor',
|
||
|
||
7]);
|
||
|
||
|
||
$response = Http::withHeaders([
|
||
'X-Original' => 'foo',
|
||
])->replaceHeaders([
|
||
'X-Replacement' => 'bar',
|
||
])->post('http://example.com/users', [
|
||
'name' => 'Taylor',
|
||
]);
|
||
|
||
### Authentication
|
||
|
||
You may specify basic and digest authentication credentials using the
|
||
`withBasicAuth` and `withDigestAuth` methods, respectively:
|
||
|
||
|
||
|
||
1// Basic authentication...
|
||
|
||
2$response = Http::withBasicAuth('[[email protected]](/cdn-cgi/l/email-protection)', 'secret')->post(/* ... */);
|
||
|
||
3
|
||
|
||
4// Digest authentication...
|
||
|
||
5$response = Http::withDigestAuth('[[email protected]](/cdn-cgi/l/email-protection)', 'secret')->post(/* ... */);
|
||
|
||
|
||
// Basic authentication...
|
||
$response = Http::withBasicAuth('[[email protected]](/cdn-cgi/l/email-protection)', 'secret')->post(/* ... */);
|
||
|
||
// Digest authentication...
|
||
$response = Http::withDigestAuth('[[email protected]](/cdn-cgi/l/email-protection)', 'secret')->post(/* ... */);
|
||
|
||
#### Bearer Tokens
|
||
|
||
If you would like to quickly add a bearer token to the request's
|
||
`Authorization` header, you may use the `withToken` method:
|
||
|
||
|
||
|
||
1$response = Http::withToken('token')->post(/* ... */);
|
||
|
||
|
||
$response = Http::withToken('token')->post(/* ... */);
|
||
|
||
### Timeout
|
||
|
||
The `timeout` method may be used to specify the maximum number of seconds to
|
||
wait for a response. By default, the HTTP client will timeout after 30
|
||
seconds:
|
||
|
||
|
||
|
||
1$response = Http::timeout(3)->get(/* ... */);
|
||
|
||
|
||
$response = Http::timeout(3)->get(/* ... */);
|
||
|
||
If the given timeout is exceeded, an instance of
|
||
`Illuminate\Http\Client\ConnectionException` will be thrown.
|
||
|
||
You may specify the maximum number of seconds to wait while trying to connect
|
||
to a server using the `connectTimeout` method. The default is 10 seconds:
|
||
|
||
|
||
|
||
1$response = Http::connectTimeout(3)->get(/* ... */);
|
||
|
||
|
||
$response = Http::connectTimeout(3)->get(/* ... */);
|
||
|
||
### Retries
|
||
|
||
If you would like the HTTP client to automatically retry the request if a
|
||
client or server error occurs, you may use the `retry` method. The `retry`
|
||
method accepts the maximum number of times the request should be attempted and
|
||
the number of milliseconds that Laravel should wait in between attempts:
|
||
|
||
|
||
|
||
1$response = Http::retry(3, 100)->post(/* ... */);
|
||
|
||
|
||
$response = Http::retry(3, 100)->post(/* ... */);
|
||
|
||
If you would like to manually calculate the number of milliseconds to sleep
|
||
between attempts, you may pass a closure as the second argument to the `retry`
|
||
method:
|
||
|
||
|
||
|
||
1use Exception;
|
||
|
||
2
|
||
|
||
3$response = Http::retry(3, function (int $attempt, Exception $exception) {
|
||
|
||
4 return $attempt * 100;
|
||
|
||
5})->post(/* ... */);
|
||
|
||
|
||
use Exception;
|
||
|
||
$response = Http::retry(3, function (int $attempt, Exception $exception) {
|
||
return $attempt * 100;
|
||
})->post(/* ... */);
|
||
|
||
For convenience, you may also provide an array as the first argument to the
|
||
`retry` method. This array will be used to determine how many milliseconds to
|
||
sleep between subsequent attempts:
|
||
|
||
|
||
|
||
1$response = Http::retry([100, 200])->post(/* ... */);
|
||
|
||
|
||
$response = Http::retry([100, 200])->post(/* ... */);
|
||
|
||
If needed, you may pass a third argument to the `retry` method. The third
|
||
argument should be a callable that determines if the retries should actually
|
||
be attempted. For example, you may wish to only retry the request if the
|
||
initial request encounters an `ConnectionException`:
|
||
|
||
|
||
|
||
1use Exception;
|
||
|
||
2use Illuminate\Http\Client\PendingRequest;
|
||
|
||
3
|
||
|
||
4$response = Http::retry(3, 100, function (Exception $exception, PendingRequest $request) {
|
||
|
||
5 return $exception instanceof ConnectionException;
|
||
|
||
6})->post(/* ... */);
|
||
|
||
|
||
use Exception;
|
||
use Illuminate\Http\Client\PendingRequest;
|
||
|
||
$response = Http::retry(3, 100, function (Exception $exception, PendingRequest $request) {
|
||
return $exception instanceof ConnectionException;
|
||
})->post(/* ... */);
|
||
|
||
If a request attempt fails, you may wish to make a change to the request
|
||
before a new attempt is made. You can achieve this by modifying the request
|
||
argument provided to the callable you provided to the `retry` method. For
|
||
example, you might want to retry the request with a new authorization token if
|
||
the first attempt returned an authentication error:
|
||
|
||
|
||
|
||
1use Exception;
|
||
|
||
2use Illuminate\Http\Client\PendingRequest;
|
||
|
||
3use Illuminate\Http\Client\RequestException;
|
||
|
||
4
|
||
|
||
5$response = Http::withToken($this->getToken())->retry(2, 0, function (Exception $exception, PendingRequest $request) {
|
||
|
||
6 if (! $exception instanceof RequestException || $exception->response->status() !== 401) {
|
||
|
||
7 return false;
|
||
|
||
8 }
|
||
|
||
9
|
||
|
||
10 $request->withToken($this->getNewToken());
|
||
|
||
11
|
||
|
||
12 return true;
|
||
|
||
13})->post(/* ... */);
|
||
|
||
|
||
use Exception;
|
||
use Illuminate\Http\Client\PendingRequest;
|
||
use Illuminate\Http\Client\RequestException;
|
||
|
||
$response = Http::withToken($this->getToken())->retry(2, 0, function (Exception $exception, PendingRequest $request) {
|
||
if (! $exception instanceof RequestException || $exception->response->status() !== 401) {
|
||
return false;
|
||
}
|
||
|
||
$request->withToken($this->getNewToken());
|
||
|
||
return true;
|
||
})->post(/* ... */);
|
||
|
||
If all of the requests fail, an instance of
|
||
`Illuminate\Http\Client\RequestException` will be thrown. If you would like to
|
||
disable this behavior, you may provide a `throw` argument with a value of
|
||
`false`. When disabled, the last response received by the client will be
|
||
returned after all retries have been attempted:
|
||
|
||
|
||
|
||
1$response = Http::retry(3, 100, throw: false)->post(/* ... */);
|
||
|
||
|
||
$response = Http::retry(3, 100, throw: false)->post(/* ... */);
|
||
|
||
If all of the requests fail because of a connection issue, a
|
||
`Illuminate\Http\Client\ConnectionException` will still be thrown even when
|
||
the `throw` argument is set to `false`.
|
||
|
||
### Error Handling
|
||
|
||
Unlike Guzzle's default behavior, Laravel's HTTP client wrapper does not throw
|
||
exceptions on client or server errors (`400` and `500` level responses from
|
||
servers). You may determine if one of these errors was returned using the
|
||
`successful`, `clientError`, or `serverError` methods:
|
||
|
||
|
||
|
||
1// Determine if the status code is >= 200 and < 300...
|
||
|
||
2$response->successful();
|
||
|
||
3
|
||
|
||
4// Determine if the status code is >= 400...
|
||
|
||
5$response->failed();
|
||
|
||
6
|
||
|
||
7// Determine if the response has a 400 level status code...
|
||
|
||
8$response->clientError();
|
||
|
||
9
|
||
|
||
10// Determine if the response has a 500 level status code...
|
||
|
||
11$response->serverError();
|
||
|
||
12
|
||
|
||
13// Immediately execute the given callback if there was a client or server error...
|
||
|
||
14$response->onError(callable $callback);
|
||
|
||
|
||
// 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);
|
||
|
||
#### Throwing Exceptions
|
||
|
||
If you have a response instance and would like to throw an instance of
|
||
`Illuminate\Http\Client\RequestException` if the response status code
|
||
indicates a client or server error, you may use the `throw` or `throwIf`
|
||
methods:
|
||
|
||
|
||
|
||
1use Illuminate\Http\Client\Response;
|
||
|
||
2
|
||
|
||
3$response = Http::post(/* ... */);
|
||
|
||
4
|
||
|
||
5// Throw an exception if a client or server error occurred...
|
||
|
||
6$response->throw();
|
||
|
||
7
|
||
|
||
8// Throw an exception if an error occurred and the given condition is true...
|
||
|
||
9$response->throwIf($condition);
|
||
|
||
10
|
||
|
||
11// Throw an exception if an error occurred and the given closure resolves to true...
|
||
|
||
12$response->throwIf(fn (Response $response) => true);
|
||
|
||
13
|
||
|
||
14// Throw an exception if an error occurred and the given condition is false...
|
||
|
||
15$response->throwUnless($condition);
|
||
|
||
16
|
||
|
||
17// Throw an exception if an error occurred and the given closure resolves to false...
|
||
|
||
18$response->throwUnless(fn (Response $response) => false);
|
||
|
||
19
|
||
|
||
20// Throw an exception if the response has a specific status code...
|
||
|
||
21$response->throwIfStatus(403);
|
||
|
||
22
|
||
|
||
23// Throw an exception unless the response has a specific status code...
|
||
|
||
24$response->throwUnlessStatus(200);
|
||
|
||
25
|
||
|
||
26return $response['user']['id'];
|
||
|
||
|
||
use Illuminate\Http\Client\Response;
|
||
|
||
$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);
|
||
|
||
// Throw an exception if an error occurred and the given closure resolves to true...
|
||
$response->throwIf(fn (Response $response) => true);
|
||
|
||
// Throw an exception if an error occurred and the given condition is false...
|
||
$response->throwUnless($condition);
|
||
|
||
// Throw an exception if an error occurred and the given closure resolves to false...
|
||
$response->throwUnless(fn (Response $response) => false);
|
||
|
||
// Throw an exception if the response has a specific status code...
|
||
$response->throwIfStatus(403);
|
||
|
||
// Throw an exception unless the response has a specific status code...
|
||
$response->throwUnlessStatus(200);
|
||
|
||
return $response['user']['id'];
|
||
|
||
The `Illuminate\Http\Client\RequestException` instance has a public
|
||
`$response` property which will allow you to inspect the returned response.
|
||
|
||
The `throw` method returns the response instance if no error occurred,
|
||
allowing you to chain other operations onto the `throw` method:
|
||
|
||
|
||
|
||
1return Http::post(/* ... */)->throw()->json();
|
||
|
||
|
||
return Http::post(/* ... */)->throw()->json();
|
||
|
||
If you would like to perform some additional logic before the exception is
|
||
thrown, you may pass a closure to the `throw` method. The exception will be
|
||
thrown automatically after the closure is invoked, so you do not need to re-
|
||
throw the exception from within the closure:
|
||
|
||
|
||
|
||
1use Illuminate\Http\Client\Response;
|
||
|
||
2use Illuminate\Http\Client\RequestException;
|
||
|
||
3
|
||
|
||
4return Http::post(/* ... */)->throw(function (Response $response, RequestException $e) {
|
||
|
||
5 // ...
|
||
|
||
6})->json();
|
||
|
||
|
||
use Illuminate\Http\Client\Response;
|
||
use Illuminate\Http\Client\RequestException;
|
||
|
||
return Http::post(/* ... */)->throw(function (Response $response, RequestException $e) {
|
||
// ...
|
||
})->json();
|
||
|
||
By default, `RequestException` messages are truncated to 120 characters when
|
||
logged or reported. To customize or disable this behavior, you may utilize the
|
||
`truncateRequestExceptionsAt` and `dontTruncateRequestExceptions` methods when
|
||
configuring your application's exception handling behavior in your
|
||
`bootstrap/app.php` file:
|
||
|
||
|
||
|
||
1use Illuminate\Foundation\Configuration\Exceptions;
|
||
|
||
2
|
||
|
||
3->withExceptions(function (Exceptions $exceptions) {
|
||
|
||
4 // Truncate request exception messages to 240 characters...
|
||
|
||
5 $exceptions->truncateRequestExceptionsAt(240);
|
||
|
||
6
|
||
|
||
7 // Disable request exception message truncation...
|
||
|
||
8 $exceptions->dontTruncateRequestExceptions();
|
||
|
||
9})
|
||
|
||
|
||
use Illuminate\Foundation\Configuration\Exceptions;
|
||
|
||
->withExceptions(function (Exceptions $exceptions) {
|
||
// Truncate request exception messages to 240 characters...
|
||
$exceptions->truncateRequestExceptionsAt(240);
|
||
|
||
// Disable request exception message truncation...
|
||
$exceptions->dontTruncateRequestExceptions();
|
||
})
|
||
|
||
Alternatively, you may customize the exception truncation behavior per request
|
||
using the `truncateExceptionsAt` method:
|
||
|
||
|
||
|
||
1return Http::truncateExceptionsAt(240)->post(/* ... */);
|
||
|
||
|
||
return Http::truncateExceptionsAt(240)->post(/* ... */);
|
||
|
||
### Guzzle Middleware
|
||
|
||
Since Laravel's HTTP client is powered by Guzzle, you may take advantage of
|
||
[Guzzle Middleware](https://docs.guzzlephp.org/en/stable/handlers-and-
|
||
middleware.html) to manipulate the outgoing request or inspect the incoming
|
||
response. To manipulate the outgoing request, register a Guzzle middleware via
|
||
the `withRequestMiddleware` method:
|
||
|
||
|
||
|
||
1use Illuminate\Support\Facades\Http;
|
||
|
||
2use Psr\Http\Message\RequestInterface;
|
||
|
||
3
|
||
|
||
4$response = Http::withRequestMiddleware(
|
||
|
||
5 function (RequestInterface $request) {
|
||
|
||
6 return $request->withHeader('X-Example', 'Value');
|
||
|
||
7 }
|
||
|
||
8)->get('http://example.com');
|
||
|
||
|
||
use Illuminate\Support\Facades\Http;
|
||
use Psr\Http\Message\RequestInterface;
|
||
|
||
$response = Http::withRequestMiddleware(
|
||
function (RequestInterface $request) {
|
||
return $request->withHeader('X-Example', 'Value');
|
||
}
|
||
)->get('http://example.com');
|
||
|
||
Likewise, you can inspect the incoming HTTP response by registering a
|
||
middleware via the `withResponseMiddleware` method:
|
||
|
||
|
||
|
||
1use Illuminate\Support\Facades\Http;
|
||
|
||
2use Psr\Http\Message\ResponseInterface;
|
||
|
||
3
|
||
|
||
4$response = Http::withResponseMiddleware(
|
||
|
||
5 function (ResponseInterface $response) {
|
||
|
||
6 $header = $response->getHeader('X-Example');
|
||
|
||
7
|
||
|
||
8 // ...
|
||
|
||
9
|
||
|
||
10 return $response;
|
||
|
||
11 }
|
||
|
||
12)->get('http://example.com');
|
||
|
||
|
||
use Illuminate\Support\Facades\Http;
|
||
use Psr\Http\Message\ResponseInterface;
|
||
|
||
$response = Http::withResponseMiddleware(
|
||
function (ResponseInterface $response) {
|
||
$header = $response->getHeader('X-Example');
|
||
|
||
// ...
|
||
|
||
return $response;
|
||
}
|
||
)->get('http://example.com');
|
||
|
||
#### Global Middleware
|
||
|
||
Sometimes, you may want to register a middleware that applies to every
|
||
outgoing request and incoming response. To accomplish this, you may use the
|
||
`globalRequestMiddleware` and `globalResponseMiddleware` methods. Typically,
|
||
these methods should be invoked in the `boot` method of your application's
|
||
`AppServiceProvider`:
|
||
|
||
|
||
|
||
1use Illuminate\Support\Facades\Http;
|
||
|
||
2
|
||
|
||
3Http::globalRequestMiddleware(fn ($request) => $request->withHeader(
|
||
|
||
4 'User-Agent', 'Example Application/1.0'
|
||
|
||
5));
|
||
|
||
6
|
||
|
||
7Http::globalResponseMiddleware(fn ($response) => $response->withHeader(
|
||
|
||
8 'X-Finished-At', now()->toDateTimeString()
|
||
|
||
9));
|
||
|
||
|
||
use Illuminate\Support\Facades\Http;
|
||
|
||
Http::globalRequestMiddleware(fn ($request) => $request->withHeader(
|
||
'User-Agent', 'Example Application/1.0'
|
||
));
|
||
|
||
Http::globalResponseMiddleware(fn ($response) => $response->withHeader(
|
||
'X-Finished-At', now()->toDateTimeString()
|
||
));
|
||
|
||
### Guzzle Options
|
||
|
||
You may specify additional [Guzzle request
|
||
options](http://docs.guzzlephp.org/en/stable/request-options.html) for an
|
||
outgoing request using the `withOptions` method. The `withOptions` method
|
||
accepts an array of key / value pairs:
|
||
|
||
|
||
|
||
1$response = Http::withOptions([
|
||
|
||
2 'debug' => true,
|
||
|
||
3])->get('http://example.com/users');
|
||
|
||
|
||
$response = Http::withOptions([
|
||
'debug' => true,
|
||
])->get('http://example.com/users');
|
||
|
||
#### Global Options
|
||
|
||
To configure default options for every outgoing request, you may utilize the
|
||
`globalOptions` method. Typically, this method should be invoked from the
|
||
`boot` method of your application's `AppServiceProvider`:
|
||
|
||
|
||
|
||
1use Illuminate\Support\Facades\Http;
|
||
|
||
2
|
||
|
||
3/**
|
||
|
||
4 * Bootstrap any application services.
|
||
|
||
5 */
|
||
|
||
6public function boot(): void
|
||
|
||
7{
|
||
|
||
8 Http::globalOptions([
|
||
|
||
9 'allow_redirects' => false,
|
||
|
||
10 ]);
|
||
|
||
11}
|
||
|
||
|
||
use Illuminate\Support\Facades\Http;
|
||
|
||
/**
|
||
* Bootstrap any application services.
|
||
*/
|
||
public function boot(): void
|
||
{
|
||
Http::globalOptions([
|
||
'allow_redirects' => false,
|
||
]);
|
||
}
|
||
|
||
## Concurrent Requests
|
||
|
||
Sometimes, you may wish to make multiple HTTP requests concurrently. In other
|
||
words, you want several requests to be dispatched at the same time instead of
|
||
issuing the requests sequentially. This can lead to substantial performance
|
||
improvements when interacting with slow HTTP APIs.
|
||
|
||
Thankfully, you may accomplish this using the `pool` method. The `pool` method
|
||
accepts a closure which receives an `Illuminate\Http\Client\Pool` instance,
|
||
allowing you to easily add requests to the request pool for dispatching:
|
||
|
||
|
||
|
||
1use Illuminate\Http\Client\Pool;
|
||
|
||
2use Illuminate\Support\Facades\Http;
|
||
|
||
3
|
||
|
||
4$responses = Http::pool(fn (Pool $pool) => [
|
||
|
||
5 $pool->get('http://localhost/first'),
|
||
|
||
6 $pool->get('http://localhost/second'),
|
||
|
||
7 $pool->get('http://localhost/third'),
|
||
|
||
8]);
|
||
|
||
9
|
||
|
||
10return $responses[0]->ok() &&
|
||
|
||
11 $responses[1]->ok() &&
|
||
|
||
12 $responses[2]->ok();
|
||
|
||
|
||
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();
|
||
|
||
As you can see, each response instance can be accessed based on the order it
|
||
was added to the pool. If you wish, you can name the requests using the `as`
|
||
method, which allows you to access the corresponding responses by name:
|
||
|
||
|
||
|
||
1use Illuminate\Http\Client\Pool;
|
||
|
||
2use Illuminate\Support\Facades\Http;
|
||
|
||
3
|
||
|
||
4$responses = Http::pool(fn (Pool $pool) => [
|
||
|
||
5 $pool->as('first')->get('http://localhost/first'),
|
||
|
||
6 $pool->as('second')->get('http://localhost/second'),
|
||
|
||
7 $pool->as('third')->get('http://localhost/third'),
|
||
|
||
8]);
|
||
|
||
9
|
||
|
||
10return $responses['first']->ok();
|
||
|
||
|
||
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();
|
||
|
||
#### Customizing Concurrent Requests
|
||
|
||
The `pool` method cannot be chained with other HTTP client methods such as the
|
||
`withHeaders` or `middleware` methods. If you want to apply custom headers or
|
||
middleware to pooled requests, you should configure those options on each
|
||
request in the pool:
|
||
|
||
|
||
|
||
1use Illuminate\Http\Client\Pool;
|
||
|
||
2use Illuminate\Support\Facades\Http;
|
||
|
||
3
|
||
|
||
4$headers = [
|
||
|
||
5 'X-Example' => 'example',
|
||
|
||
6];
|
||
|
||
7
|
||
|
||
8$responses = Http::pool(fn (Pool $pool) => [
|
||
|
||
9 $pool->withHeaders($headers)->get('http://laravel.test/test'),
|
||
|
||
10 $pool->withHeaders($headers)->get('http://laravel.test/test'),
|
||
|
||
11 $pool->withHeaders($headers)->get('http://laravel.test/test'),
|
||
|
||
12]);
|
||
|
||
|
||
use Illuminate\Http\Client\Pool;
|
||
use Illuminate\Support\Facades\Http;
|
||
|
||
$headers = [
|
||
'X-Example' => 'example',
|
||
];
|
||
|
||
$responses = Http::pool(fn (Pool $pool) => [
|
||
$pool->withHeaders($headers)->get('http://laravel.test/test'),
|
||
$pool->withHeaders($headers)->get('http://laravel.test/test'),
|
||
$pool->withHeaders($headers)->get('http://laravel.test/test'),
|
||
]);
|
||
|
||
## Macros
|
||
|
||
The Laravel HTTP client allows you to define "macros", which can serve as a
|
||
fluent, expressive mechanism to configure common request paths and headers
|
||
when interacting with services throughout your application. To get started,
|
||
you may define the macro within the `boot` method of your application's
|
||
`App\Providers\AppServiceProvider` class:
|
||
|
||
|
||
|
||
1use Illuminate\Support\Facades\Http;
|
||
|
||
2
|
||
|
||
3/**
|
||
|
||
4 * Bootstrap any application services.
|
||
|
||
5 */
|
||
|
||
6public function boot(): void
|
||
|
||
7{
|
||
|
||
8 Http::macro('github', function () {
|
||
|
||
9 return Http::withHeaders([
|
||
|
||
10 'X-Example' => 'example',
|
||
|
||
11 ])->baseUrl('https://github.com');
|
||
|
||
12 });
|
||
|
||
13}
|
||
|
||
|
||
use Illuminate\Support\Facades\Http;
|
||
|
||
/**
|
||
* Bootstrap any application services.
|
||
*/
|
||
public function boot(): void
|
||
{
|
||
Http::macro('github', function () {
|
||
return Http::withHeaders([
|
||
'X-Example' => 'example',
|
||
])->baseUrl('https://github.com');
|
||
});
|
||
}
|
||
|
||
Once your macro has been configured, you may invoke it from anywhere in your
|
||
application to create a pending request with the specified configuration:
|
||
|
||
|
||
|
||
1$response = Http::github()->get('/');
|
||
|
||
|
||
$response = Http::github()->get('/');
|
||
|
||
## Testing
|
||
|
||
Many Laravel services provide functionality to help you easily and
|
||
expressively write tests, and Laravel's HTTP client is no exception. The
|
||
`Http` facade's `fake` method allows you to instruct the HTTP client to return
|
||
stubbed / dummy responses when requests are made.
|
||
|
||
### Faking Responses
|
||
|
||
For example, to instruct the HTTP client to return empty, `200` status code
|
||
responses for every request, you may call the `fake` method with no arguments:
|
||
|
||
|
||
|
||
1use Illuminate\Support\Facades\Http;
|
||
|
||
2
|
||
|
||
3Http::fake();
|
||
|
||
4
|
||
|
||
5$response = Http::post(/* ... */);
|
||
|
||
|
||
use Illuminate\Support\Facades\Http;
|
||
|
||
Http::fake();
|
||
|
||
$response = Http::post(/* ... */);
|
||
|
||
#### Faking Specific URLs
|
||
|
||
Alternatively, you may pass an array to the `fake` method. The array's keys
|
||
should represent URL patterns that you wish to fake and their associated
|
||
responses. The `*` character may be used as a wildcard character. You may use
|
||
the `Http` facade's `response` method to construct stub / fake responses for
|
||
these endpoints:
|
||
|
||
|
||
|
||
1Http::fake([
|
||
|
||
2 // Stub a JSON response for GitHub endpoints...
|
||
|
||
3 'github.com/*' => Http::response(['foo' => 'bar'], 200, $headers),
|
||
|
||
4
|
||
|
||
5 // Stub a string response for Google endpoints...
|
||
|
||
6 'google.com/*' => Http::response('Hello World', 200, $headers),
|
||
|
||
7]);
|
||
|
||
|
||
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),
|
||
]);
|
||
|
||
Any requests made to URLs that have not been faked will actually be executed.
|
||
If you would like to specify a fallback URL pattern that will stub all
|
||
unmatched URLs, you may use a single `*` character:
|
||
|
||
|
||
|
||
1Http::fake([
|
||
|
||
2 // Stub a JSON response for GitHub endpoints...
|
||
|
||
3 'github.com/*' => Http::response(['foo' => 'bar'], 200, ['Headers']),
|
||
|
||
4
|
||
|
||
5 // Stub a string response for all other endpoints...
|
||
|
||
6 '*' => Http::response('Hello World', 200, ['Headers']),
|
||
|
||
7]);
|
||
|
||
|
||
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']),
|
||
]);
|
||
|
||
For convenience, simple string, JSON, and empty responses may be generated by
|
||
providing a string, array, or integer as the response:
|
||
|
||
|
||
|
||
1Http::fake([
|
||
|
||
2 'google.com/*' => 'Hello World',
|
||
|
||
3 'github.com/*' => ['foo' => 'bar'],
|
||
|
||
4 'chatgpt.com/*' => 200,
|
||
|
||
5]);
|
||
|
||
|
||
Http::fake([
|
||
'google.com/*' => 'Hello World',
|
||
'github.com/*' => ['foo' => 'bar'],
|
||
'chatgpt.com/*' => 200,
|
||
]);
|
||
|
||
#### Faking Exceptions
|
||
|
||
Sometimes you may need to test your application's behavior if the HTTP client
|
||
encounters an `Illuminate\Http\Client\ConnectionException` when attempting to
|
||
make a request. You can instruct the HTTP client to throw a connection
|
||
exception using the `failedConnection` method:
|
||
|
||
|
||
|
||
1Http::fake([
|
||
|
||
2 'github.com/*' => Http::failedConnection(),
|
||
|
||
3]);
|
||
|
||
|
||
Http::fake([
|
||
'github.com/*' => Http::failedConnection(),
|
||
]);
|
||
|
||
To test your application's behavior if a
|
||
`Illuminate\Http\Client\RequestException` is thrown, you may use the
|
||
`failedRequest` method:
|
||
|
||
|
||
|
||
1Http::fake([
|
||
|
||
2 'github.com/*' => Http::failedRequest(['code' => 'not_found'], 404),
|
||
|
||
3]);
|
||
|
||
|
||
Http::fake([
|
||
'github.com/*' => Http::failedRequest(['code' => 'not_found'], 404),
|
||
]);
|
||
|
||
#### Faking Response Sequences
|
||
|
||
Sometimes you may need to specify that a single URL should return a series of
|
||
fake responses in a specific order. You may accomplish this using the
|
||
`Http::sequence` method to build the responses:
|
||
|
||
|
||
|
||
1Http::fake([
|
||
|
||
2 // Stub a series of responses for GitHub endpoints...
|
||
|
||
3 'github.com/*' => Http::sequence()
|
||
|
||
4 ->push('Hello World', 200)
|
||
|
||
5 ->push(['foo' => 'bar'], 200)
|
||
|
||
6 ->pushStatus(404),
|
||
|
||
7]);
|
||
|
||
|
||
Http::fake([
|
||
// Stub a series of responses for GitHub endpoints...
|
||
'github.com/*' => Http::sequence()
|
||
->push('Hello World', 200)
|
||
->push(['foo' => 'bar'], 200)
|
||
->pushStatus(404),
|
||
]);
|
||
|
||
When all the responses in a response sequence have been consumed, any further
|
||
requests will cause the response sequence to throw an exception. If you would
|
||
like to specify a default response that should be returned when a sequence is
|
||
empty, you may use the `whenEmpty` method:
|
||
|
||
|
||
|
||
1Http::fake([
|
||
|
||
2 // Stub a series of responses for GitHub endpoints...
|
||
|
||
3 'github.com/*' => Http::sequence()
|
||
|
||
4 ->push('Hello World', 200)
|
||
|
||
5 ->push(['foo' => 'bar'], 200)
|
||
|
||
6 ->whenEmpty(Http::response()),
|
||
|
||
7]);
|
||
|
||
|
||
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()),
|
||
]);
|
||
|
||
If you would like to fake a sequence of responses but do not need to specify a
|
||
specific URL pattern that should be faked, you may use the
|
||
`Http::fakeSequence` method:
|
||
|
||
|
||
|
||
1Http::fakeSequence()
|
||
|
||
2 ->push('Hello World', 200)
|
||
|
||
3 ->whenEmpty(Http::response());
|
||
|
||
|
||
Http::fakeSequence()
|
||
->push('Hello World', 200)
|
||
->whenEmpty(Http::response());
|
||
|
||
#### Fake Callback
|
||
|
||
If you require more complicated logic to determine what responses to return
|
||
for certain endpoints, you may pass a closure to the `fake` method. This
|
||
closure will receive an instance of `Illuminate\Http\Client\Request` and
|
||
should return a response instance. Within your closure, you may perform
|
||
whatever logic is necessary to determine what type of response to return:
|
||
|
||
|
||
|
||
1use Illuminate\Http\Client\Request;
|
||
|
||
2
|
||
|
||
3Http::fake(function (Request $request) {
|
||
|
||
4 return Http::response('Hello World', 200);
|
||
|
||
5});
|
||
|
||
|
||
use Illuminate\Http\Client\Request;
|
||
|
||
Http::fake(function (Request $request) {
|
||
return Http::response('Hello World', 200);
|
||
});
|
||
|
||
### Inspecting Requests
|
||
|
||
When faking responses, you may occasionally wish to inspect the requests the
|
||
client receives in order to make sure your application is sending the correct
|
||
data or headers. You may accomplish this by calling the `Http::assertSent`
|
||
method after calling `Http::fake`.
|
||
|
||
The `assertSent` method accepts a closure which will receive an
|
||
`Illuminate\Http\Client\Request` instance and should return a boolean value
|
||
indicating if the request matches your expectations. In order for the test to
|
||
pass, at least one request must have been issued matching the given
|
||
expectations:
|
||
|
||
|
||
|
||
1use Illuminate\Http\Client\Request;
|
||
|
||
2use Illuminate\Support\Facades\Http;
|
||
|
||
3
|
||
|
||
4Http::fake();
|
||
|
||
5
|
||
|
||
6Http::withHeaders([
|
||
|
||
7 'X-First' => 'foo',
|
||
|
||
8])->post('http://example.com/users', [
|
||
|
||
9 'name' => 'Taylor',
|
||
|
||
10 'role' => 'Developer',
|
||
|
||
11]);
|
||
|
||
12
|
||
|
||
13Http::assertSent(function (Request $request) {
|
||
|
||
14 return $request->hasHeader('X-First', 'foo') &&
|
||
|
||
15 $request->url() == 'http://example.com/users' &&
|
||
|
||
16 $request['name'] == 'Taylor' &&
|
||
|
||
17 $request['role'] == 'Developer';
|
||
|
||
18});
|
||
|
||
|
||
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';
|
||
});
|
||
|
||
If needed, you may assert that a specific request was not sent using the
|
||
`assertNotSent` method:
|
||
|
||
|
||
|
||
1use Illuminate\Http\Client\Request;
|
||
|
||
2use Illuminate\Support\Facades\Http;
|
||
|
||
3
|
||
|
||
4Http::fake();
|
||
|
||
5
|
||
|
||
6Http::post('http://example.com/users', [
|
||
|
||
7 'name' => 'Taylor',
|
||
|
||
8 'role' => 'Developer',
|
||
|
||
9]);
|
||
|
||
10
|
||
|
||
11Http::assertNotSent(function (Request $request) {
|
||
|
||
12 return $request->url() === 'http://example.com/posts';
|
||
|
||
13});
|
||
|
||
|
||
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';
|
||
});
|
||
|
||
You may use the `assertSentCount` method to assert how many requests were
|
||
"sent" during the test:
|
||
|
||
|
||
|
||
1Http::fake();
|
||
|
||
2
|
||
|
||
3Http::assertSentCount(5);
|
||
|
||
|
||
Http::fake();
|
||
|
||
Http::assertSentCount(5);
|
||
|
||
Or, you may use the `assertNothingSent` method to assert that no requests were
|
||
sent during the test:
|
||
|
||
|
||
|
||
1Http::fake();
|
||
|
||
2
|
||
|
||
3Http::assertNothingSent();
|
||
|
||
|
||
Http::fake();
|
||
|
||
Http::assertNothingSent();
|
||
|
||
#### Recording Requests / Responses
|
||
|
||
You may use the `recorded` method to gather all requests and their
|
||
corresponding responses. The `recorded` method returns a collection of arrays
|
||
that contains instances of `Illuminate\Http\Client\Request` and
|
||
`Illuminate\Http\Client\Response`:
|
||
|
||
|
||
|
||
1Http::fake([
|
||
|
||
2 'https://laravel.com' => Http::response(status: 500),
|
||
|
||
3 'https://nova.laravel.com/' => Http::response(),
|
||
|
||
4]);
|
||
|
||
5
|
||
|
||
6Http::get('https://laravel.com');
|
||
|
||
7Http::get('https://nova.laravel.com/');
|
||
|
||
8
|
||
|
||
9$recorded = Http::recorded();
|
||
|
||
10
|
||
|
||
11[$request, $response] = $recorded[0];
|
||
|
||
|
||
Http::fake([
|
||
'https://laravel.com' => Http::response(status: 500),
|
||
'https://nova.laravel.com/' => Http::response(),
|
||
]);
|
||
|
||
Http::get('https://laravel.com');
|
||
Http::get('https://nova.laravel.com/');
|
||
|
||
$recorded = Http::recorded();
|
||
|
||
[$request, $response] = $recorded[0];
|
||
|
||
Additionally, the `recorded` method accepts a closure which will receive an
|
||
instance of `Illuminate\Http\Client\Request` and
|
||
`Illuminate\Http\Client\Response` and may be used to filter request / response
|
||
pairs based on your expectations:
|
||
|
||
|
||
|
||
1use Illuminate\Http\Client\Request;
|
||
|
||
2use Illuminate\Http\Client\Response;
|
||
|
||
3
|
||
|
||
4Http::fake([
|
||
|
||
5 'https://laravel.com' => Http::response(status: 500),
|
||
|
||
6 'https://nova.laravel.com/' => Http::response(),
|
||
|
||
7]);
|
||
|
||
8
|
||
|
||
9Http::get('https://laravel.com');
|
||
|
||
10Http::get('https://nova.laravel.com/');
|
||
|
||
11
|
||
|
||
12$recorded = Http::recorded(function (Request $request, Response $response) {
|
||
|
||
13 return $request->url() !== 'https://laravel.com' &&
|
||
|
||
14 $response->successful();
|
||
|
||
15});
|
||
|
||
|
||
use Illuminate\Http\Client\Request;
|
||
use Illuminate\Http\Client\Response;
|
||
|
||
Http::fake([
|
||
'https://laravel.com' => Http::response(status: 500),
|
||
'https://nova.laravel.com/' => Http::response(),
|
||
]);
|
||
|
||
Http::get('https://laravel.com');
|
||
Http::get('https://nova.laravel.com/');
|
||
|
||
$recorded = Http::recorded(function (Request $request, Response $response) {
|
||
return $request->url() !== 'https://laravel.com' &&
|
||
$response->successful();
|
||
});
|
||
|
||
### Preventing Stray Requests
|
||
|
||
If you would like to ensure that all requests sent via the HTTP client have
|
||
been faked throughout your individual test or complete test suite, you can
|
||
call the `preventStrayRequests` method. After calling this method, any
|
||
requests that do not have a corresponding fake response will throw an
|
||
exception rather than making the actual HTTP request:
|
||
|
||
|
||
|
||
1use Illuminate\Support\Facades\Http;
|
||
|
||
2
|
||
|
||
3Http::preventStrayRequests();
|
||
|
||
4
|
||
|
||
5Http::fake([
|
||
|
||
6 'github.com/*' => Http::response('ok'),
|
||
|
||
7]);
|
||
|
||
8
|
||
|
||
9// An "ok" response is returned...
|
||
|
||
10Http::get('https://github.com/laravel/framework');
|
||
|
||
11
|
||
|
||
12// An exception is thrown...
|
||
|
||
13Http::get('https://laravel.com');
|
||
|
||
|
||
use Illuminate\Support\Facades\Http;
|
||
|
||
Http::preventStrayRequests();
|
||
|
||
Http::fake([
|
||
'github.com/*' => Http::response('ok'),
|
||
]);
|
||
|
||
// An "ok" response is returned...
|
||
Http::get('https://github.com/laravel/framework');
|
||
|
||
// An exception is thrown...
|
||
Http::get('https://laravel.com');
|
||
|
||
Sometimes, you may wish to prevent most stray requests while still allowing
|
||
specific requests to execute. To accomplish this, you may pass an array of URL
|
||
patterns to the `allowStrayRequests` method. Any request matching one of the
|
||
given patterns will be allowed, while all other requests will continue to
|
||
throw an exception:
|
||
|
||
|
||
|
||
1use Illuminate\Support\Facades\Http;
|
||
|
||
2
|
||
|
||
3Http::preventStrayRequests();
|
||
|
||
4
|
||
|
||
5Http::allowStrayRequests([
|
||
|
||
6 'http://127.0.0.1:5000/*',
|
||
|
||
7]);
|
||
|
||
8
|
||
|
||
9// This request is executed...
|
||
|
||
10Http::get('http://127.0.0.1:5000/generate');
|
||
|
||
11
|
||
|
||
12// An exception is thrown...
|
||
|
||
13Http::get('https://laravel.com');
|
||
|
||
|
||
use Illuminate\Support\Facades\Http;
|
||
|
||
Http::preventStrayRequests();
|
||
|
||
Http::allowStrayRequests([
|
||
'http://127.0.0.1:5000/*',
|
||
]);
|
||
|
||
// This request is executed...
|
||
Http::get('http://127.0.0.1:5000/generate');
|
||
|
||
// An exception is thrown...
|
||
Http::get('https://laravel.com');
|
||
|
||
## Events
|
||
|
||
Laravel fires three events during the process of sending HTTP requests. The
|
||
`RequestSending` event is fired prior to a request being sent, while the
|
||
`ResponseReceived` event is fired after a response is received for a given
|
||
request. The `ConnectionFailed` event is fired if no response is received for
|
||
a given request.
|
||
|
||
The `RequestSending` and `ConnectionFailed` events both contain a public
|
||
`$request` property that you may use to inspect the
|
||
`Illuminate\Http\Client\Request` instance. Likewise, the `ResponseReceived`
|
||
event contains a `$request` property as well as a `$response` property which
|
||
may be used to inspect the `Illuminate\Http\Client\Response` instance. You may
|
||
create [event listeners](/docs/12.x/events) for these events within your
|
||
application:
|
||
|
||
|
||
|
||
1use Illuminate\Http\Client\Events\RequestSending;
|
||
|
||
2
|
||
|
||
3class LogRequest
|
||
|
||
4{
|
||
|
||
5 /**
|
||
|
||
6 * Handle the event.
|
||
|
||
7 */
|
||
|
||
8 public function handle(RequestSending $event): void
|
||
|
||
9 {
|
||
|
||
10 // $event->request ...
|
||
|
||
11 }
|
||
|
||
12}
|
||
|
||
|
||
use Illuminate\Http\Client\Events\RequestSending;
|
||
|
||
class LogRequest
|
||
{
|
||
/**
|
||
* Handle the event.
|
||
*/
|
||
public function handle(RequestSending $event): void
|
||
{
|
||
// $event->request ...
|
||
}
|
||
}
|
||
|