EasyТеория4 min

Основы HTTP

RFC 9110, request/response, HTTP методы, заголовки — всё что нужно для экзамена

HTTP (HyperText Transfer Protocol) — основа веба. Symfony построен вокруг концепции Request/Response. На экзамене проверяют знание HTTP-методов, заголовков, структуры запросов и ответов.

Структура HTTP-запроса

HTTP-запрос состоит из трёх частей: стартовая строка, заголовки и тело.

POST /api/products HTTP/1.1         ← Request line
Host: example.com                    ← Headers
Content-Type: application/json
Authorization: Bearer abc123
Content-Length: 42
                                     ← Empty line (separator)
{"name":"Widget","price":9.99}       ← Body

Компоненты стартовой строки

Компонент Описание Пример
Метод HTTP verb GET, POST, PUT
URI Путь ресурса /api/products?page=1
Версия Версия протокола HTTP/1.1, HTTP/2

Структура HTTP-ответа

HTTP/1.1 200 OK                      ← Status line
Content-Type: application/json       ← Headers
Cache-Control: max-age=3600
X-Request-Id: abc-123
                                     ← Empty line
{"id":1,"name":"Widget"}             ← Body

Symfony 8.0: Symfony 8.0 обновил HttpFoundation в соответствии с RFC 9110 (замена RFC 7230-7235). Основные изменения — в обработке заголовков и статус-кодов. RFC 9110 объединяет семантику HTTP в один документ.

HTTP-методы

Таблица методов

Метод Назначение Тело запроса Идемпотентный Безопасный Кэшируемый
GET Получить ресурс Нет Да Да Да
HEAD Только заголовки Нет Да Да Да
POST Создать ресурс Да Нет Нет Условно
PUT Заменить ресурс целиком Да Да Нет Нет
PATCH Частичное обновление Да Нет Нет Нет
DELETE Удалить ресурс Может быть Да Нет Нет
OPTIONS Узнать возможности Нет Да Да Нет

Подвох экзамена: Идемпотентность означает, что повторный запрос даёт тот же результат. PUT идемпотентен (повторная замена = тот же результат), а PATCH — нет (повторный патч может дать другой результат). DELETE идемпотентен (удаление удалённого = ок). Это частый вопрос.

Подробнее о каждом методе

<?php

declare(strict_types=1);

namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;

#[Route('/api/products', name: 'api_product_')]
class ProductApiController extends AbstractController
{
    // GET — retrieve resource(s), no side effects
    #[Route('', name: 'list', methods: ['GET'])]
    public function list(Request $request): JsonResponse
    {
        $page = $request->query->getInt('page', 1);
        return $this->json(['page' => $page, 'items' => []]);
    }

    // POST — create a new resource
    #[Route('', name: 'create', methods: ['POST'])]
    public function create(Request $request): JsonResponse
    {
        $data = $request->toArray();
        // Create product...
        return $this->json(['id' => 1], Response::HTTP_CREATED);
    }

    // PUT — replace entire resource
    #[Route('/{id}', name: 'replace', methods: ['PUT'])]
    public function replace(int $id, Request $request): JsonResponse
    {
        $data = $request->toArray();
        // Replace all fields of product $id
        return $this->json(['id' => $id]);
    }

    // PATCH — partial update
    #[Route('/{id}', name: 'update', methods: ['PATCH'])]
    public function update(int $id, Request $request): JsonResponse
    {
        $data = $request->toArray();
        // Update only provided fields
        return $this->json(['id' => $id]);
    }

    // DELETE — remove resource
    #[Route('/{id}', name: 'delete', methods: ['DELETE'])]
    public function delete(int $id): Response
    {
        // Delete product $id
        return new Response(null, Response::HTTP_NO_CONTENT);
    }

    // OPTIONS — describe communication options
    #[Route('', name: 'options', methods: ['OPTIONS'])]
    public function options(): Response
    {
        return new Response(null, 200, [
            'Allow' => 'GET, POST, OPTIONS',
        ]);
    }
}

Safe vs Idempotent vs Neither

Safe (безопасный):       GET, HEAD, OPTIONS
Idempotent (идемпотент): GET, HEAD, OPTIONS, PUT, DELETE
Neither (ни то, ни другое): POST, PATCH

