😅Tạo Request HttpClient Tới Server Thật Đơn Giản Với symfony/http-client hoặc guzzlehttp/guzzle

Symfony HttpClient

last modified July 5, 2020

Symfony HttpClient tutorial shows how to create HTTP requests in Symfony with the HttpClient component. The component provides utilities to consume APIs and supports synchronous and asynchronous operations.

For more information, read the official The HttpComponent documentation.

Symfony

Symfony is a set of reusable PHP components and a PHP framework for web projects. Symfony was published as free software in 2005. The original author of Symfony is Fabien Potencier. Symfony was heavily inspired by the Spring Framework.

In the examples, we will use the httpbin.org and http://jsonplaceholder.typicode.com/ online services.

$ composer require symfony/http-client
$ composer require symfony/var-dumper

We install the HttpClient and the var-dumper components.

HttpClient GET request

HTTP defines a set of request methods to indicate the desired action to be performed for a given resource. The GET request is used to request data from a specified resource. Requests using GET should only retrieve data and should have no other effect on the data.

Note: The purpose and impact of the HTTP methods is a recommendation; these are not strict rules. This is why we said earlier that GET method should not have effect on data. In practice, this is not always followed. get_request.php

<?php

require('vendor/autoload.php');

use Symfony\Component\HttpClient\HttpClient;

$httpClient = HttpClient::create();
$response = $httpClient->request('GET', 'http://webcode.me');

$statusCode = $response->getStatusCode();
echo $statusCode . "\n";

$contentType = $response->getHeaders()['content-type'][0];
echo $contentType . "\n";

$content = $response->getContent();
echo $content . "\n";

The example creates the HttpClient and issues a GET request to the specified webpage.

Note: Responses are always asynchronous, so that the call to the method returns immediately instead of waiting to receive the response. There are blocking methods such as getStatusCode() and getContent(), which wait until the full response contents are received.

$httpClient = HttpClient::create();
$response = $httpClient->request('GET', 'http://webcode.me');

We create an HttpClient with HttpClient::create(). A GET request is generated with the request() method.

$statusCode = $response->getStatusCode();
echo $statusCode . "\n";

We get the status code with the getStatusCode() method.

$contentType = $response->getHeaders()['content-type'][0];
echo $contentType . "\n";

From the headers of the response, we get the content type.

$content = $response->getContent();
echo $content . "\n";

Finally, we get the content of the page with the getContent() method.

$ php get_request.php
200
text/html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>My html page</title>
</head>
<body>

    <p>
        Today is a beautiful day. We go swimming and fishing.
    </p>

    <p>
          Hello there. How are you?
    </p>

</body>
</html>

This is the output.

HttpClient user agent

When we create the HttpClient, we can pass some options, such as header values.

user_agent.php

<?php

require('vendor/autoload.php');

use Symfony\Component\HttpClient\HttpClient;

$httpClient = HttpClient::create(['headers' => [
    'User-Agent' => 'PHP console app',
]]);

$response = $httpClient->request('GET', 'https://httpbin.org/user-agent');

echo $response->getContent() . "\n";    

We connect to the httpbin.org website, which is an online tool for testing HTTP requests & responses.

$httpClient = HttpClient::create(['headers' => [
    'User-Agent' => 'PHP console app',
]]);

In the headers array, we add the User-Agent option.

$response = $httpClient->request('GET', 'https://httpbin.org/user-agent');

We send a GET request to the https://httpbin.org/user-agent URL, which returns the user agent option of the request.

HttpClient toArray()

The toArray() method gets the response body decoded as array, typically from a JSON payload.

content_array.php

<?php

require('vendor/autoload.php');

use Symfony\Component\HttpClient\HttpClient;

$httpClient = HttpClient::create();

$response = $httpClient->request('GET', 
        'https://jsonplaceholder.typicode.com/posts/2/');

dump($response->toArray());

The example issues a GET request to the jsonplaceholder.typicode.com online service website, which returns the post with Id 2 in JSON format.

dump($response->toArray());

We dump the output of the toArray() method.

