PSR6 - Cache (Bài trước)
(Bài tiếp) PHP - Hello World!

Định nghĩa về HTTM Message

PSR-7 là các chỉ dẫn nên tuân theo về lập trình ứng dụng với thông điệp HTTP, nó căn cứ vào các tiêu chuẩn  RFC 7230, RFC 7231, RFC 3986. Thông điệp HTTP là nền tảng của ứng dụng web. Các Web Browser và các trình khách HTTP như cURL tạo ra một thông điệp HTTP (HTTP request), rồi gửi nó đến web server, server nhận được thông điệp đó (có thể hiểu là yêu cầu), nó sẽ gửi về một thông điệp HTTP (HTTP Response).

Như vậy có hai thông loại mà bạn quan tâm đó là HTTP RequestHTTP Response.

Các HTTP message thường thì không được nhìn thấy, không cần phải hiểu bởi người dùng, nhưng với người phát triển web thì rất nên hiểu về cấu trúc để sử dụng chúng thi hành các tác vụ theo yêu cầu. Như diễn giải của hình vẽ trên, các HTTP Message (Response và Request) là một dữ liệu dạng văn bản text có các thành phần gồm Header, Body, Trailer


Giao thức HTTP sử dụng truyền để truyền tải dữ liệu giữa server và client, bản chất là nó dựa vào giao thức TCP. Đối với HTTP các thông điệp gửi đi được trình bày dưới dạng text - đọc được

http message Cấu tạo các HTTP MESSAGE

Cấu tạo HTTP Request

Mọi cấu tạo thông điệp HTTP từ Client gửi đến Server đều có dạng:

  • POST /path HTTP/1.1
    Host: example.com
    
    foo=bar&baz=bat
  • 1 Dòng đầu tiên chứa yêu cầu, theo thứ tự chứa các thông tin: phương thức yêu cầu (POST,GET ...), đích yêu cầu (thường là url - đường dẫn tương đối path), và phiên bản của giao thức HTTP, các thông tin này cách nhau bởi khoảng trắng.
  • 2 Theo sau là các dòng HTTP Header nếu nó (mỗi header trên một dòng), ví dụ:
    Accept-Encoding: gzip, deflate
    Cache-Control: no-cache
    Host: xuanthulab.net
  • 3 Một dòng trống
  • 4 Cuối cùng là nội dung message.

Cấu tạo HTTP Response

HTTP Response là thông điệp do server gửi về cho Client (như máy chủ web gửi về cho browser). Nó có cấu tạo

HTTP/1.1 200 OK
Content-Type: text/plain

This is the response body
  • Dòng đầu là dòng trạng thái, theo thứ tự chứa phiên bản giao thức HTTP, mã trả về, và văn bản diễn giải mã trả về.
  • Tiếp theo là các dòng header nếu có
  • Tiếp theo là một dòng trống
  • Cuối cùng là nội dung của HTTP Response Message.

Tiêu chuẩn PSR-7 là gợi ý triển khai mã theo một giao diện thống nhất để xây dựng lên các lớp tạo ra HTTP Response, HTTP Request và tương tác với chúng, sử dụng chúng

Các interface chuẩn để bạn triển khai bạn tải trực tiếp từ http-message hoặc dùng lệnh composer tích hợp giao diện vào dự án:

composer require psr/http-message

Tải về trong đó có sẵn các 7 interface để bạn triển thành các lớp: 

  1. MessageInterface
  2. RequestInterface
  3. ResponseInterface
  4. ServerRequestInterface
  5. StreamInterface
  6. UploadedFileInterface
  7. UriInterface
  • Thuật ngữ gợi ý của PSR bạn lưu ý đó là: PHẢI, KHÔNG PHẢI, KHUYẾN NGHỊ ... đó là các từ để căn cứ để bạn quyết định áp dụng phần nào của kỹ thuật vào ứng dụng.
  • Tiếp theo là các mô tả và namespace, interface class, các phương thức nên theo.

