Данная страница документация не завершена.
Актуально для версии 1.22.00.
Все действия будут производиться в папке модуля, например, Abills/modules/Portal.
За пример взят модуль Portal.
Старт
Для начала, в корневой папке модуля файл Api.pm, с таким начальным содержанием:
# Мы объявляем package с названием "*модуль*::Api".
package Portal::Api;
=head1 NAME
Portal Api
=cut
use strict;
use warnings FATAL => 'all';
# Импортируем сообщения для ошибок
use Control::Errors;
my Control::Errors $Errors;
#**********************************************************
=head2 new($db, $conf, $admin, $lang, $debug, $type)
=cut
#**********************************************************
# Создаём конструктор
sub new {
my ($class, $db, $admin, $conf, $lang, $debug, $type) = @_;
my $self = {
db => $db,
admin => $admin,
conf => $conf,
lang => $lang,
debug => $debug
};
bless($self, $class);
$self->{routes_list} = ();
# Определяем, для чего роутер вызвал наш модуль API
# Соответственно, ли это USER API или ADMIN API
# И записываем routes_list
if ($type eq 'user') {
$self->{routes_list} = $self->user_routes();
}
elsif ($type eq 'admin') {
$self->{routes_list} = $self->admin_routes();
}
$Errors = Control::Errors->new($self->{db}, $self->{admin}, $self->{conf},
# Обязательно обозначить что это за модуль,
# чтобы система могла подгрузить сообщения для ошибок со словаря модуля за потребности
{ lang => $lang, module => 'Portal' }
);
# Сохраняем словарь ошибок в объект, он нам потом будет нужен
$self->{Errors} = $Errors;
return $self;
}
1;
Словарь ошибок
Документация по словарю ошибок
Важная часть - позволяет систематизировать ошибки и избежать разночтений ошибок во время написания кода.
Создаем в корневой папке модуля файл Errors.pm
Он должен выглядеть приблизительно так:
# Пэкэдж должен ОБЯЗАТЕЛЬНО иметь название *модуль*::Errors
# Это позволяет библиотеке словаря ошибок его же найти
package Portal::Errors;
=head1 NAME
Portal::Errors
# Обязательно укажите в pod какая приставка ошибок у модуля.
# Это поможет лучше его найти при разработке.
IDS: 144*
=cut
use strict;
use warnings FATAL => 'all';
#**********************************************************
=head2 errors() - errors list
=cut
#**********************************************************
# Создаём функцию словаря ошибок с анонимным хэшэм.
# В будущем он будет заполнен парами *errno* => *errstr*
sub errors {
return {
# Семизначный код ошибки, с приставкой 144 (модуль Portal) => ключ в словаре ошибки
# Ниже пример
1440001 => 'ERR_PORTAL_NO_SENDER'
};
}
1;
Валидатор
Документация по валидатору
Позволяет проверять полученный реквест от клиента по схеме.
Избегает вызова пути с неправильными параметрами, что повышает устойчивость системы и возможных ошибок.
Также позволяет чётко и подробно расписывать какие именно параметры отсутствуют или неправильные в целом.
Создаём корневой файл Validations.pm с таким примерным содержанием:
# Называем пэкэдж *модуль*::Validations
package Portal::Validations;
use strict;
use warnings FATAL => 'all';
# Вызываем специальный модуль экспортера, для удобного экспортирования констант с описанием в IDE
use Exporter;
use parent 'Exporter';
# Записываем константы валидации в экспорт
our @EXPORT = qw(
POST_PORTAL_ARTICLES
);
# Записываем константы валидации в экспорт
our @EXPORT_OK = qw(
POST_PORTAL_ARTICLES
);
use constant {
# Называем константу *МЕТОД*_ПУТЬ_
# А подробнее про валидатор можно узнать ссылкой выше.
POST_PORTAL_ARTICLES => {
TITLE => {
required => 1,
type => 'string',
min_length => 5,
max_length => 255
},
DATE => {
required => 1,
type => 'date'
},
PORTAL_MENU_ID => {
required => 1,
type => 'string'
},
SHORT_DESCRIPTION => {
type => 'string',
max_length => 600
},
CONTENT => {
type => 'string',
}
},
};
1;
ADMIN API
Создание роутов
Мы создали обязательное начало для API, теперь, если вам это нужно, создаём роуты для ADMIN API:
#**********************************************************
=head2 admin_routes() - Returns available ADMIN API paths
=cut
#**********************************************************
sub admin_routes {
my $self = shift;
return [
{
method => 'GET',
# Абсолютный путь, за которым можно будет достучаться, например billing.url/api.cgi/portal/articles
path => '/portal/articles/',
# Указываем "контроллер" для API /portal/articles/*
controller => 'Portal::Api::admin::Articles',
# Даём ссылку на функцию-эндпойнт контроллера
endpoint => \&Portal::Api::admin::Articles::get_portal_articles,
credentials => [
# Определяем нужные параметры для авторизации.
# ADMIN - API_KEY
# ADMINSID - admin_sid по cookie (в том числе для api_call)
'ADMIN', 'ADMINSID'
]
},
]
}
Мы создали свой первый роут, но нам ещё нужно создать для его base первый контроллер.
Вы, конечно, можете писать функцию сразу же в этом хэндлере, но мы не рекомендуем так делать, поскольку в будущем вам станет неудобно это поддерживать.
Создание контроллера
Соответственно, как наши пути будут в /portal/articles/* и всё что с этим связано, и мы находимся в ADMIN API, то рекомендуем создать файл за такой схемой:Api/*тип API*/*Контроллер*.pmнапример Api/admin/Articles.pm
Со следующим содержанием:
package Portal::Api::admin::Articles;
=head1 NAME
Portal articles manage
# Рекомендуем в подах записывать к каким
# группам эндпойнтов относится данный контроллер
Endpoints:
/portal/articles/*
=cut
use strict;
use warnings FATAL => 'all';
use Control::Errors;
# Импортируем объект Portal для работы с базой
# он должен находиться в /usr/abills/Abills/mysql/Portal.pm
use Portal;
my Control::Errors $Errors;
my Portal $Portal;
my Portal::Misc::Attachments $Attachments;
#**********************************************************
=head2 new($db, $admin, $conf)
=cut
#**********************************************************
sub new {
my ($class, $db, $admin, $conf, $attr) = @_;
my $self = {
db => $db,
admin => $admin,
conf => $conf,
attr => $attr
};
bless($self, $class);
$Portal = Portal->new($db, $admin, $conf);
$Attachments = Portal::Misc::Attachments->new($self->{db}, $self->{admin}, $self->{conf});
# Определяем словарь ошибок, который нам пришёл выше
$Errors = $self->{attr}->{Errors};
return $self;
}
#**********************************************************
=head2 get_portal_articles($path_params, $query_params)
# Всегда пишите в подах Endpoint *METHOD* *path*
# Это позволит легче искать путь во время разработки.
Endpoint GET /portal/articles
=cut
#**********************************************************
sub get_portal_articles {
my $self = shift;
my ($path_params, $query_params) = @_;
# Определяем системные параметры, сортировки, пагинации
my %PARAMS = (
COLS_NAME => 1,
PAGE_ROWS => $query_params->{PAGE_ROWS} ? $query_params->{PAGE_ROWS} : 25,
SORT => $query_params->{SORT} ? $query_params->{SORT} : 1,
PG => $query_params->{PG} ? $query_params->{PG} : 0,
DESC => $query_params->{DESC},
);
# Даём возможность сортировки с помощью ?filename&file_size&file_type
foreach my $param (keys %{$query_params}) {
$query_params->{$param} = ($query_params->{$param} || "$query_params->{$param}" eq '0')
? $query_params->{$param}
: '_SHOW';
}
# Вызываем функцию для извлечения списка из базы, с нашими параметрами и которые определены вызовом
# которые будут внутри обрабатываться search_former my $list = $Portal->portal_articles_list({ %$query_params, %PARAMS });
my @result = map {
my $article_sublink = $_->{permalink} || $_->{id};
my $picture_link = $_->{picture} ? $self->_portal_picture_link($_->{picture}) : '';
$_->{url} = $self->_portal_news_link($article_sublink);
$_->{picture} = $picture_link;
$_
} @$list; # Рекомендация: когда вы создаёте путь, который возвращает массив, то возвращайте
# его с объектом с ключём list, а в total возвращайте общее число айтемов - это позволит работать пагинации
return {
list => \@result,
total => $Portal->{TOTAL}
};
}
USER API
Создание роутов
Если нужно USER API, заполняем и это:
#**********************************************************
=head2 user_routes() - Returns available USER API paths
=cut
#**********************************************************
sub user_routes {
my $self = shift;
return [
{
method => 'GET',
# Для USER API ОБЯЗАТЕЛЬНО начинаем абсолютный путь с /user/*.
path => '/user/portal/menu/',
# Подключаем "контроллер" для API /user/portal/*
controller => 'Portal::Api::user::News',
# Даём ссылку на функцию-эндпойнт контроллера
endpoint => \&Portal::Api::user::News::get_user_portal_news,
credentials => [
# Определяем нужные параметры для авторизации.
# USER - авторизация по header, полученном с /user/login
# USERSID - авторизация по cookie (в том числе для api_call)
# PUBLIC - без авторизации
# Тоесть, в данном случае путь может работать как и с авторизованными пользователями, так и нет.
# Внутри хэндлера можно определять какой пользователь, об этом ниже.
'USER', 'USERSID', 'PUBLIC'
]
},
]
}
Создание контроллера
package Portal::Api::user::News;
=head1 NAME
User Portal
# Рекомендуем в подах записывать к каким
# группам эндпойнтов относится данный контроллер
Endpoints:
/user/portal/news*
/user/portal/menu
=cut
use strict;
use warnings FATAL => 'all';
use Control::Errors;
# Импортируем объект Portal для работы с базой
# он должен находиться в /usr/abills/Abills/mysql/Portal.pm
use Portal;
my Portal $Portal;
my Control::Errors $Errors;
#**********************************************************
=head2 new($db, $admin, $conf)
=cut
#**********************************************************
sub new {
my ($class, $db, $admin, $conf, $attr) = @_;
my $self = {
db => $db,
admin => $admin,
conf => $conf,
attr => $attr
};
bless($self, $class);
$Portal = Portal->new($db, $admin, $conf);
# Определяем словарь ошибок, который нам пришёл выше
$Errors = $self->{attr}->{Errors};
return $self;
}
#**********************************************************
=head2 get_user_portal_news($path_params, $query_params)
Endpoint GET /user/portal/news
=cut
#**********************************************************
sub get_user_portal_news {
my $self = shift;
my ($path_params, $query_params) = @_;
# Не обязательно писать всю логику прямо внутри эндпоинта, как в примере с ADMIN API
# Вы можете делить логику в бизнес-функции, для сокращения использования.
# Но для простоты понимания, с самого начала лучше писать всё в эндпоинтах
return $self->_portal_menu({
# Если пользователь авторизован - в $path_params->{uid} будет UID пользователя.
UID => $path_params->{uid} || '',
DOMAIN_ID => $query_params->{DOMAIN_ID},
PORTAL_MENU_ID => $query_params->{PORTAL_MENU_ID},
MAIN_PAGE => $query_params->{MAIN_PAGE},
LIST => 1
});
}
1;