$ php content_array.php
array:4 [
  "userId" => 1
  "id" => 2
  "title" => "qui est esse"
  "body" => """
    est rerum tempore vitae\n
    sequi sint nihil reprehenderit dolor beatae ea dolores neque\n
    fugiat blanditiis voluptate porro vel nihil molestiae ut reiciendis\n
    qui aperiam non debitis possimus qui neque nisi nulla
    """
]

This is the output.

HttpClient POST data

A post request is used to send data to the server. The data is in the request body of the HTTP request.

Post requests are never cached, they do not remain in the browser history, they cannot be bookmarked and there are no restrictions on the data length.

post_data.php

<?php

require('vendor/autoload.php');

use Symfony\Component\HttpClient\HttpClient;

$httpClient = HttpClient::create();

$response = $httpClient->request('POST', 'https://httpbin.org/post', [
    'body' => ['msg' => 'Hello there']
]);

echo $response->getContent();

In the example, we send a message variable to the specified URL in a POST request. In the response object, we find the data that were sent in the POST request.

$ php post_data.php
{
  "args": {},
  "data": "",
  "files": {},
  "form": {
    "msg": "Hello there"
  },
  ...
}

This is the output.

HttpClient redirects

URL redirection is the process of forwarding a request from one page to another. When a web browser attempts to open a URL that has been redirected, a page with a different URL is opened. There may be multiple redirects for a single URL.

Reasons for using redirects:

  • URL shortening

  • preventing broken links when web pages are moved

  • allowing multiple domain names belonging to the same owner to refer to a single web site

  • privacy protection

  • HTTP to HTTPs transition

  • hostile purposes such as phishing attacks or malware distribution

The HttpClient follows redirects, up to a maximum of 20, when making a request. The max_redirects attribute is used to configure this behavior. Value 0 means not to follow any redirect.

redirect.php

<?php

require 'vendor/autoload.php';

use Symfony\Component\HttpClient\HttpClient;

$httpClient = HttpClient::create();

$response = $httpClient->request('GET', 'https://httpbin.org/redirect/4', [
    'max_redirects' => 3,
]);

echo $response->getStatusCode();

We send a GET request to an URL which redirects four times, while setting the max redirection attribute to three. This means we get 302 redirect status code. If we increase the max_redirects value, we should get 200.

HttpClient query parameters

Query parameters are the part of a uniform resource locator (URL) which assigns values to specified parameters. This is one way of sending data to the destination server.

http://example.com/api/users?name=John%20Doe&occupation=gardener

The query parameters are specified after the ? character. Multiple fields are separated with the &. Special characters, such as spaces, are encoded. In the above string, the space is encoded with the %20 value.

Symfony HttpClient automatically encodes values before including them in the URL.

query_params.php

<?php

require('vendor/autoload.php');

use Symfony\Component\HttpClient\HttpClient;

$httpClient = HttpClient::create();

$response = $httpClient->request('GET', 'https://httpbin.org/get', [

    'query' => [
        'name' => 'John Doe',
    ],
]);

echo $response->getContent();

We send a name field to the https://httpbin.org/get URL. In the response we get the URL parameters back.

$ php query_params.php
{
  "args": {
    "name": "John Doe"
  },
  "headers": {
    "Accept-Encoding": "deflate, gzip",
    "Host": "httpbin.org",
    "User-Agent": "Symfony HttpClient/Curl"
  },
  ...
}

This is the output.

Using httpbin's Docker container

The httpbin.org also provides a Docker container for testing.

$ docker run -p 80:80 kennethreitz/httpbin

We run the container.

docker_ex.php

<?php

require 'vendor/autoload.php';

use Symfony\Component\HttpClient\HttpClient;

$httpClient = HttpClient::create();

$response = $httpClient->request('GET', 'http://localhost:80/anything',
    [
        'json' => ['message' => 'Hello there'],
    ]);

dump($response->toArray());

In the example, we connect to the container with the httpbin's service. The localhost:80/anything returns anything that is passed to the request.

HTTP Basic Authentication

HTTP basic authentication is a simple challenge and response mechanism in which a server requests credentials from a client. The client passes the credentials to the server in an Authorization header. The authentication information is not encrypted or hashed in any way. It is in encoded with Base64 algorithm. Therefore, the HTTP Basic Authentication is only considered to be secure when used with HTTPS.

