Change record status: 
Project: 
Introduced in branch: 
8.x
Description: 

The Guzzle HTTP client has been added to Drupal core.

Introduction

Guzzle supports many features that drupal_http_request() currently does not, like an object oriented and easier to use API, error-handling using exceptions and better support for the HTTP standard. Guzzle also has a modern architecture, which can be extended with plugins to support requesting mocking for tests, caching and much more.

Guzzle can be accessed through the http_client service and the \Drupal::httpClient() helper method, like this $client = \Drupal::httpClient();

This returns an instance of Client. In the typical use case, an instance of GuzzleHttp\Message\RequestInterface is instantiated like this:

$client = \Drupal::httpClient();
$request = $client->createRequest('GET', $feed->url);

Call $response = $client->send($request); to send the request, this method returns an instance of the GuzzleHttp\Message\Response class.

More detailed API documentation and usage examples can be found in the official Guzzle documentation. Note that Drupal currently only includes the base HTTP component of Guzzle.

Example code

7.x

$headers = array('If-Modified-Since' => gmdate(DATE_RFC1123, $last_fetched));
$result = drupal_http_request($feed->url, array('headers' => $headers));
if ($result->code == 200) {
  // Expected result.
  $data = $result->data;
}
else {
  // Error handling.
}

8.x

$client = \Drupal::httpClient();
$request = $client->createRequest('GET', $feed->url);
$request->addHeader('If-Modified-Since', gmdate(DATE_RFC1123, $last_fetched));

try {
  $response = $client->get($feed->uri, [
    'headers' => [
      'If-Modified-Since' => gmdate(DATE_RFC1123, $last_fetched),
    ],
  ]);
  // Expected result.
  // getBody() returns an instance of Psr\Http\Message\StreamInterface.
  // @see http://docs.guzzlephp.org/en/latest/psr7.html#body
  $data = $response->getBody();
}
catch (RequestException $e) {
  watchdog_exception('my_module', $e);
}

In case of an error, guzzle will throw an exception. Some examples include:

- TransferException
-- RequestException
--- BadResponseException
---- ClientException
---- ServerException
--- TooManyRedirectsException

To simply handle and log an error in case something goes wrong, a single catch for RequestException and passing that exception to watchdog_exception() is enough. More specific exception handling is possible by adding additional catch blocks for e.g. the BadResponseException, which has a getResponse method that allows to get and do something with the returned HTTP response.

Impacts: 
Module developers
Updates Done (doc team, etc.)
Online documentation: 
Not done
Theming guide: 
Not done
Module developer documentation: 
Not done
Examples project: 
Not done
Coder Review: 
Not done
Coder Upgrade: 
Not done
Other: 
Other updates done

Comments

JeremyFrench’s picture

Is there a reason we couldn't have put Guzzle behind drupal_http_request?

The logic of including Guzzle is sound, but do we have to change the interface every time we introduce something new.

Crell’s picture

An HTTP client is too complex to be able to throw our own interface around it without cutting off 90% of the functionality. We tried. It's not worth the effort.

If you really want to use an HTTP client other than Guzzle for your own code, it's all just composer libraries so you can pull in your own and use it. They'll co-exist happily.

kentr’s picture

Update: Looks like the page has been updated to reflect this comment, thanks to ingaro.

The D8 code doesn't appear to work anymore. Looks like there's no longer a createRequest() method in Guzzle.

Per the Guzzle Documentation, this should work for the example above:

$client = \Drupal::httpClient();
$response = $client->request('GET', $feed->url, [
    'headers' => [
        'If-Modified-Since', gmdate(DATE_RFC1123, $last_fetched)
    ]
]);

try {
  $response = $client->send($request);
  // Expected result.
  $data = $response->getBody();
}
catch (RequestException $e) {
  watchdog_exception('my_module', $e->getMessage());
}
visabhishek’s picture

Following Code is working for me

 try {
    $response = \Drupal::httpClient()->get($uri, array('headers' => array('Accept' => 'text/plain')));
    $data = (string) $response->getBody();
    if (empty($data)) {
      return FALSE;
    }
  }
  catch (RequestException $e) {
    return FALSE;
  }

http://drupal.stackexchange.com/questions/187697/what-is-the-equivalent-...

dobe’s picture

R.K’s picture

I have a site with an API:
http://www.sitename.com/api/popular!getAsJson.action?period=10days

I have a code:

$url = "http://www.sitename.com/api/popular!getAsJson.action?period=" . $period;
$client = new Client();
$response = $client->request('GET', $url);
json_decode($response->getBody());

but I can`t get the response right, when I enter the url directly on the browser I see the right response.

Please help!

javierlandini’s picture

I think you have to either use the casting operator like:

json_decode((string) $response->getBody());

or use getContents() method as:

json_decode($response->getBody()->getContents());

More details here: http://stackoverflow.com/a/30549372

amoebanath’s picture

As mentioned in one of the other comments, createRequest is no longer a thing. The same comment also suggests that the example has been updated, but actually they still mention createRequest? Is there a reason, or shall I jump in and change it up?
At the same time, I see that it might not make sense to keep a change log up to date with all the subsequent api changes... but the page is so chock-full of useful information for D7 people looking to do an http request in D8 that it would be good to either update or to put a big obvious link to the appropriate documentation.

reuven’s picture

This is in Drupal library in /core/lib/Drupal/Core/Http/TrustedHostsRequestFactory.php
$request = $client->createRequest(); // - creates request
// and after we can
$response = $client->send($request);
$client->request() // - creates response

$response = $client->request('GET',$uri);

nicrodgers’s picture

The d8 code in the docs doesn't seem to work with D8.3, and I've been struggling to find
any examples of making more than a simple get request.

Here's an example of making a POST request, with http authentication, custom headers and
posting form data:

$client = \Drupal::httpClient();
$url = 'http://example.com/something';
$method = 'POST';
$options = [
  'auth' =>
    'username',
    'password',
  ],
 'headers' => [
    'X-My-Example-Header' => 'value',
  ],
  'form_params' => [
    'name' => 'value',
  ],
];
try {
  $response = $client->request($method, $url, $options);
  $code = $response->getStatusCode();
  if ($code == 200) {
    $body = $response->getBody()->getContents();
    return $body;
  }
}
catch (RequestException $e) {
  watchdog_exception('custom_modulename', $e);
}

BartK’s picture

Just wanted to point something out. Since the createRequest() method is no longer available, you can do the same thing this way:

use GuzzleHttp\Psr7\ServerRequest;

/* ... */

foreach($links as $url) {
  $requests[$url] = new ServerRequest('GET', $url); // replaces \Drupal::httpClient()->createRequest()
}

$batch_results = Pool::batch(\Drupal::httpClient(), $requests, ['http_errors' => FALSE]);