Подвох экзамена: Безопасный метод — тот, что не меняет состояние сервера. Идемпотентный — повторный вызов даёт тот же результат. Все безопасные методы идемпотентны, но не все идемпотентные — безопасны (PUT изменяет сервер, но идемпотентен).

Заголовки HTTP

Request Headers

Заголовок Назначение Пример
Host Доменное имя сервера Host: api.example.com
Accept Желаемый формат ответа Accept: application/json
Content-Type Формат тела запроса Content-Type: application/json
Authorization Авторизация Authorization: Bearer token123
Accept-Language Желаемый язык Accept-Language: ru-RU,en;q=0.9
User-Agent Клиентское ПО User-Agent: Mozilla/5.0...
Referer Откуда пришёл запрос Referer: https://example.com/
Cookie Куки Cookie: session=abc123
If-None-Match Условный запрос (ETag) If-None-Match: "v1"
If-Modified-Since Условный запрос (дата) If-Modified-Since: Mon, 01 Jan...

Response Headers

Заголовок Назначение Пример
Content-Type Формат тела ответа Content-Type: text/html; charset=UTF-8
Content-Length Размер тела Content-Length: 348
Cache-Control Инструкции кэширования Cache-Control: max-age=3600
Set-Cookie Установить cookie Set-Cookie: session=abc; HttpOnly
Location Редирект Location: /new-url
ETag Версия ресурса ETag: "33a64df5"
Last-Modified Дата изменения Last-Modified: Wed, 21 Oct...
Allow Разрешённые методы Allow: GET, POST, HEAD
WWW-Authenticate Схема авторизации WWW-Authenticate: Bearer

HttpFoundation в Symfony

Symfony оборачивает HTTP в объекты Request и Response.

<?php

declare(strict_types=1);

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

// Creating Request from PHP globals
$request = Request::createFromGlobals();

// Accessing request data
$method = $request->getMethod();           // 'GET', 'POST', etc.
$uri = $request->getRequestUri();          // '/api/products?page=1'
$path = $request->getPathInfo();           // '/api/products'
$host = $request->getHost();               // 'example.com'
$scheme = $request->getScheme();           // 'https'
$isSecure = $request->isSecure();          // true
$isXhr = $request->isXmlHttpRequest();     // Ajax check
$clientIp = $request->getClientIp();       // '192.168.1.1'
$contentType = $request->getContentTypeFormat(); // 'json'

// Query parameters (?key=value)
$page = $request->query->get('page', '1');

// POST data
$name = $request->request->get('name');

// Headers
$auth = $request->headers->get('Authorization');
$accept = $request->headers->get('Accept');

// Creating Response
$response = new Response(
    'Hello World',
    Response::HTTP_OK,
    ['Content-Type' => 'text/plain']
);

$response->send();

Request Property Bags

Свойство Источник данных PHP-эквивалент
$request->query URL query string $_GET
$request->request POST body $_POST
$request->cookies Cookies $_COOKIE
$request->files Uploaded files $_FILES
$request->server Server vars $_SERVER
$request->headers HTTP headers Из $_SERVER
$request->attributes Route params & custom Нет аналога

Подвох экзамена: $request->attributes — это НЕ HTTP-данные. Это внутренний bag Symfony для передачи данных между компонентами (route parameters, resolved objects и т.д.). На экзамене могут спросить откуда берутся route parameters — ответ: из $request->attributes.

HTTP/2 и HTTP/3

Отличия версий

Характеристика HTTP/1.1 HTTP/2 HTTP/3
Транспорт TCP TCP QUIC/UDP
Мультиплексирование Нет Да Да
Сжатие заголовков Нет HPACK QPACK
Server Push Нет Да Нет
Текстовый/бинарный Текст Бинарный Бинарный

Symfony работает с любой версией HTTP через веб-сервер (Nginx, Caddy). Приложению не нужно заботиться о версии протокола — HttpFoundation абстрагирует различия.

Symfony 8.0: Symfony 8.0 улучшил поддержку HTTP/2 Server Push через Link header и Preload. Однако Server Push deprecated в Chrome и не поддерживается в HTTP/3. Рекомендуется использовать 103 Early Hints вместо Server Push.