HTTP Basic authentication uses standard fields in the HTTP header, removing the need for handshakes.

authenticate.php

<?php

require('vendor/autoload.php');

use Symfony\Component\HttpClient\HttpClient;

$httpClient = HttpClient::create([
    'auth_basic' => ['user7', 'passwd']
]);

$response = $httpClient->request('GET', 
    'https://httpbin.org/basic-auth/user7/passwd');

echo $response->getStatusCode();

dump($response);

In the example, we use the HTTP Basic authentication.

$httpClient = HttpClient::create([
    'auth_basic' => ['user7', 'passwd']
]);

The HTTP Basic Authentication is specified with the auth_basic option. All requests will use the same credentials.

$response = $httpClient->request('GET', 
  'https://httpbin.org/basic-auth/user7/passwd');

Do not be confused with the user and password in the URL; this is merely for testing in the httpbin's service.

HttpClient stream data

Chunked transfer encoding is a streaming data transfer mechanism available since HTTP 1.1. In chunked transfer encoding, the data stream is divided into a series of non-overlapping chunks.

The chunks are sent out and received independently of one another. Each chunk is preceded by its size in bytes.

In Symfony HttpClient, streaming is done with stream().

streaming.php

<?php

require 'vendor/autoload.php';

use Symfony\Component\HttpClient\HttpClient;

$httpClient = HttpClient::create();

$url = 'https://download.freebsd.org/ftp/releases/amd64/amd64/ISO-IMAGES/12.0/FreeBSD-12.0-RELEASE-amd64-mini-memstick.img';
$response = $httpClient->request('GET', $url, [
    'buffer' => false,
]);

if (200 !== $response->getStatusCode()) {
    throw new \Exception('Failed to create a request');
}

$fileHandler = fopen('freebsd-12.0-amd64-mini-memstick.iso', 'w');
foreach ($httpClient->stream($response) as $chunk) {
    fwrite($fileHandler, $chunk->getContent());
}

In the example, we download a FreeBSD ISO image.

$response = $httpClient->request('GET', $url, [
  'buffer' => false,
]);

We create a GET request to the specified URL; optionally, we can turn off memory buffering.

$fileHandler = fopen('freebsd-12.0-amd64-mini-memstick.iso', 'w');
foreach ($httpClient->stream($response) as $chunk) {
    fwrite($fileHandler, $chunk->getContent());
}

We get the response contents in chunks and save them in a file.

Symfony HttClient webapp example

In the following example, we create a Symfony web application, which uses the HttpClient to generate a request. We use the HttpClientInterface to inject the HttpClient.

The application issues a GET request to the https://jsonplaceholder.typicode.com/users, which returns ten users.

$ composer create-project symfony/skeleton symfapp
$ cd symfapp
$ composer require annotations
$ composer require maker server --dev
$ composer require symfony/http-client

We create a new Symfony skeleton application and install some dependencies.

config/packages/framework.yaml

framework:
...
  http_client:
      max_host_connections: 5
      default_options:
          max_redirects: 3

In the framework.yaml file, we can configure the HttpClient.

$ php bin/console make:controller DataController

We create a new controller.

src/Controller/DataController.php

<?php

namespace App\Controller;

use App\Service\UserService;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;

class DataController extends AbstractController
{
    /**
     * @Route("/data", name="data")
     */
    public function index(UserService $userService): Response
    {
        $data = $userService->getUsers();

        return $this->json($data);
    }
}

The DataController injects the UserService, calls its getUsers() method to retrieve data. The data returned as JSON to the caller.

src/Service/UserService.php

<?php

namespace App\Service;

use Symfony\Contracts\HttpClient\HttpClientInterface;

class UserService
{
    private $httpClient;

    public function __construct(HttpClientInterface $httpClient)
    {
        $this->httpClient = $httpClient;
    }

    public function getUsers(): Array
    {
        $response = $this->httpClient->request('GET', 
            'https://jsonplaceholder.typicode.com/users');

        $data = $response->getContent();

        $decoded = json_decode($data);

        return $decoded;
    }
}

This is the UserService.