Các đặc điểm kỹ thuật về HTTP MESSAGE

1) Giao diện thông điệp  - Message

Một thông điệp HTTP có thể là yêu cầu gửi đi từ client (HTTP Request) hoặc thông điệp đáp trả từ server (HTTP Response) cho client. Như vậy cần phải có các lớp định nghĩa giao diện cho các thông điệp yêu cầu (request) Psr\Http\Message\RequestInterface và giao diện thông điệp đáp trả (response) Psr\Http\Message\ResponseInterface.  Cả hai loại thông điệp này đều kế thừa từ một giao diện chung có Psr\Http\Message\MessageInterface.

Các lớp biểu diễn HTTP Request và HTTP Response trong ứng dụng của bạn CÓ THỂ xây dựng bằng cách triển khai trực tiếp từ Psr\Http\Message\MessageInterface, nhưng bạn NÊN xây dựng bằng cách triển khai từ Psr\Http\Message\RequestInterface và Psr\Http\Message\ResponseInterface.

2) Về các HTTP Header

Không phân biệt chữ hoa - thường

Các header được truy cập bởi tên của bằng cách triển khai mã từ lớp giao diện MessageInterface, các tên Header được thiết lập và truy cập không phân biệt chữ hoa - thường.

Ví dụ:

$message = $message->withHeader('foo', 'bar');//Thiết lập header tên foo giá trị bar

echo $message->getHeaderLine('foo');
// Outputs: bar

echo $message->getHeaderLine('FOO');
// Outputs: bar

$message = $message->withHeader('fOO', 'baz');
echo $message->getHeaderLine('foo');
// Outputs: baz

Bởi vì tên của header khi các server trả về có thể có chữ hoa, chữ thường nên bạn PHẢI xử lý việc này trong phương thức getHeaders() của lớp triển khai để đảm bảo là không phân biệt chữ hoa, chữ thường 

Header nhiều giá trị

Thông lệ thì các header với nhiều giá trị vẫn được dùng, header có thể nhận được từ MessageInterface như là một mảng hoặc chuỗi. Sử dụng phương thức getHeaderLine để nhận chuỗi có chứa giá trị. Các giá trị phân cách bởi dấu ,. Sử dụng getHeader để nhận  về mảng tất cả giá trị header theo tên Header.

Ví dụ:

$message = $message
    ->withHeader('foo', 'bar')
    ->withAddedHeader('foo', 'baz');

$header = $message->getHeaderLine('foo');
// $header chứa: 'bar, baz'

$header = $message->getHeader('foo');
// ['bar', 'baz']

Host Header

Trong các thông điệp yêu cầu (Request) thường Host Header tương ứng với thành phần URI, nó chỉ ra host sử dụng trong kết nối TCP. Tuy nhiên, tiêu chuẩn HTTP 

Khi khởi tạo thông điệp Request PHẢI thiết lập Host header từ một URI nếu Host chưa được thiết lập. 

RequestInterface::withUri  mặc định sẽ thiết lập Host Header tương ứng phù hợp với tham số chuyển đến (tham số là đối tượng UriInterface).

Để lấy Host Header gọi phương thức: getHeaderLine('Host')

3) Streams - Các luồng

Như đã mô tả HTTP message nó chứa: dòng bắt đầu, các header và body.  Phần body có thể rất nhỏ hoặc rất lớn (về dữ liệu như chứa cả một file dữ liệu).  Nếu cứ cố gắng mô tả phần body bằng cách thông thường là dùng chuỗi ký tự (text) nhiều khi là quá lớn dẫn đến các vấn đề nghiệm trọng cho bộ nhớ của hệ thống. Do vậy giao diện StreamInterface lúc này được dùng để diễn tả luồng dữ liệu có thể được đọc hoặc ghi vào đó khi cần dùng đến. Lúc này chuỗi ký tự của body không phải là dữ liệu thực mà sẽ là  chuỗi chỉ đến các luồng tương ứng ví dụ như php://memoryphp://temp.

