PHP PHP  ·  30 вопросов  ·  ~85 мин  ·  ⏱ Таймер 25:00  ·  Сложный  ·  👥 1 прошёл

REST API на PHP

Здесь оценивается не красота URL, а предсказуемость поведения клиента при успехе, ошибке и повторе запроса. Вопросы охватывают валидацию JSON, коды HTTP, идемпотентность, пагинацию, версионирование и безопасные ответы.

Отвечено: 0 из 30
⏱ --:--
0%
💡 Инструкция: Выберите один ответ из пяти. Читайте каждый сценарий глазами клиента API: что он отправил, что получил и может ли повторить действие. Таймер — 85 минут.
Вопрос 1 из 30
Какой маршрут лучше выражает получение заказа 42?
PHP
if ($request->method === 'GET' && preg_match('#^/orders/(\d+)$#', $request->path, $m)) {
    return $ordersController->show((int) $m[1]);
}
Вопрос 2 из 30
Какой метод подходит частичному изменению заказа?
PHP
if ($request->method === 'PATCH' && preg_match('#^/orders/(\d+)$#', $request->path, $m)) {
    return $ordersController->patch((int) $m[1], $request->json());
}
Вопрос 3 из 30
Почему контроллер не должен содержать весь SQL и расчёт скидки?
PHP
final class OrderController
{
    public function __construct(private CreateOrder $createOrder) {}

    public function create(Request $request): Response
    {
        $command = CreateOrderCommand::fromArray($request->json());
        $result = $this->createOrder->execute($command);
        return JsonResponse::created($result);
    }
}
Вопрос 4 из 30
Что делать с неизвестным маршрутом?
Вопрос 5 из 30
Почему порядок /users/me и /users/{id} важен в простом роутере?
Вопрос 6 из 30
Как безопасно декодировать обязательное JSON-тело?
PHP
if (!str_starts_with($request->header('Content-Type'), 'application/json')) {
    return JsonResponse::error('unsupported_media_type', 415);
}
$body = json_decode($request->body(), true, 512, JSON_THROW_ON_ERROR);
Вопрос 7 из 30
Что возвращать при синтаксически неверном JSON?
PHP
try {
    $body = json_decode($request->body(), true, 512, JSON_THROW_ON_ERROR);
} catch (JsonException) {
    return JsonResponse::error('invalid_json', 400);
}
Вопрос 8 из 30
Когда уместен 422?
PHP
$violations = $validator->validate($input);
if ($violations !== []) {
    return new JsonResponse(['error' => 'validation_failed', 'fields' => $violations], 422);
}
Вопрос 9 из 30
Почему JSON_THROW_ON_ERROR лучше проверки json_last_error в каждом месте?
Вопрос 10 из 30
Как избежать потери большого целого идентификатора у некоторых клиентов?
Вопрос 11 из 30
Успешно создан новый ресурс. Какой статус?
PHP
$order = $service->create($command);
header('Location: /orders/' . $order->id);
return new JsonResponse(['id' => (string) $order->id], 201);
Вопрос 12 из 30
Разница 401 и 403?
PHP
if ($currentUser === null) return JsonResponse::error('unauthorized', 401);
if (!$policy->mayEdit($currentUser, $order)) return JsonResponse::error('forbidden', 403);
Вопрос 13 из 30
Когда 409 Conflict уместнее 422?
PHP
if ($order->status()->isFinal()) {
    return JsonResponse::error('order_state_conflict', 409);
}
Вопрос 14 из 30
Можно ли у 204 быть тело?
Вопрос 15 из 30
Почему 200 с {"error":...} для всех ошибок плох?
Вопрос 16 из 30
Какое изменение обычно совместимо?
PHP
return json_encode([
    'id' => $user->id,
    'name' => $user->name,
    'middle_name' => $user->middleName,
], JSON_THROW_ON_ERROR);
Вопрос 17 из 30
Где можно указывать версию?
PHP
$version = $request->header('Accept') === 'application/vnd.example.v2+json' ? 2 : 1;
return $presenters[$version]->order($order);
Вопрос 18 из 30
Почему изменение значения status с paid на completed может быть ломающим?
PHP
switch ($payload['status']) {
    case 'pending': showWaiting(); break;
    case 'paid': showReceipt(); break;
    default: throw new UnexpectedValueException('Unknown status');
}
Вопрос 19 из 30
Как завершать старую версию?
Вопрос 20 из 30
Нужно ли копировать всю кодовую базу для v2?
Вопрос 21 из 30
Что должен проверять контрактный тест?
PHP
$response = $client->get('/orders/42');
self::assertSame(200, $response->status());
self::assertSame('application/json; charset=utf-8', $response->header('Content-Type'));
self::assertIsString($response->json()['id']);
Вопрос 22 из 30
Как тестировать идемпотентный PUT?
PHP
$first = $client->put('/profiles/42', ['name' => 'Ира']);
$second = $client->put('/profiles/42', ['name' => 'Ира']);
self::assertSame($first->json(), $second->json());
self::assertSame(1, $profiles->countById(42));
Вопрос 23 из 30
Зачем request_id в ошибке?
PHP
$requestId = bin2hex(random_bytes(8));
$logger->error('API failure', ['request_id' => $requestId, 'exception' => $error]);
return new JsonResponse(['error' => 'internal_error', 'request_id' => $requestId], 500);
Вопрос 24 из 30
Какие тела проверить?
Вопрос 25 из 30
Почему snapshot всего JSON может быть хрупким?
Вопрос 26 из 30
Как предотвратить IDOR?
PHP
$order = $orders->findOwnedBy($orderId, $currentUser->id);
if ($order === null) {
    return JsonResponse::error('not_found', 404);
}
Вопрос 27 из 30
Зачем ограничивать размер JSON-тела?
PHP
$length = (int) $request->header('Content-Length');
if ($length > 1_000_000) {
    return JsonResponse::error('payload_too_large', 413);
}
Вопрос 28 из 30
Что включать в rate limit ответ?
PHP
if (!$limiter->allow($clientKey)) {
    header('Retry-After: ' . $limiter->retryAfter($clientKey));
    return JsonResponse::error('rate_limited', 429);
}
Вопрос 29 из 30
Почему логировать полное тело API опасно?
Вопрос 30 из 30
Как выпускать изменение API?

Ответьте на все 30 вопросов, чтобы получить результат

🔗 Встроить тест на свой сайт (iframe) ▼

Скопируйте код и вставьте в любое место на вашем сайте:

Также доступна прямая ссылка на embed-страницу