public function __construct(HttpClientInterface $httpClient)
{
    $this->httpClient = $httpClient;
}

We inject the HttpClient with HttpClientInterface.

public function getUsers(): Array
{
    $response = $this->httpClient->request('GET', 
        'https://jsonplaceholder.typicode.com/users');

    $data = $response->getContent();

    $decoded = json_decode($data);

    return $decoded;
}

We generate a GET request, decode the data and return it.

$ php bin/console server:run

We run the application and navigate to localhost:8000/data.

In this tutorial we have worked with the Symfony HttpClient component.

Tạo Request HttpClient Tới Server Thật Đơn Giản Với Guzzle

Bài đăng này đã không được cập nhật trong 2 năm

Tìm Hiểu Về HTTP Client

Khi ta truy cập một website và tương tác với các thành phần của website để gửi thông tin thì bản chất của việc đó là browser thay ta gửi đi các HTTP requests. Khi đó browser mà chúng ta đang dùng đóng vai trò một HTTP client.

Guzzle Là Gì ?

Guzzle là một PHP HTTP client giúp việc gửi HTTP request trở lên đơn giản. Phiên bản mới nhất là Guzzle 6. Các lợi thế của Guzzle 6:

  • Dễ dàng thực hiện tạo query string, POST request, streaming large upload, streaming large download, sử dụng HTTP cookies, upload dữ liệu Json....

  • Có thể gửi cả request đồng bộ và không đồng bộ bằng cách sử dụng cùng một interface.

  • Sử dụng tiểu chuẩn PSR-7 cho request, response, stream. Điều này giúp bạn dễ dàng tích hợp các thư viện khác sử dụng PSR-7 với Guzzle. (Các phiên bản trước không sử dụng PSR-7)

  • Không phụ thuộc chặt vào cURL, PHP stream, sockets hoặc vòng lặp không bị chặn.

  • Hệ thống Middleware cho phép bạn kiểm soát hành vi của client

Cài Đặt Guzzle

Vì là một composer package nên bạn phải cài composer và tích hợp composer vào dự án của bạn đã, những framework hiện đại như Symfony và Laravel thì không cần vì chúng đã được tích hợp sẵn rồi. Tiếp theo là dùng lệnh sau để thêm Guzzle vào dự án:

composer require guzzlehttp/guzzle

Test Một Số Request Bằng Guzzle

Gửi Get request

public function getGuzzleRequest()
{
    $client = new \GuzzleHttp\Client();
    $request = $client->get('http://myexample.com');
    $response = $request->getBody();
   
    dd($response);
}

Gửi POST request

public function postGuzzleRequest()
{
    $client = new \GuzzleHttp\Client();
    $url = "http://myexample.com/api/posts";
   
    $myBody['name'] = "Demo";
    $request = $client->post($url,  ['body'=>$myBody]);
    $response = $request->send();
  
    dd($response);
}

Gửi PUT request

public function putGuzzleRequest()
{
    $client = new \GuzzleHttp\Client();
    $url = "http://myexample.com/api/posts/1";
    $myBody['name'] = "Demo";
    $request = $client->put($url,  ['body'=>$myBody]);
    $response = $request->send();
   
    dd($response);
}

Gửi DELETE request

public function deleteGuzzleRequest()
{
    $client = new \GuzzleHttp\Client();
    $url = "http://myexample.com/api/posts/1";
    $request = $client->delete($url);
    $response = $request->send();
  
    dd($response);
}

Tổng Kết

Việc dùng thư viện sẽ giúp bạn tiết kiệm được kha khá thời gian và tránh được nhiều lỗi không đáng có. Tuy vậy, nó vẫn có nhược điểm cho nên đừng lạm dụng quá.

Thứ nhất là làm cho bạn phụ thuộc vào thư viện, thứ hai là làm tăng độ cồng kềnh của dự án. Chỉ cần maintainer thư viện đó lỡ tay release một bản lỗi thôi là dự án bạn cũng lỗi theo, hoặc là cả một project của bạn chỉ có một chỗ gọi HTTP GET request đơn giản nhưng bạn lại cài cả một thư viện to uỳnh vào là không cần thiết.

Last updated