JSON-RPC Server (Laravel/Lumen)
Описание
JsonRpc сервер - реализация сервера по спецификации JsonRpc 2.0.
Поддерживаемые версии:
- Lumen >= 5.7
- Laravel >= 5.7
Поддерживает:
- вызов удаленных методов по нотификации имяКонтроллера_имяМетода
- вызов нескольких удаленных методов в одном запросе
- передача параметров в метод контроллера по имени, либо в порядке очереди
- аутентификация с помощью токена, переданного в заголовке (отключаемо)
- контроль доступа по IP-адресам (отключаемо)
- контроль доступа к методам для разных сервисов - ACL (отключаемо)
- автоматическая генерация SMD-схемы
- возможность настройки нескольких точек входа с разными настройками JsonRpc-сервера
Установка
Установка через composer:
composer require tochka-developers/jsonrpc
Laravel
Для Laravel есть возможность опубликовать конфигурацию для всех пакетов:
php artisan vendor:publish
Для того, чтобы опубликовать только конфигурацию данного пакета, можно воспользоваться опцией tag
php artisan vendor:publish --tag="jsonrpc-config"
Lumen
В Lumen отсутствует команда vendor:publish, поэтому делается это вручную. Если в проекте еще нет директории для конфигураций - создайте ее:
mkdir config
Скопируйте в нее конфигрурацию jsonrpc:
cp vendor/tochka-developers/jsonrpc/config/jsonrpc.php config/jsonrpc.php
Вместо config/jsonrpc.php нужно указать любую другую директорию, где хранятся ваши конфиги и название будущего конфига. Далее необходимо прописать скопированный конфиг в bootstrap/app.php
$app->configure('jsonrpc');
Так же прописать провайдер:
$app->register(\Tochka\JsonRpc\JsonRpcServiceProvider::class);
Где jsonrpc - имя файла конфига
Для корректной работы так же необходимы фасады:
$app->withFacades();
Ручная настройка точек входа
При ручной настройке вы сами контролируете процесс роутинга. Пропишите в вашем route.php:
Route::post('/api/v1/jsonrpc', function (Illuminate\Http\Request $request, \Tochka\JsonRpc\JsonRpcServer $server) {
return $server->handle($request);
});
Если планируется передавать имя контроллера в адресе, после точки входа, роутинги дожны быть следующего вида:
Route::post('/api/v1/jsonrpc/{endpoint}[/{action}]', function (Illuminate\Http\Request $request, \Tochka\JsonRpc\JsonRpcServer $server, $endpoint, $action = null) {
return $server->handle($request, ['endpoint' => $endpoint, 'action' => $action]);
});
Для установки уникальных параметров сервера необходимо передать массив с параметрами в метод handle
:
return $server->handle($request, $options);
Описание массива $options
uri
('/api/v1/jsonrpc') - точка входаnamespace
('App\Http\Controllers\') - Namespace для контроллеров. Если не указан - берется значениеjsonrpc.controllerNamespace
controller
('Api') - контроллер по умолчанию (для методов без указания контроллера - имяМетода). Если не указано - берется значениеjsonrpc.defaultController
postfix
('Controller') - суффикс контроллеров (для метода foo_bar будет выбран контроллер fooController). Если не указано - берется значениеjsonrpc.controllerPostfix
middleware
(array) - список обработчиков запроса. В списке обработчиков обязательно должен быть\Tochka\JsonRpc\Middleware\MethodClosureMiddleware::class
, данный обработчик отвечате за выбор контроллера и метода. Если список не указан - берется значениеjsonrpc.middleware
description
('JsonRpc server') - описание сервиса. Возвращается в SMD-схеме. Если не указано - берется значениеjsonrpc.description
auth
(true) - стандартная авторизация по токены в заголовке. Если не указано - берется значениеjsonrpc.authValidate
acl
(array) - список контроля доступа к методам. Заполняется в видеимяМетода => [serviceName1, serviceName2]
. Игнорируется, если не включен обработчикAccessControlListMiddleware
. Если не указано - берется значениеjsonrpc.acl
endpoint
(string) - Пространство имён контроллеров. Добавляется к значению параметра namespace.action
(string) - Имя контроллера. Если не указано и при этом указан endpoint, то endpoint является именем контроллера в пространстве namespace.
Автоматический роутинг
Данный метод более удобен. Для роутинга достаточно перечислить точки входа в параметре jsonrpc.routes
.
[
'/api/v1/jsonrpc', // для этой точки входа будут использованы глобальные настройки
'v2' => [ // для этой точки входа задаются свои настройки. Если какой-то из параметров не указан - используется глобальный
'uri' => '/api/v1/jsonrpc', // URI (обязательный)
'namespace' => 'App\\Http\\Controllers\\V2\\', // Namespace для контроллеров
'controller' => 'Api', // контроллер по умолчанию
'postfix' => 'Controller', // суффикс для имен контроллеров
'middleware' => [], // список обработчиков запросов
'auth' => true, // аутентификация сервиса
'acl' => [], // Список контроля доступа
'description' => 'JsonRpc server V2' // описание для SMD схемы
]
]
Для использования передачи имени контроллера в адресе
[
'v3' => [ // для этой точки входа задаются свои настройки. Если какой-то из параметров не указан - используется глобальный
'uri' => '/api/v3/jsonrpc/{endpoint}[/{action}]', // URI (обязательный)
'namespace' => 'App\\Http\\Controllers\\V3\\', // Namespace для контроллеров
'controller' => 'Api', // контроллер по умолчанию
'postfix' => 'Controller', // суффикс для имен контроллеров
'middleware' => [], // список обработчиков запросов
'auth' => false, // аутентификация сервиса
'acl' => [], // Список контроля доступа
'description' => 'JsonRpc server V3', // описание для SMD схемы
]
]
Каждая точка входа может быть либо строкой с указанием адреса, либо массивом, аналогичном $options.
Обработчики (Middleware)
Обработчики позволяют подготовить запрос, прежде чем вызывать указанный метод. Список обработчиков задается в параметре
jsonrpc.middleware
. Это массив, в котором необходимо перечислить в порядке очереди классы обработчиков.
По умолчанию доступны следующие обработчики:
ValidateJsonRpcMiddleware
Валидация запроса на соответствие спецификации JsonRpc2.0. Рекомендуется использовать.
AccessControlListMiddleware
Контроль доступа к методам.
ServiceValidationMiddleware
Контроль доступа к сервису на основе списка IP-адресов
MethodClosureMiddleware
Обработчик разбирает запрос и находит необходимый контроллер и метод в нем. Данный обработчик обязательно необходимо
включать в список для работы сервера.
AssociateParamsMiddleware
Передача параметров из запроса в метод на основе имен.
Кроме того, вы можете использовать свои обработчики.
Для этого просто реализуйте интерфейс \Tochka\JsonRpc\Middleware\BaseMiddleware
и укажите обработчик в списке.
Аутентификация
Если включена аутентификация (jqonrpc.authValidate
), то в каждом запросе должен присутствовать заголовок (указанный в jsonrpc.accessHeaderName
).
Значение заголовка - токен. Список токенов необходимо указать в параметре jsonrpc.keys
:
[
'systemName1' => 'secretToken1',
'systemName2' => 'secretToken2'
]
Если запрос был осуществлен без данного заголовка, либо с токеном, которого нет в списке - клиенту вернется ошибка.
Если аутентификация прошла успешно - клиент будет опознан как systemName1
(systemName2
), что позволит контролировать доступ к методам.
Если аутентификация отклбючена - клиент будет опознан как guest
.
Контроль доступа к сервису
Если включен обработчик ServiceValidationMiddleware
, то будет осуществлен контроль доступа к сервису на основе списка IP-адресов.
Для описания доступа необходимо заполнить параметр jsonrpc.servers
:
[
'systemName1' => ['192.168.0.1', '192.168.1.5'],
'systemName2' => '*',
]
В указанном примере авторизоваться с ключом доступа сервиса systemName1
могут только клиенты с IP адресами
192.168.0.1
и 192.168.1.5
. Сервис systemName2
может авторизовываться с любых IP адресов.
Контроль доступа к методам
Если включен обработчик AccessControlListMiddleware
, то будет осуществлен контроль доступа к методам.
Для описания доступа необходимо заполнить параметр jsonrpc.acl
:
[
'App\\Http\\TestController1' => [
'*' => '*' // доступ ко всем методам контроллера по умолчанию есть у всех систем
'method1' => ['system1', 'system2'], // но к этому методу есть доступ только у system1 и system2
'method2' => 'system3', // а к этому методу только у system3
]
'App\\Http\\TestController2' => '*', // доступ ко всем методам контроллера есть у всех систем
],
Валидация параметров
Для валидации входных параметров внутри контроллера можно использовать готовый trait: Tochka\JsonRpc\Traits\JsonRpcController
Подключив данный trait в своем контроллере вы сможете проверить данные с помощью средств валидации Laravel:
public function store($title, $body)
{
$validatedData = $this->validate([
'title' => 'required|unique:posts|max:255',
'body' => 'required',
]);
// The blog post is valid...
}
Кроме того, в указанном trait доступны следующие методы:
/**
* Возвращает массив с переданными в запросе параметрами
*/
protected function getArrayRequest(): array;
/**
* Возвращает экземпляр класса с текущим запросом
*/
protected function getRequest(): JsonRpcRequest;
/**
* Валидация переданных в контроллер параметров
*
* @param array $rules Правила валидации
* @param array $messages Сообщения об ошибках
* @param bool $noException Если true - Exception генерироваться не будет
*
* @return bool|MessageBag Прошла валидация или нет
*/
protected function validate($rules, array $messages = [], $noException = false);
/**
* Валидирует и фильтрует переданные в контроллер параметры. Возвращает отфильтрованный массив с параметрами
*
* @param array $rules Правила валидации
* @param array $messages Сообщения об ошибках
* @param bool $noException Если true - Exception генерироваться не будет
*/
protected function validateAndFilter($rules, array $messages = [], $noException = false): array;
Логирование
Вы можете указать любой канал логов, который зарегистрирован у вас в системе (либо предварительно создать его)
с помощью параметра jsonrpc.log.channel
:
/**
* Канал лога, в который будут записываться все логи
*/
'channel' => 'default',
Скрытие конфиденциальной информации в логах системы
Для того, чтобы убрать конфиденциальную информацию (логины, пароли, токены и пр.) из логов системы нужно в
указать,что именно скрывать, в конфигурации jsonrpc.log.hideParams
:
[
'App\\Http\\TestController1@method' => ['password', 'data.phone_number'],
'App\\Http\\TestController2' => '*',
]
В указанном примере из логов метода method
контроллера App\Http\TestController1
будут скрываться занчения параметров
password
и data.phone_number
(поддерживается вложенность параметров в запросе при помощи разделителя .
).
В контроллере App\Http\TestController2
во всех методах будут скрываться значения абсолютно всех параметров.
Как это работает
Клиент послает валидный JsonRpc2.0-запрос:
{
"jsonrpc": "2.0",
"method": "client_getInfoById",
"params": {
"clientCode": "100500",
"fromAgent" : true
},
"id": 15
}
JsonRpc сервер пытается найти указанный метод client_getInfoById
.
Имя метода разбивается на части: имяКонтроллера_имяМетода
.
Класс контроллера ищется по указанному пространству имен (параметр jsonrpc.controllerNamespace
) с указанным суффиксом (по умолчанию Controller
).
Для нашего примера сервер попытается подключить класс 'App\Http\Controller\ClientController'.
Если контроллер не существует - клиенту вернется ошибка Method not found
.
В найденном контроллере вызывается метод getInfoById
.
Далее возможно два варианта.
Если подключен обработчик AssociateParamsMiddleware
, то все переданные параметры будут переданы в метод по именам.
То есть в контроллере должен быть метод getInfoById($clientCode, $fromAgent)
.
Все параметры будут отвалидированы по типам (если типы указаны). Кроме того, таким способом можно указывать необязательные
параметры в методе - в таком случае их необязательно передавать в запросе, вместо непереданных параметров будут
использованы значения по умолчанию из метода.
Если же не будет передан один из обязательных параметров - клиенту вернется ошибка.
Если обработчик AssociateParamsMiddleware
не подключен - то все параметры из запроса будут переданы в метод по порядку.
В таком случае указанному запросу аналогичен следующий:
{
"jsonrpc": "2.0",
"method": "client_getInfoById",
"params": ["100500", true],
"id": 15
}
Если настроено получение имени контроллера из точки входа то логика следующая: Клиент посылает валидный JsonRpc2.0-запрос:
{
"jsonrpc": "2.0",
"method": "getInfoById",
"params": {
"clientCode": "100500",
"fromAgent" : true
},
"id": 15
}
На адрес \client
.
JsonRpc сервер пытается найти указанный метод getInfoById
.
В контроллере: Сlient
. Если запрос идет на адрес \client\action
то имя контроллера будет иметь вид СlientAction
.
Класс контроллера ищется по указанному пространству имен (параметр jsonrpc.controllerNamespace
) с указанным суффиксом (по умолчанию Controller
).
Для нашего примера сервер попытается подключить класс 'App\Http\Controller\ClientController' или 'App\Http\Controller\ClientActionController' соответственно.
Если контроллер не существует - клиенту вернется ошибка Method not found
.
В найденном контроллере вызывается метод getInfoById
.
Несколько вызовов в одном запросе
По спецификации JsonRpc разрешено вызывать несколько методов в одном запросе. Для этого необходимо валидные JsonRpc2.0-вызовы передать в виде массива. Каждый вызываемый метод будет вызван из соответствующего контроллера, а вернувшиеся результаты будут возвращены клиенту в том порядке, в котором пришли запросы.
В ответе клиенту всегда присутствует параметр id, если таковой был передан клиентом. Данный параметр также позволяет идентифицировать ответы на свои запросы на стороне клиента.
SMD-схема
Если на настроенную точку входу сделать запрос с параметром ?smd
, то сервер проигнорирует запрос и вернет полную
SMD-схему для указанной точки входа. SMD-схема строится автоматически на основании настроек указанной точки входа.
Список методов формируется исходя из доступных контроллеров в указанном для точки входа пространстве имен.
В SMD-схеме кроме стандартных описаний присутствуют дополнительные параметры, которые позволяют более точно сгенерировать
JsonRpc-клиента и документацию по серверу.
Дополнительное описание для SMD-схемы
SMD-схема генерируется на основе доступных публичных методов в доступных в указанном пространстве имен контроллеров.
По умолчанию этой информации достаточно для генерации на основе схемы прокси-клиента. Но для генерации понятной и полной
документации также можно использовать расширенное описание контроллеров и методов в блоках PhpDoc
.
Описание группы методов
По умолчанию в качестве названия группы методов используется описание класса из PhpDoc:
<?php
/**
* Методы для работы с чем-то
*/
class SomeController {
// ...
}
Также название можно указать с помощью тега @apiGroupName
. В таком случае само описание класса будет проигнорировано:
<?php
/**
* Class SomeController
* @package App\Http\Controllers\Api
*
* @apiGroupName
* Методы для работы с чем-то
*/
class SomeController {
// ...
}
Доступные методы
По умолчанию генератор SMD-схемы собирает все доступные публичные методы из контроллера. Если необходимо скрыть из
схемы какие-либо методы, можно воспользоваться тегом @apiIgnoreMethod
:
/**
* Class DossierController
* @package App\Http\Controllers\Api
*
* @apiGroupName
* Методы для работы с чем-то
* @apiIgnoreMethod someMethod
* @apiIgnoreMethod otherMethod
*/
class SomeController {
Описание метода
/**
* Основное описание метода.
* @apiName Название метода. Если не указан этот тег - название метода формируется автоматически по правилам
* @apiDescription
* Описание метода. Если не указан - то берется основное описание метода.
* @apiNote Замечание к методу
* @apiWarning Предупреждение к методу
* @apiParam * date="d.m.Y" $param="01.01.1970" ("23.12.2018") Описание параметра
* @apiReturn int $someData Описание возвращаемого значения
*
* @param string $param1 Описание параметра 1
* @param int $param2 Описание параметра 2
* @param string $param3 Описание параметра 3
* @return bool Описание возвращаемого ответа
*
* @apiRequestExample
* Пример запроса
* @apiResponseExample
* Пример ответа
*/