Các stream có ba phương thức tương ứng là: isReadable(), isWritable(),  và isSeekable(). Chúng được sử dụng để xác định các khả năng của stream. Ngoài ra mỗi stream có vài thông tin như: read-only, write-only, read-write ...

Cuối cùng thì giao diện StreamInterface phải định nghĩa phương thức __toString() để lấy dữ liệu từ body.

4) Request Targets và URIs

Request target(mục tiêu - đích của HTTP mgs) có thể ở dạng sau:

  • origin-form, chứa đường dẫn URL
  • absolute-form, chứa scheme và người gửi authority ("[user-info@]host[:port]")
  • authority-form, chỉ chứa authority
  • asterisk-form, kiểm tra khả năng.

URL được định nghĩa bởi UriInterface, nó cung cấp __toString() để lấy URI. Nhận request target bằng phương thức getRequestTarget().

Ví dụ:

$request = $request
    ->withMethod('OPTIONS')
    ->withRequestTarget('*')
    ->withUri(new Uri('https://example.org/'));

5) Server-side Request  - các request tại server

PHP cung cấp các biến toàn cục:

  • $_COOKIE, truy cập các cookie HTTP
  • $_GET, truy cập các tham số của chuỗi query (query trong url)
  • $_POST, truy cập các tham số urlencoded được chuyển đến bởi HTTP POST
  • $_FILES, chứa thông tin mô tả về các file được upload
  • $_SERVER, truy cập các biến môi trường CGI/SAPI, chứa nhiều thứ gồm request method, scheme, URI, header.

Thông thường với các biến đó, lập trình viên sẽ biết được mọi thông tin mà khách yêu cầu gửi đến Server, và trong PSR-7 cung cấp giao diện ServerRequestInterface mở rộng từ  RequestInterface để bao bọc tất cả các biến toàn cục đó và thêm vào một số thuộc tính tiện ích (có nghĩa là diễn tả mọi thông tin client gửi đến Server).

6 Uploaded files - các file upload

ServerRequestInterface có phương thức getUploadedFiles() để nhận một cây mô tả file upload, mà mỗi lá của cây là một UploadedFileInterface. Có các phương thưc để làm việc với file, moveTo($targetPath) , _uploaded_file()

Trên đây trình bày khái quát về PSR-7, chuẩn giao diện thông điệp HTTP bắt đầu được cung cấp bởi hầu hết các Framework PHP phổ biến. Trong tiêu chuẩn này gợi ý xây dựng các Interface, Method, Atribute để trừu tượng hóa HTTP Message. 

Gợi ý này được đóng gói và được nhiều Framework làm theo, bạn lấy Package này tại https://github.com/php-fig/http-message

Interface tiêu chuẩn cho HTTM Message

Nếu bạn tự xây dựng, thì cần định nghĩa theo chuẩn interface sau:

1) Psr\Http\Message\MessageInterface
<?php
namespace Psr\Http\Message;
interface MessageInterface
{
    public function getProtocolVersion(); //Hàm lấy HTTP protocol 
    public function withProtocolVersion($version); //Hàm thiết lập HTTP protocol  
    public function getHeaders(); //Trả về tất cả các Header
    public function hasHeader($name); //Kiểm tra sự tồn tại của Header có tên $name
    public function getHeader($name);  //Lấy Header có tên $name (trả về một mảng string là giá trị của Header)
    public function getHeaderLine($name); //Lấy giá trị của Header (trả về một string : các giá trị cách nhau bởi dấu ,
    public function withHeader($name, $value); //Thiết lập Header
    public function withAddedHeader($name, $value); //Thêm giá trị vào Header
    public function withoutHeader($name); //Loại bỏ Header
    public function getBody(); //Lấy đối tượng StreamInterface của Body
    public function withBody(StreamInterface $body); //Thiết lập body
}

Chú ý: các phương thức có thiết lập giá trị sẽ tạo ra đối tượng MessageInterface trả về (đối tượng gốc không đổi)

