REST API
API — адреса сайта, которые отдают не страницы, а данные, обычно в формате JSON. Ими пользуются мобильные приложения и JavaScript. REST — договорённость о таких адресах: один адрес обозначает ресурс (статьи), а метод HTTP — действие над ним. В Laminas для этого есть готовый базовый контроллер.
Подготовка
В уроке об ответах в config/autoload/global.php уже включена стратегия ViewJsonStrategy — без неё JsonModel не превратится в ответ JSON.
Контроллер API
Создайте файл в папке контроллеров модуля:
// module/Blog/src/Controller/PostApiController.php
namespace Blog\Controller;
use Blog\Model\PostTable;
use Laminas\Mvc\Controller\AbstractRestfulController;
use Laminas\View\Model\JsonModel;
class PostApiController extends AbstractRestfulController
{
public function __construct(private PostTable $posts) {}
public function getList()
{
return new JsonModel(['data' => $this->posts->fetchLatest(20)]);
}
}Класс наследует AbstractRestfulController. В отличие от обычного контроллера здесь нет действий с окончанием Action: метод выбирается по методу HTTP.
Маршрут
Добавьте контроллер в controllers.factories с ReflectionBasedAbstractFactory и опишите маршрут в module/Blog/config/module.config.php:
// module/Blog/config/module.config.php, внутри 'routes'
'api-posts' => [
'type' => Segment::class,
'options' => [
'route' => '/api/posts[/:id]',
'constraints' => ['id' => '[0-9]+'],
'defaults' => [
'controller' => Controller\PostApiController::class,
],
],
],Ключа action в маршруте нет — метод выбирает сам контроллер. Откройте /api/posts: браузер покажет JSON с ключом data и списком статей.
Какой метод вызывается
| Запрос | Метод контроллера |
|---|---|
GET /api/posts | getList() |
GET /api/posts/5 | get($id) |
POST /api/posts | create($data) |
PUT /api/posts/5 | update($id, $data) |
DELETE /api/posts/5 | delete($id) |
Если метод не описан в вашем классе, контроллер ответит кодом 405 «метод не разрешён».
Одна запись
public function get($id)
{
$post = $this->posts->find((int) $id);
if ($post === null) {
$this->getResponse()->setStatusCode(404);
return new JsonModel(['error' => 'Статья не найдена']);
}
return new JsonModel(['data' => $post]);
}Для несуществующей статьи клиент получит код 404 и сообщение об ошибке в JSON.
Создание и удаление
public function create($data)
{
$post = new Post();
$post->exchangeArray($data);
$this->posts->save($post);
$this->getResponse()->setStatusCode(201);
return new JsonModel(['data' => $post]);
}
public function delete($id)
{
$this->posts->delete((int) $id);
$this->getResponse()->setStatusCode(204);
return new JsonModel();
}В $data приходит массив полей. Если клиент отправил тело в формате JSON с заголовком Content-Type: application/json, контроллер разберёт его сам. Код 201 означает «создано», 204 — «выполнено, тела ответа нет». Перед сохранением данные проверяют набором правил InputFilter из урока о проверке данных.
Проверка через curl
curl http://localhost:8080/api/posts/1
curl -X POST http://localhost:8080/api/posts \
-H "Content-Type: application/json" \
-d '{"title": "Статья из API", "body": "Текст статьи."}'Первая команда напечатает статью с номером 1, вторая создаст новую и вернёт её вместе с присвоенным id.
Частая ошибка
API не защищён слушателем из урока о правах, если его маршрут не внесён в список закрытых: адрес DELETE /api/posts/5 удалит статью по запросу любого посетителя. Добавьте маршрут api-posts в проверку и для запросов, меняющих данные, отвечайте кодом 401 вместо перенаправления на страницу входа.