2) Psr\Http\Message\RequestInterface

<?php
namespace Psr\Http\Message;
interface RequestInterface extends MessageInterface
{
    
    public function getRequestTarget();//lấy Target 
    public function withRequestTarget($requestTarget); //Thiết lập Target
    public function getMethod();//Lấy phương thức (GET,POST ...)
    public function withMethod($method); //Thiết lập phương thức 
    public function getUri(); //Lấy URI 
    public function withUri(UriInterface $uri, $preserveHost = false); //Thiết lập URI
}

Psr\Http\Message\ServerRequestInterface
Diễn tả HTTP Message ở Server

<?php
namespace Psr\Http\Message;
interface ServerRequestInterface extends RequestInterface
{   
    public function getCookieParams(); 
    public function withCookieParams(array $cookies);    
    public function getQueryParams(); 
    public function withQueryParams(array $query);    
    public function getUploadedFiles(); 
    public function withUploadedFiles(array $uploadedFiles); 
    public function getParsedBody(); 
    public function withParsedBody($data); 
    public function getAttributes(); //Lấy các thuộc tính chuyển đến từ Request 
    public function getAttribute($name, $default = null); //Lấy các thuộc tính chuyển đến từ Request 
    public function withAttribute($name, $value);   
    public function withoutAttribute($name);
}

Psr\Http\Message\ResponseInterface

<?php
namespace Psr\Http\Message;
 
interface ResponseInterface extends MessageInterface
{ 
    public function getStatusCode(); //Mã trả về 200, 301, 404 ...
    public function withStatus($code, $reasonPhrase = '');
    public function getReasonPhrase(); //Mô tả mã trả về: OK, chuyển hướng, không thấy ...
}

4) Psr\Http\Message\StreamInterface

<?php
namespace Psr\Http\Message;
 
interface StreamInterface
{ 
    public function __toString();
    public function close();
    public function detach();
    public function getSize();
    public function tell();
    public function eof();
    public function isSeekable();
    public function seek($offset, $whence = SEEK_SET);
    public function rewind();
    public function isWritable();
    public function write($string);
    public function isReadable();
    public function read($length);
    public function getContents();
    public function getMetadata($key = null);
}

Psr\Http\Message\UriInterface

<?php
namespace Psr\Http\Message;
 
interface UriInterface
{ 
    public function getScheme(); 
    public function getAuthority(); 
    public function getUserInfo(); 
    public function getHost(); 
    public function getPort(); 
    public function getPath(); 
    public function getQuery(); 
    public function getFragment(); 
    public function withScheme($scheme); 
    public function withUserInfo($user, $password = null); 
    public function withHost($host); 
    public function withPort($port); 
    public function withPath($path); 
    public function withQuery($query); 
    public function withFragment($fragment); 
    public function __toString();
}

Psr\Http\Message\UploadedFileInterface

<?php
namespace Psr\Http\Me 
interface UploadedFileInterface
{   
    public function getStream(); 
    public function moveTo($targetPath); 
    public function getError(); 
    public function getClientFilename(); 
    public function getClientMediaType();
}

Kết luận: 

Để xây dựng ứng dụng HTTP Message theo chuẩn bạn cần tự triển khai các mã theo các giao diện chuẩn đưa ra ở trên

Khuyên bạn sử dụng ngay các framework đã triển khai mã theo đúng chuẩn giao diện ở trên, có rất nhiều Framework cho bạn lựa chọn:

Các Framework xây dựng sẵn cho bạn theo PSR-7

Symfony : http://symfony.com/blog/psr-7-support-in-symfony-is-here

Zend Framework : https://github.com/zendframework/zend-diactoros

Laravel :  https://laravel.com/docs/5.1/requests

Đặc biệt: Slim Framework


Đăng ký nhận bài viết mới
PSR6 - Cache (Bài trước)
(Bài tiếp) PHP - Hello World!