Bitrix controller error

Контроллеры - это часть MVC архитектуры [ https://dev.1c-bitrix.ru/learning/course/?COURSE_ID=43&LESSON_ID=2817 ] , которая отвечает за обработку запроса и гене...

Контроллеры — это часть MVC архитектуры, которая отвечает за обработку запроса и генерирование ответа.

Действия

Контроллеры состоят из действий, которые являются основной сутью и их в конечном итоге запрашивает пользователь для получения результата. В одном контроллере может быть одно или несколько действий. Для примера сделаем контроллер с двумя действиями item.add и item.view в модуле example.

Первый шаг — создать в корне модуля файл .settings.php.

<?php
//modules/vendor.example/.settings.php
return [
	'controllers' => [
		'value' => [
			'defaultNamespace' => '\Vendor\Example\Controller',
		],
		'readonly' => true,
	]
];

Далее создаётся сам файл контроллера (смотри подробное описание доступных методов):

namespace VendorExampleController;

use BitrixMainError;

class Item extends BitrixMainEngineController
{
	public function addAction(array $fields):? array
	{
		$item = Item::add($fields);

		if (!$item)
		{
			$this->addError(new Error('Could not create item.', {код_ошибки}));

			return null;
		}

		return $item->toArray();
	}

	public function viewAction($id):? array
	{
		$item = Item::getById($id);
		if (!$item)
		{
			$this->addError(new Error('Could not find item.', {код_ошибки}));
					
			return null;
		} 

		return $item->toArray();
	}
}

В действии add (определенным методом Item::addAction) сначала идёт попытка создания некого Item по переданным $fields.

Примечание. Массив $fields получается путем автоматического связывания параметров метода и $_REQUEST.

Если не удалось выполнить создание по каким-то причинам, то возвращаем null и наполняем ошибками сам контроллер. В этом случае ядро сгенерирует ответ:

{
	"status": "error", //обратите внимание, что статус автоматически сменился
	"data": null,
	"errors": [
		{
			"message": "Could not create item.",
			"code": {код}
		}
	]
}

Иначе добавляем Item и возвращаем из действия его представление в виде массива $item->toArray(). Таким образом ядро сгенерирует ответ:

{
	"status": "success",
	"data": {
		"ID": 1,
		"NAME": "Nobody",
		//...поля элемента
	},
	"errors": null
}

В целом, действие может вернуть не просто скаляры, но и объекты.

В действии view (определенным методом Item::viewAction) сначала идёт попытка загрузки некого объекта Item по переданному параметру $id. Важно заметить, что $id будет автоматически получен из $_POST['id'] или $_GET['id'].

Если данный параметр не найден, то ядро сгенерирует ответ с ошибкой:

{
	"status": "error",
	"data": null,
	"errors": [
		{
			"message": "Could not find value for parameter {id}",
			"code": 0
		}
	]
}

Как обратиться к действию контроллера?

Для вызова конкретного аякс-действия нужно знать и пользоваться соглашением по именованию. В нашем случае: Item::addAction -> vendor:example.item.add Item::viewAction -> vendor:example.item.view.

vendor:example.item.add, vendor:example.item.view можно использовать для вызова действий через BX.ajax.runAction:

BX.ajax.runAction('vendor:example.item.add', {
	data: {
		fields: {
            ID: 1,
            NAME: "test"
        } 
	}
}).then(function (response) {
	console.log(response);
	/**
	{
		"status": "success", 
		"data": {
			"ID": 1,
			"NAME": "test"
		}, 
		"errors": []
	}
	**/			
}, function (response) {
	//сюда будут приходить все ответы, у которых status !== 'success'
	console.log(response);
	/**
	{
		"status": "error", 
		"errors": [...]
	}
	**/				
});

Либо можно получить ссылку на действие и послать http-запрос самостоятельно.

/** @var BitrixMainWebUri $uri **/
$uri = BitrixMainEngineUrlManager::getInstance()->create('vendor:example.item.view', ['id' => 1]);
echo $uri;
// /bitrix/services/main/ajax.php?action=vendor:example.item.view&id=1
// выполняем GET-запрос

Создание контроллеров и действий

Создание контроллеров

Контроллеры должны быть унаследованы от BitrixMainEngineController или его потомков. Контроллеры могут располагаться внутри модуля, либо внутри компонента в файле ajax.php и быть контроллером для компонента.

Создание действий

Создание действий, это создание просто методов в конкретном контроллере. Метод обязан быть public и иметь суффикс Action.

namespace VendorExampleController;

class Item extends BitrixMainEngineController
{
	public function addAction(array $fields)
	{
        //...
	}
}

Возвращаемое значение действия представляет собой данные ответа, которые будут высланы клиенту.

Если действие возвращает BitrixMainHttpResponse или его наследников, то данный объект и будет отправлен клиенту. Если действие возвращает некие данные, то они должны приводиться к скаляру или объекту, который после будет превращен в JSON и на основе него будет сформирован BitrixMainEngineResponseAjaxJson.

В целом действие может вернуть не просто скаляры, но и объекты, которые реализуют следующие интерфейсы:

  • JsonSerializable
  • BitrixMainTypeContractArrayable
  • BitrixMainTypeContractJsonable

Либо конкретные наследники BitrixMainHttpResponse:

  • Redirect
  • JSON
  • Типовой JSON
  • Файл b_file
  • Ресайзенное изображение
  • Архив
  • Компонент

Создание классов-действий

Есть возможность создавать классы-действия, которые унаследованы от BitrixMainEngineAction. Подобная возможность может потребоваться, когда необходимо повторно использовать логику в нескольких контроллерах. Например, если реализуется одинаковый протокол обмена в разных модулях (стандартный поиск, выполнение пошаговых действий с прогрессом и тому подобное.). Для использования нужно описать в карте конфигурации контроллера метод configureActions:

class Test extends BitrixMainEngineController
{
	public function configureActions()
	{
		return [
			'testoPresto' => [
				'class' => TestAction::class,
				'configure' => [
					'who' => 'Me',
				],
			],
		];
	}
}

И вот сам TestAction:

<?php

use BitrixMainEngineAction;

class TestAction extends Action
{
	protected $who;

	//метод для дополнительного конфигурирования из контроллера. Если требуется установить
	//какие-то значения во внутреннее состояние
	public function configure($params)
	{
		parent::configure($params);

		$this->who = $params['who']?: 'nobody';
	}

	//основной метод работы. Параметры так же автоматически связываются, как и в методе
	//аякс-действии
	public function run($objectId = null)
	{
		return "Test action is here! Do you know object with id {$objectId}? Mr. {$this->who}";
	}
}

Для вызова этого действия нужно обращаться к нему testoPresto, как описано в карте конфигурации. Класс-действие поддерживает пре- и пост-фильтры и по сути ничем не отличается от обычного метода-действия. Смысл метода run() аналогичен другим методам аякс-действиям.

Использование контроллеров внутри компонентов

Предпочтительно создавать и использовать классы контроллеров, которые располагаются в модулях, как указано в данной статье, так как позволяет лучше организовать повторное использование вспомогательного кода и бизнес-логики.

В простых случаях, если компонент самодостаточен и не используется активно с API-модуля, то можно использовать контроллеры внутри компонента.

Жизненный цикл контроллера

При обработке запроса Application создаёт контроллер на основе соглашения по именованию. Далее работу выполняет контроллер:

  • Controller::init() будет вызван после того, как контроллер создан и сконфигурирован.
  • Контроллер создает объект действия
    • Если действие не удалось создать, выкидывается исключение.
  • Контроллер вызывает метод подготовки параметров Controller::prepareParams.
  • Контроллер вызывает метод Controller::processBeforeAction(Action $action), в случае возврата true выполнение продолжается.
  • Контроллер выкидывает событие модуля main {полное_имя_класс_контроллера}::onBeforeAction, в случае возврата EventResult не равном EventResult::SUCCESS выполнение блокируется. На данном событии выполняются префильтры.
  • Контроллер запускает действие
    • Параметры действия будут сопоставлены с данными из запроса
  • Контроллер выкидывает событие модуля main {полное_имя_класс_контроллера}::onAfterAction. На данном событии выполняются постфильтры.
  • Контроллер вызывает методController::processAfterAction(Action $action, $result).
  • Приложение получает результат выполнения действия и если это данные, то создает BitrixMainEngineResponseAjaxJson с этими данными, либо отправляет объект ответа от действия.
  • Приложение вызывает метод Controller::finalizeResponse($response), передавая финальный вариант ответа, который будет отправлен пользователю после всех событий и подготовок.
  • Вывод $response пользователю.

Несколько namespaces

Указание нескольких namespaces в модуле.

В .settings.php можно указать несколько namespaces, помимо defaultNamespace. Это может быть необходимо, когда контроллеры расположены рядом со своими бизнес-сущностями. Например, в некотором модуле «Диск» у нас есть интеграция с облаками.

<?php
return [
	'controllers' => [
		'value' => [
			'namespaces' => [
				'\Bitrix\Disk\CloudIntegration\Controller' => 'cloud', //cloud - это альяс
			],
			'defaultNamespace' => '\Bitrix\Disk\Controller',
		],
		'readonly' => true,
	]
];

Теперь у нас доступны для вызова контроллеры, которые расположены в обоих namespaces. Оба из них поддерживают вызов через полное имя действия и через сокращенную запись, так как у нас есть альяс cloud.

Равносильны:

disk.CloudIntegration.Controller.GoogleFile.get
disk.cloud.GoogleFile.get

disk.Controller.File.get
disk.File.get

Вызов модульного контроллера с
подписанными параметрами компонента

Вызов модульного контроллера с подписанными параметрами компонента

В случае, если вам нужно обратиться к контроллеру, реализованному в модуле, из контекста компонента, прокинув подписанные параметры, используйте следующий способ:

BX.ajax.runAction('socialnetwork.api.user.stresslevel.get', {
    signedParameters: this.signedParameters, // результат $this->getComponent()->getSignedParameters() 
    data: {
        c: myComponentName, // например, 'bitrix:intranet.user.profile', параметры которого нам будут нужны
        fields: {
            //..
        }
    }
});

После этого внутри кода действия используйте:

<?php

    //...
    public function getAction(array $fields)
    {
        //внутри распакованный, проверенный массив параметров
        $parameters = $this->getUnsignedParameters();
        
        return $parameters['level'] * 100;
    }

Контроллеры — это часть MVC архитектуры, которая отвечает за обработку запроса и генерирование ответа. Сразу оговоримся, что дальше речь пойдет про компоненты-контроллеры в контексте Bitrix Framework.

Битрикс-контроллер принимает от клиента запрос и возвращает JSON с результатом или ошибкой. Классы-контроллеры содержат одно или несколько методов-действий и являются надстройкой над обычными компонентами, поэтому их нужно размещать в файле class.php компонента.

Бекенд

Тут все просто: в компоненте нужно реализовать интерфейс Controllerable, в ктором есть метод configureActions(). Этот метод возвращает массив с действиями которые можно вызвать. Например, для действия send нужно будет создать метод sendAction.

Метод-действие возвращает массив, который затем отдается клиенту в виде JSON.

Если требуется отдавать на сторону клиента ошибки, то нужно реализовать интерфейс Errorable, в котором есть методы getErrors() и getErrorByCode(). Также нужно будет реализовать коллекцию ошибок ErrorCollection, ее удобнее всего создать в методе onPrepareComponentParams().

В качестве простого примера создадим контроллер для формы обратной связи. Компонент пусть называется machaon:feedback, код контроллера разместится в файле /local/components/machaon/feedback/class.php.

<?php
//local/components/machaon/feedback/class.php

namespace MachaonComponents;

use BitrixMainError;
use BitrixMainErrorable;
use BitrixMainErrorCollection;
use BitrixMainEngineActionFilter;
use BitrixMainEngineContractControllerable;
use CBitrixComponent;

class FeedbackComponent extends CBitrixComponent implements Controllerable, Errorable
{
    protected ErrorCollection $errorCollection;

    public function onPrepareComponentParams($arParams)
    {
        $this->errorCollection = new ErrorCollection();
        return $arParams;
    }

    public function executeComponent()
    {
        // Метод не будет вызван при ajax запросе
    }

    public function getErrors(): array
    {
        return $this->errorCollection->toArray();
    }

    public function getErrorByCode($code): Error
    {
        return $this->errorCollection->getErrorByCode($code);
    }

    // Описываем действия
    public function configureActions(): array
    {
        return [
            'send' => [
                'prefilters' => [
                    // здесь указываются опциональные фильтры, например:
                    new ActionFilterAuthentication(), // проверяет авторизован ли пользователь
                ]
            ]
        ];
    }

    // Сюда передаются параметры из ajax запроса, навания точно такие же как и при отправке запроса.
    // $_REQUEST['username'] будет передан в $username, $_REQUEST['email'] будет передан в $email и т.д.
    public function sendAction(string $username = '', string $email = '', string $message = ''): array
    {
        try {
            $this->doSomeWork();
            return [
                "result" => "Ваше сообщение принято",
            ];
        } catch (ExceptionsEmptyEmail $e) {
            $this->errorCollection[] = new Error($e->getMessage());
            return [
                "result" => "Произошла ошибка",
            ];
        }
    }
}

Фронтенд

В js-библиотеке Битрикс уже есть функция для отправки запросов. Далее простой пример, где machaon:feedback это имя компонента, send — имя действия, а в data: {} передаются необходимые данные:

BX.ajax.runComponentAction("machaon:feedback", "send", {
    mode: "class",
    data: {
        "email": "vasya@email.tld",
        "username": "Василий",
        "message": "Где мой заказ? Жду уже целый час!"
    }
}).then(function (response) {
    // обработка ответа
});

Если все хорошо, с бэкенда нам вернется:

{
    "status": "success",
    "data": {
        "result": "Письмо отправлено"
    },
    "errors": []
}

Либо сообщение об ошибке:

{
    "status": "error",
    "data": {
        "result": "Произошла ошибка"
    },
    "errors": [{
        "message": "Не заполено поле Email",
        "code": 0,
        "customData": null
    }]
}

Отправка запроса напрямую

Если не хочется использовать BX.ajax.runComponentAction(), можно отправить запрос напрямую, например используя jQuery. Нужно отправить запрос на /bitrix/services/main/ajax.php, он будет выглядеть примерно так:

$.post(
    "/bitrix/services/main/ajax.php?mode=class&c=machaon:feedback&action=send",
    {
        "email": "vasya@email.tld",
        "username": "Василий",
        "message": "Где мой заказ? Жду уже целый час!"
    },
    function (response) {
        console.log(response);
    }
);

В параметре mode передается обязательное значение class, в c передается имя компонента в формате vendor:component, action это запускаемый метод.

Эти параметры обязательно должны передаваться в адресе запроса, иначе происходит ошибка.

email, username и message станут затем параметрами $email, $username и $message в методе sendAction().

Использование ЧПУ

Также можно в urlrewrite.php прописать красивый адрес, например /api/rest-component/<component_vendor>/<component_name>/<action>/.

Для этого добавим в urlrewrite.php следующий массив:

$arUrlRewrite = [
    // ...
    [
        "CONDITION" => "#^/api/rest-component/([a-zA-Z0-9]+)/([a-zA-Z0-9.]+)/([a-zA-Z0-9]+)/?.*#",
        "RULE" => "mode=class&c=$1:$2&action=$3",
        "PATH" => "/bitrix/services/main/ajax.php",
    ],
];

Тогда запрос будет выглядеть понятнее:

function sendFeedback(form) {
    const route = "/api/rest-component/machaon/feedback/send/";
    const data = $(form).serialize();
    $.post(route, data, function (response) {
        console.log(response);
    });
}

Нюансы

Если в configureActions() оставить пустой массив prefilters, то метод-действие будет работать без фильтров.

Но если не указывать ключ prefilters, то будут применены фильтры по умолчанию — ActionFilterAuthentication и ActionFilterCsrf.

public function configureActions(): array
{
    return [
        'send' => [
            'prefilters' => [] // метод sendAction() будет работать без фильтров
        ]
    ];
}
public function configureActions(): array
{
    return [
        'send' => []  // метод sendAction() будет работать у авторизованных пользователей, которые передали CSRF-токен
    ];
}

Пример ошибки когда пользователь не авторизован и не передал токен:

{
  "status": "error",
  "data": null,
  "errors": [
    {
      "message": "Необходимо авторизоваться на сайте",
      "code": "invalid_authentication",
      "customData": null
    },
    {
      "message": "Invalid csrf token",
      "code": "invalid_csrf",
      "customData": {
        "csrf": "dc8adda3ac983217623cf1196dfc5c61"
      }
    }
  ]
}

Если требуется CSRF-защита, то для фильтра BitrixMainEngineActionFilterCsrf нужно передать идентификатор сессии в параметре sessid.

На бэкенде его можно получить функцией bitrix_sessid(), на фронтенде — BX.bitrix_sessid().

При использовании BX.ajax.runComponentAction() сессия передается автоматически.

Другие фильтры можно посмотреть в документации

Открытые члены

  __construct (Request $request=null)
 
  forward ($controller, string $actionName, array $parameters=null)
 
  getConfigurationOfActions ()
 
  getModuleId ()
 
  isLocatedUnderPsr4 ()
 
  getActionUri ($actionName, array $params=array(), $absolute=false)
 
  getUnsignedParameters ()
 
  getCurrentUser ()
 
  setCurrentUser (CurrentUser $currentUser)
 
  convertKeysToCamelCase ($data)
 
  listNameActions ()
 
  configureActions ()
 
  getAutoWiredParameters ()
 
  getPrimaryAutoWiredParameter ()
 
  getDefaultAutoWiredParameters ()
 
  getRequest ()
 
  getScope ()
 
  setScope ($scope)
 
  getSourceParametersList ()
 
  setSourceParametersList ($sourceParametersList)
 
  run ($actionName, array $sourceParametersList)
 
  finalizeResponse (Response $response)
 
  generateActionMethodName ($action)
 
  redirectTo ($url)
 
  getErrors ()
 
  getErrorByCode ($code)
 

Поля данных

const  SCOPE_REST = ‘rest’
 
const  SCOPE_AJAX = ‘ajax’
 
const  SCOPE_CLI = ‘cli’
 
const  EVENT_ON_BEFORE_ACTION = ‘onBeforeAction’
 
const  EVENT_ON_AFTER_ACTION = ‘onAfterAction’
 
const  ERROR_REQUIRED_PARAMETER = ‘MAIN_CONTROLLER_22001’
 
const  ERROR_UNKNOWN_ACTION = ‘MAIN_CONTROLLER_22002’
 
const  EXCEPTION_UNKNOWN_ACTION = 22002
 
const  METHOD_ACTION_SUFFIX = ‘Action’
 

Защищенные члены

  init ()
 
  getFilePath ()
 
  processUnsignedParameters ()
 
  getSaltToUnsign ()
 
  writeToLogException (Throwable $e)
 
  collectDebugInfo ()
 
  logDebugInfo ()
 
  prepareParams ()
 
  processBeforeAction (Action $action)
 
  shouldDecodePostData (Action $action)
 
  decodePostData ()
 
  triggerOnBeforeAction (Action $action)
 
  processAfterAction (Action $action, $result)
 
  triggerOnAfterAction (Action $action, $result)
 
  create ($actionName)
 
  buildActionInstance ($actionName, array $config)
 
  existsAction ($actionName)
 
  getDefaultPreFilters ()
 
  getDefaultPostFilters ()
 
  buildFilters (array $config=null)
 
  appendFilters (array $filters, array $filtersToAppend)
 
  removeFilters (array $filters, array $filtersToRemove)
 
  getActionConfig ($actionName)
 
  setActionConfig ($actionName, array $config=null)
 
  runProcessingThrowable (Throwable $throwable)
 
  runProcessingException (Exception $e)
 
  runProcessingError (Error $error)
 
  runProcessingBinderThrowable (BinderArgumentException $e)
 
  buildErrorFromException (Exception $e)
 
  buildErrorFromPhpError (Error $error)
 
  runProcessingIfUserNotAuthorized ()
 
  runProcessingIfInvalidCsrfToken ()
 
  addError (Error $error)
 
  addErrors (array $errors)
 

Защищенные данные

  $errorCollection
 
  $request
 
  $configurator
 

См. определение в файле controller.php строка 29

◆ __construct()

__construct ( Request  $request = null )

Constructor Controller.

Аргументы

Переопределяется в Tracking.

См. определение в файле controller.php строка 76

77 {

78 $this->scope = self::SCOPE_AJAX;

79 $this->errorCollection = new ErrorCollection;

81 $this->configurator = new Configurator();

82 $this->converter = Converter::toJson();

83

84 $this->init();

85 }

◆ addError()

addError ( Error  $error )
protected

Adds error to error collection.

Аргументы
Возвращает
$this

См. определение в файле controller.php строка 972

973 {

974 $this->errorCollection[] = $error;

975

976 return $this;

977 }

◆ addErrors()

addErrors ( array  $errors )
protected

Adds list of errors to error collection.

Аргументы
Возвращает
$this

См. определение в файле controller.php строка 985

986 {

987 $this->errorCollection->add($errors);

988

989 return $this;

990 }

◆ appendFilters()

appendFilters ( array  $filters,
array  $filtersToAppend 
)
finalprotected

См. определение в файле controller.php строка 776

777 {

778 return array_merge($filters, $filtersToAppend);

779 }

◆ buildActionInstance()

buildActionInstance (   $actionName,
array  $config 
)
finalprotected
См. также
Action::__construct

См. определение в файле controller.php строка 638

639 {

640 if (isset($config[‘callable’]))

641 {

642 $callable = $config[‘callable’];

643 if (!is_callable($callable))

644 {

645 throw new ArgumentTypeException(‘callable’, ‘callable’);

646 }

647

648 return new ClosureAction($actionName, $this, $callable);

649 }

650 elseif (empty($config[‘class’]))

651 {

652 throw new SystemException(

653 «Could not find class in description of {$actionName} in {$this::className()} to create instance»,

654 self::EXCEPTION_UNKNOWN_ACTION

655 );

656 }

657

659 $action = new $config[‘class’]($actionName, $this, $config);

660

661 return $action;

662 }

◆ buildErrorFromException()

buildErrorFromException ( Exception  $e )
protected

См. определение в файле controller.php строка 924

925 {

926 if ($e instanceof ArgumentNullException)

927 {

928 return new Error($e->getMessage(), self::ERROR_REQUIRED_PARAMETER);

929 }

930

931 return new Error($e->getMessage(), $e->getCode());

932 }

◆ buildErrorFromPhpError()

buildErrorFromPhpError ( Error  $error )
protected

См. определение в файле controller.php строка 934

935 {

936 return new Error($error->getMessage(), $error->getCode());

937 }

◆ buildFilters()

buildFilters ( array  $config = null )
finalprotected

Builds filter by config. If there is no config, then we use default filters

См. также
BitrixMainEngineController::getDefaultPreFilters() and
BitrixMainEngineController::getDefaultPostFilters(). If now is POST query and there is no csrf check in config, then we add it.
Аргументы
Возвращает
array|null

См. определение в файле controller.php строка 715

716 {

717 if ($config === null)

718 {

719 $config = array();

720 }

721

722 if (!isset($config[‘prefilters’]))

723 {

724 $config[‘prefilters’] = $this->configurator->wrapFiltersClosure(

725 $this->getDefaultPreFilters()

726 );

727 }

728 if (!isset($config[‘postfilters’]))

729 {

730 $config[‘postfilters’] = $this->configurator->wrapFiltersClosure(

732 );

733 }

734

735 $hasPostMethod = $hasCsrfCheck = false;

736 foreach ($config[‘prefilters’] as $filter)

737 {

738 if ($filter instanceof ActionFilterHttpMethod && $filter->containsPostMethod())

739 {

740 $hasPostMethod = true;

741 }

742 if ($filter instanceof ActionFilterCsrf)

743 {

744 $hasCsrfCheck = true;

745 }

746 }

747

748 if ($hasPostMethod && !$hasCsrfCheck && $this->request->isPost())

749 {

750 $config[‘prefilters’][] = new ActionFilterCsrf;

751 }

752

753 if (!empty($config[‘-prefilters’]))

754 {

755 $config[‘prefilters’] = $this->removeFilters($config[‘prefilters’], $config[‘-prefilters’]);

756 }

757

758 if (!empty($config[‘-postfilters’]))

759 {

760 $config[‘postfilters’] = $this->removeFilters($config[‘postfilters’], $config[‘-postfilters’]);

761 }

762

763 if (!empty($config[‘+prefilters’]))

764 {

765 $config[‘prefilters’] = $this->appendFilters($config[‘prefilters’], $config[‘+prefilters’]);

766 }

767

768 if (!empty($config[‘+postfilters’]))

769 {

770 $config[‘postfilters’] = $this->appendFilters($config[‘postfilters’], $config[‘+postfilters’]);

771 }

772

773 return $config;

774 }

appendFilters(array $filters, array $filtersToAppend)

removeFilters(array $filters, array $filtersToRemove)

◆ className()

static className ( )
staticfinal

Returns the fully qualified name of this class.

Возвращает
string

См. определение в файле controller.php строка 67

68 {

69 return get_called_class();

70 }

◆ collectDebugInfo()

collectDebugInfo ( )
finalprotected

◆ configureActions()

Возвращает
array

Замещает Controllerable.

Переопределяется в Analytics, File, CalendarAjax, CalendarEntryAjax, ResourceBookingAjax, SyncAjax, ProductSelector, SkuTree, HtmlEditorAjax, Call, Disk, Secretary, AuthCode, Export, LoadExt, PhoneAuth, PhoneNumber, QrCodeAuth, Rating, ComponentController, DefaultController, Widget, Entity, FacebookConversion, AuthFlow, Grabber, File, Csv, Csv, Collector и FileUploader.

См. определение в файле controller.php строка 287

288 {

289 return [];

290 }

◆ convertKeysToCamelCase()

convertKeysToCamelCase (   $data )

Converts keys of array to camel case notation.

См. также
BitrixMainEngineResponseConverter::OUTPUT_JSON_FORMAT
Аргументы
Возвращает
array|mixed|string

См. определение в файле controller.php строка 257

258 {

259 return $this->converter->process($data);

260 }

◆ create()

create (   $actionName )
protected

Переопределяется в ComponentController и Base.

См. определение в файле controller.php строка 605

606 {

607 $config = $this->getActionConfig($actionName);

608 $methodName = $this->generateActionMethodName($actionName);

609

610 if (method_exists($this, $methodName))

611 {

612 $method = new ReflectionMethod($this, $methodName);

613 if ($method->isPublic() && mb_strtolower($method->getName()) === mb_strtolower($methodName))

614 {

615 return new InlineAction($actionName, $this, $config);

616 }

617 }

618 else

619 {

620 if (!$config && ($this instanceof ContractFallbackActionInterface))

621 {

622 return new FallbackAction($actionName, $this, []);

623 }

624 if (!$config)

625 {

626 throw new SystemException(

627 «Could not find description of {$actionName} in {$this::className()}»,

628 self::EXCEPTION_UNKNOWN_ACTION

629 );

630 }

631

633 }

634

635 return null;

636 }

buildActionInstance($actionName, array $config)

◆ decodePostData()

decodePostData ( )
finalprotected

См. определение в файле controller.php строка 516

516 : void

517 {

518 CUtil::jSPostUnescape();

519 $this->request->addFilter(new PostDecodeFilter);

520 }

◆ existsAction()

existsAction (   $actionName )
finalprotected

См. определение в файле controller.php строка 664

665 {

666 try

667 {

668 $action = $this->create($actionName);

669 }

670 catch (SystemException $e)

671 {

673 {

674 throw $e;

675 }

676 }

677

678 return isset($action);

679 }

const EXCEPTION_UNKNOWN_ACTION

◆ finalizeResponse()

Finalizes response. The method will be invoked when HttpApplication will be ready to send response to client. It’s a final place where Controller can interact with response.

Аргументы
Возвращает
void

См. определение в файле controller.php строка 581

◆ forward()

forward (   $controller,
string  $actionName,
array  $parameters = null 
)
Аргументы
Controller $controller
string $actionName
array | null $parameters
Возвращает
HttpResponse|mixed
Исключения

См. определение в файле controller.php строка 95

96 {

97 if (is_string($controller))

98 {

99 $controller = new $controller;

100 }

101

102

103 $controller->request = $this->getRequest();

104 $controller->setScope($this->getScope());

105 $controller->setCurrentUser($this->getCurrentUser());

106

107

108 $result = $controller->run(

109 $actionName,

110 $parameters === null ? $this->getSourceParametersList() : [$parameters]

111 );

112

113 $this->addErrors($controller->getErrors());

114

115 return $result;

116 }

◆ generateActionMethodName()

generateActionMethodName (   $action )
final

См. определение в файле controller.php строка 600

601 {

602 return $action . self::METHOD_ACTION_SUFFIX;

603 }

◆ getActionConfig()

getActionConfig (   $actionName )
finalprotected

См. определение в файле controller.php строка 849

850 {

851 $listOfActions = array_change_key_case($this->configurationOfActions, CASE_LOWER);

852 $actionName = mb_strtolower($actionName);

853

854 if (!isset($listOfActions[$actionName]))

855 {

856 return null;

857 }

858

859 return $listOfActions[$actionName];

860 }

◆ getActionUri()

getActionUri (   $actionName,
array  $params = array(),
  $absolute = false 
)
final

Returns uri for ajax end point for the action name. It’s a helper, which uses relative action name without controller name.

Аргументы
string $actionName Action name. It’s a relative action name without controller name.
array $params Parameters for creating uri.
bool $absolute
Возвращает
BitrixMainWebUri

См. определение в файле controller.php строка 176

177 {

178 if (mb_strpos($this->getFilePath(), ‘/components/’) === false)

179 {

180 return UrlManager::getInstance()->createByController($this, $actionName, $params, $absolute);

181 }

182

183 return UrlManager::getInstance()->createByComponentController($this, $actionName, $params, $absolute);

184 }

◆ getAutoWiredParameters()

getAutoWiredParameters ( )

◆ getConfigurationOfActions()

getConfigurationOfActions ( )
final
Возвращает
array|null

См. определение в файле controller.php строка 131

132 {

133 return $this->configurationOfActions;

134 }

◆ getCurrentUser()

◆ getDefaultAutoWiredParameters()

getDefaultAutoWiredParameters ( )
final
Возвращает
Parameter[]

См. определение в файле controller.php строка 311

312 {

313 return [];

314 }

◆ getDefaultPostFilters()

getDefaultPostFilters ( )
protected

Returns default post-filters for action.

Возвращает
array

См. определение в файле controller.php строка 700

701 {

702 return array();

703 }

◆ getDefaultPreFilters()

getDefaultPreFilters ( )
protected

Returns default pre-filters for action.

Возвращает
array

Переопределяется в Mode, ProductForm, ProductSelector, SkuTree, StoreSelector, DefaultElement, Element, Disk, Cookies, DiskFile, Landing, Note, Address, Format, Location, Source, UserFieldConfig, JsonController, BaseReceiver, Application, Configuration, Import, File, ConsentPreview, Base, EntitySelector, Feedback и FileUploader.

См. определение в файле controller.php строка 685

686 {

687 return array(

688 new ActionFilterAuthentication(),

689 new ActionFilterHttpMethod(

690 array(ActionFilterHttpMethod::METHOD_GET, ActionFilterHttpMethod::METHOD_POST)

691 ),

692 new ActionFilterCsrf(),

693 );

694 }

◆ getErrorByCode()

getErrorByCode (   $code )
final

Getting once error with the necessary code.

Аргументы
string $code Code of error.
Возвращает
Error

Замещает Errorable.

См. определение в файле controller.php строка 1006

1007 {

1008 return $this->errorCollection->getErrorByCode($code);

1009 }

◆ getErrors()

Getting array of errors.

Возвращает
Error[]

Замещает Errorable.

См. определение в файле controller.php строка 996

997 {

998 return $this->errorCollection->toArray();

999 }

◆ getFilePath()

getFilePath ( )
finalprotected

См. определение в файле controller.php строка 155

156 {

157 if (!$this->filePath)

158 {

159 $reflector = new ReflectionClass($this);

160 $this->filePath = preg_replace(‘#[\/]+#’, ‘/’, $reflector->getFileName());

161 }

162

163 return $this->filePath;

164 }

◆ getFullEventName()

static getFullEventName (   $eventName )
staticfinal

См. определение в файле controller.php строка 468

469 {

470 return get_called_class() . ‘::’ . $eventName;

471 }

◆ getModuleId()

Returns module id. Tries to guess module id by file path and function

См. также
getModuleId().
Возвращает
string

См. определение в файле controller.php строка 142

143 {

144 return getModuleId($this->getFilePath());

145 }

◆ getPrimaryAutoWiredParameter()

getPrimaryAutoWiredParameter ( )
Возвращает
Parameter|null

Переопределяется в Basket.

См. определение в файле controller.php строка 303

304 {

305 return null;

306 }

◆ getRequest()

◆ getSaltToUnsign()

getSaltToUnsign ( )
protected

Tries to find salt from request. It’s «c» (component name) in general.

Возвращает
string|null

См. определение в файле controller.php строка 221

222 {

223 foreach ($this->getSourceParametersList() as $source)

224 {

225 if (isset($source[‘c’]) && is_string($source[‘c’]))

226 {

227 return $source[‘c’];

228 }

229 }

230

231 return null;

232 }

◆ getScope()

Возвращает
string

См. определение в файле controller.php строка 332

333 {

334 return $this->scope;

335 }

◆ getSourceParametersList()

getSourceParametersList ( )
final
Возвращает
array

См. определение в файле controller.php строка 352

353 {

354 return $this->sourceParametersList;

355 }

◆ getUnsignedParameters()

getUnsignedParameters ( )
final
Возвращает
mixed

См. определение в файле controller.php строка 189

190 {

191 return $this->unsignedParameters;

192 }

◆ init()

◆ isLocatedUnderPsr4()

isLocatedUnderPsr4 ( )
final

См. определение в файле controller.php строка 147

147 : bool

148 {

149

150 $firstLetter = mb_substr(basename($this->getFilePath()), 0, 1);

151

152 return $firstLetter !== mb_strtolower($firstLetter);

153 }

◆ listNameActions()

listNameActions ( )
final

Returns list of all

Возвращает
array

См. определение в файле controller.php строка 266

267 {

268 $actions = array_keys($this->getConfigurationOfActions());

269 $lengthSuffix = mb_strlen(self::METHOD_ACTION_SUFFIX);

270

271 $class = new ReflectionClass($this);

272 foreach ($class->getMethods(ReflectionMethod::IS_PUBLIC) as $method)

273 {

274 $probablySuffix = mb_substr($method->getName(), -$lengthSuffix);

275 if ($probablySuffix === self::METHOD_ACTION_SUFFIX)

276 {

277 $actions[] = mb_strtolower(mb_substr($method->getName(), 0, -$lengthSuffix));

278 }

279 }

280

281 return array_unique($actions);

282 }

◆ logDebugInfo()

logDebugInfo ( )
finalprotected

◆ prepareParams()

prepareParams ( )
protected

Prepare params before process action.

Возвращает
bool

См. определение в файле controller.php строка 496

497 {

498 return true;

499 }

◆ processAfterAction()

processAfterAction ( Action  $action,
  $result 
)
protected

Common operations after process action. If the method returns void or null it means that we don’t want to modify $result.

Аргументы
Возвращает
HttpResponse|mixed|void

Переопределяется в UserFieldConfig.

См. определение в файле controller.php строка 570

◆ processBeforeAction()

processBeforeAction ( Action  $action )
protected

◆ processUnsignedParameters()

processUnsignedParameters ( )
finalprotected

См. определение в файле controller.php строка 194

195 {

196 foreach ($this->getSourceParametersList() as $source)

197 {

198 if (isset($source[‘signedParameters’]) && is_string($source[‘signedParameters’]))

199 {

200 try

201 {

202 $this->unsignedParameters = ParameterSigner::unsignParameters(

203 $this->getSaltToUnsign(),

204 $source[‘signedParameters’]

205 );

206 }

207 catch (BadSignatureException $exception)

208 {}

209

210

211 return;

212 }

213 }

214 }

◆ redirectTo()

◆ removeFilters()

removeFilters ( array  $filters,
array  $filtersToRemove 
)
finalprotected

См. определение в файле controller.php строка 781

782 {

783 $cleanedFilters = [];

784 foreach ($filters as $filter)

785 {

786 $found = false;

787 foreach ($filtersToRemove as $filterToRemove)

788 {

789 if (is_a($filter, $filterToRemove))

790 {

791 $found = true;

792 break;

793 }

794 }

795

796 if (!$found)

797 {

798 $cleanedFilters[] = $filter;

799 }

800 }

801

802 return $cleanedFilters;

803 }

◆ run()

run (   $actionName,
array  $sourceParametersList 
)
final
Аргументы
$actionName
array $sourceParametersList
Возвращает
HttpResponse|mixed
Исключения

См. определение в файле controller.php строка 376

377 {

378 $this->collectDebugInfo();

379

380 $result = null;

381

382 try

383 {

384 $this->setSourceParametersList($sourceParametersList);

385 $this->processUnsignedParameters();

386

387 $action = $this->create($actionName);

388 if (!$action)

389 {

390 throw new SystemException(«Could not create action by name {$actionName}»);

391 }

392

393 $this->attachFilters($action);

394 if ($this->shouldDecodePostData($action))

395 {

396 $this->decodePostData();

397 }

398

402 {

403 $result = $action->runWithSourceParametersList();

404

405 if ($action instanceof Errorable)

406 {

407 $this->errorCollection->add($action->getErrors());

408 }

409 }

410

413 if ($probablyResult !== null)

414 {

415 $result = $probablyResult;

416 }

417 }

418 catch (Throwable $e)

419 {

421 $this->processExceptionInDebug($e);

422 }

423

425

426 return $result;

427 }

runProcessingThrowable(Throwable $throwable)

processAfterAction(Action $action, $result)

processBeforeAction(Action $action)

triggerOnBeforeAction(Action $action)

triggerOnAfterAction(Action $action, $result)

◆ runProcessingBinderThrowable()

См. определение в файле controller.php строка 902

902 : void

903 {

904 $currentControllerErrors = $this->getErrors();

906 if ($errors)

907 {

908 foreach ($errors as $error)

909 {

910 if (in_array($error, $currentControllerErrors, true))

911 {

912 continue;

913 }

914

915 $this->addError($error);

916 }

917 }

918 else

919 {

921 }

922 }

runProcessingException(Exception $e)

◆ runProcessingError()

runProcessingError ( Error  $error )
protected

См. определение в файле controller.php строка 896

897 {

898

899 $this->errorCollection[] = $this->buildErrorFromPhpError($error);

900 }

◆ runProcessingException()

runProcessingException ( Exception  $e )
protected

Runs processing exception.

Аргументы
Возвращает
void

Переопределяется в Base.

См. определение в файле controller.php строка 890

891 {

892

893 $this->errorCollection[] = $this->buildErrorFromException($e);

894 }

◆ runProcessingIfInvalidCsrfToken()

runProcessingIfInvalidCsrfToken ( )
protected

Runs processing if csrf token is invalid.

Возвращает
void

См. определение в файле controller.php строка 954

955 {

956 $this->errorCollection[] = new Error(‘Invalid csrf token’);

957

958 throw new SystemException(‘Invalid csrf token’);

959 }

◆ runProcessingIfUserNotAuthorized()

runProcessingIfUserNotAuthorized ( )
protected

Runs processing if user is not authorized.

Возвращает
void

См. определение в файле controller.php строка 943

944 {

945 $this->errorCollection[] = new Error(‘User is not authorized’);

946

947 throw new SystemException(‘User is not authorized’);

948 }

◆ runProcessingThrowable()

runProcessingThrowable ( Throwable  $throwable )
protected

См. определение в файле controller.php строка 869

870 {

872 {

873 $this->runProcessingBinderThrowable($throwable);

874 }

875 elseif ($throwable instanceof Exception)

876 {

877 $this->runProcessingException($throwable);

878 }

879 elseif ($throwable instanceof Error)

880 {

882 }

883 }

runProcessingError(Error $error)

◆ setActionConfig()

setActionConfig (   $actionName,
array  $config = null 
)
finalprotected

См. определение в файле controller.php строка 862

863 {

864 $this->configurationOfActions[$actionName] = $config;

865

866 return $this;

867 }

◆ setCurrentUser()

Аргументы

См. определение в файле controller.php строка 245

246 {

247 $this->currentUser = $currentUser;

248 }

◆ setScope()

Аргументы
Возвращает
Controller

См. определение в файле controller.php строка 342

343 {

344 $this->scope = $scope;

345

346 return $this;

347 }

◆ setSourceParametersList()

setSourceParametersList (   $sourceParametersList )
final
Аргументы
array $sourceParametersList
Возвращает
Controller

См. определение в файле controller.php строка 362

363 {

364 $this->sourceParametersList = $sourceParametersList;

365

366 return $this;

367 }

◆ shouldDecodePostData()

shouldDecodePostData ( Action  $action )
protected

См. определение в файле controller.php строка 511

511 : bool

512 {

513 return $this->request->isPost();

514 }

◆ triggerOnAfterAction()

triggerOnAfterAction ( Action  $action,
  $result 
)
finalprotected

См. определение в файле controller.php строка 584

585 {

586 $event = new Event(

587 ‘main’,

588 static::getFullEventName(static::EVENT_ON_AFTER_ACTION),

589 array(

590 ‘result’ => $result,

591 ‘action’ => $action,

592 ‘controller’ => $this,

593 )

594 );

595 $event->send($this);

596

597 return $event->getParameter(‘result’);

598 }

◆ triggerOnBeforeAction()

triggerOnBeforeAction ( Action  $action )
finalprotected

Triggers the event {{static::EVENT_ON_BEFORE_ACTION}}

См. также
BitrixMainEngineController::getFullEventName. This method is invoked right before an action is executed. In case the action should not run, event handler have to return EvenResult with type EventResult::ERROR.
Аргументы
Возвращает
bool

См. определение в файле controller.php строка 531

532 {

533 $event = new Event(

534 ‘main’,

535 static::getFullEventName(static::EVENT_ON_BEFORE_ACTION),

536 array(

537 ‘action’ => $action,

538 ‘controller’ => $this,

539 )

540 );

541 $event->send($this);

542

543 $allow = true;

544 foreach ($event->getResults() as $eventResult)

545 {

546 if ($eventResult->getType() != EventResult::SUCCESS)

547 {

548 $handler = $eventResult->getHandler();

549 if ($handler && $handler instanceof Errorable)

550 {

551 $this->errorCollection->add($handler->getErrors());

552 }

553

554 $allow = false;

555 }

556 }

557

558 return $allow;

559 }

◆ writeToLogException()

writeToLogException ( Throwable  $e )
protected

См. определение в файле controller.php строка 429

430 {

432 $exceptionHandler->writeToLog($e);

433 }

◆ $configurator

◆ $errorCollection

◆ $request

◆ ERROR_REQUIRED_PARAMETER

const ERROR_REQUIRED_PARAMETER = ‘MAIN_CONTROLLER_22001’

◆ ERROR_UNKNOWN_ACTION

const ERROR_UNKNOWN_ACTION = ‘MAIN_CONTROLLER_22002’

◆ EVENT_ON_AFTER_ACTION

const EVENT_ON_AFTER_ACTION = ‘onAfterAction’

◆ EVENT_ON_BEFORE_ACTION

const EVENT_ON_BEFORE_ACTION = ‘onBeforeAction’

◆ EXCEPTION_UNKNOWN_ACTION

const EXCEPTION_UNKNOWN_ACTION = 22002

◆ METHOD_ACTION_SUFFIX

const METHOD_ACTION_SUFFIX = ‘Action’

inherited

◆ SCOPE_AJAX

const SCOPE_AJAX = ‘ajax’

◆ SCOPE_CLI

◆ SCOPE_REST

const SCOPE_REST = ‘rest’


Объявления и описания членов класса находятся в файле:

  • C:/Bitrix/modules/main/lib/engine/controller.php

Views: 1254
Last Modified: 18.01.2022

You can use the following two approaches for processing AJAX queries in component:

class.php

Query handler in component class (class.php file) allows to:

  • Incapsulate complete code in a single class
  • Repeatedly use component methods, data and parameters
  • Use component phrases and templates
  • Re-define standard behaviour in descendant-components

For the component class to process queries, it’s necessary to:

  • Implement the interface BitrixMainEngineContractControllerable
  • Define action method with suffix Action
  • Implement the method configureActions (usually returns an empty array === default configuration)
  • When you need to add, process errors, you can implement BitrixMainErrorable

Note: Executing component in Ajax mode sequentially executes CBitrixComponent::onIncludeComponentLang, CBitrixComponent::onPrepareComponentParams and launching an action with filters.

Attention! Executing component in ajax-mode doesn’t launch method CBitrixComponent::executeComponent().

Example

<?php
#components/bitrix/example/class.php

use BitrixMainError;
use BitrixMainErrorCollection;

if(!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED!==true)die();

class ExampleComponent extends CBitrixComponent implements BitrixMainEngineContractControllerable, BitrixMainErrorable
{
	/** @var ErrorCollection */
	protected $errorCollection;

	public function configureActions()
	{
		//when actions does not require configuration, write them as is. They will use default config 
		return [];
	}

	public function onPrepareComponentParams($arParams)
	{
		$this->errorCollection = new ErrorCollection();
		
		//parameter preparation
		//This code **will** be executed upon launching ajax-actions
	}
	
	public function executeComponent()
	{
		//Attention! This code **won't be** executed upon launching ajax-actions
	}
		
	//data from REQUEST will be inserted to parameter $person 
	public function greetAction($person = 'guest')
	{
		return "Hi {$person}!";
	}

	//example of error processing
	public function showMeYourErrorAction():? string
	{
		if (rand(3, 43) === 42)
		{
			$this->errorCollection[] = new Error('You are so beautiful or so handsome');
			//now response will contain errors and automatic 'error' response status. 
			
			return  null;					
		}

		return "Ok";
	}
	
	/**
	 * Getting array of errors.
	 * @return Error[]
	 */
	public function getErrors()
	{
		return $this->errorCollection->toArray();
	}

	/**
	 * Getting once error with the necessary code.
	 * @param string $code Code of error.
	 * @return Error
	 */
	public function getErrorByCode($code)
	{
		return $this->errorCollection->getErrorByCode($code);
	}
}

ajax.php

Query controller handler in the ajax.php file allows creating lightweight ajax-query handler, by directly dedicating logic from component.

To implement this:

  • Create ajax.php file in the component root
  • Define action method with Action suffix

The controller logic is fully the same as module controller description.

<?php
#components/bitrix/example/ajax.php

if(!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED!==true)die();

class ExampleAjaxController extends BitrixMainEngineController
{
	#data from REQUEST is automatically inserted into the parameter $person 
	public function sayByeAction($person = 'guest')
	{
		return "Goodbye {$person}";
	}

	public function listUsersAction(array $filter)
	{
		$users = [];
		//user list as per filter
		//array data composition for response
		
		return $users;
	}
}

С версии битрикса 17.5.10 добавился новый функционал по работе с аяксом. Теперь можно вызвать аяксом произвольный метод модуля или произвольный метод компонента без необходимости выполнения шаблона сайта. То есть по запросу выполнится только немного служебного кода и собственно сам метод.

На клиенте все довольно просто. Мы должны отправить обычный аякс запрос по адресу /bitrix/services/main/ajax.php

В js библиотеке битрикса есть методы которые самостоятельно формируют запрос, запись получается слегка короче, чем при написании запроса другими способами. Я покажу примеры запросов на битриксовой библиотеке и на jquery

Содержимое статьи

  • 1 Запрос к компоненту
    • 1.1 Клиентская сторона
    • 1.2 Серверная сторона:
  • 2 Запрос к модулю:
    • 2.1 Клиентская сторона
    • 2.2 Серверная сторона
  • 3 Выводы

На конференции битрикса в марте объяснялись нововведения в общих чертах, кому для понимания удобнее смотреть видео, вот:

На мой взгляд в виде текста такую информацию воспринимать удобнее. Поэтому я решил написать статью и раскрыть тему чуть более подробно

Запрос к компоненту

Для начала рассмотрим как происходит вызов методов компонента.

Клиентская сторона

Вот пример запроса на битриксовой библиотеке:

var request = BX.ajax.runComponentAction('custom:ajax', 'test', {
    mode:'class',
    data: {
        param1: 'asd'
    }
});

request.then(function(response){
    console.log(response);
});

Приведу код и jsdoc битриксовой функции. Из jsdoc можно узнать параметры, которые эта функция ожидает

/**
 *
 * @param {string} component
 * @param {string} action
 * @param {Object} config
 * @param {?string} [config.analyticsLabel]
 * @param {?string} [config.signedParameters]
 * @param {string} [config.method='POST']
 * @param {string} [config.mode='ajax'] Ajax or class.
 * @param {Object} [config.data]
 * @param {?array} [config.headers]
 * @param {?number} [config.timeout]
 * @param {Object} [config.navigation]
 */
BX.ajax.runComponentAction = function (component, action, config)
{
	config = prepareAjaxConfig(config);
	config.mode = config.mode || 'ajax';

	var getParameters = prepareAjaxGetParameters(config);
	getParameters.c = component;
	getParameters.action = action;

	var url = BX.util.add_url_param('/bitrix/services/main/ajax.php', getParameters);

	return buildAjaxPromiseToRestoreCsrf({
		method: config.method,
		dataType: 'json',
		url: url,
		data: config.data,
		timeout: config.timeout,
		preparePost: config.preparePost,
		headers: config.headers
	});
};

Вот примерно то же самое на jquery:

var query = {
    c: 'custom:ajax',
    action: 'test',
    mode: 'class'
};

var data = {
    param1: 'eee',
    SITE_ID: 's1',
    //sessid: BX.message('bitrix_sessid')
};

var request = $.ajax({
    url: '/bitrix/services/main/ajax.php?' + $.param(query, true),
    method: 'POST',
    data: data
});

request.done(function (response) {
    console.log(response);
});

Немного сложностей вносит использование csrf. Если кратко, то это защита от вызова метода на сторонних сайтах. Если вы хотите защитить запрос, то вам придется воспользоваться методом BX.message(‘bitrix_sessid’) в js (он закомментирован в объекте data). Либо каким то другим образом передать с бэкенда на фронт данные функции <?echo bitrix_sessid();?> и подставить эту строку в sessid.

Если же вы никакие сколько нибудь значимые данные в запросах не передаете, то проверку csrf можно будет отключить на бэкенде (для каждого аякс метода независимо, для каких-то можно и оставить) и отправлять запросы без sessid.

Серверная сторона:

На серверной стороне генерировать ответ будет обычный компонент. Так как в примере выше мы отправляли запросы к компоненту custom:ajax, то фактически будет выполняться метод testAction в классе из файла /local/components/custom/ajax/class.php, название класса в файле как обычно не важно. Вот примерно такое там может быть:

<?
if (!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED !== true) die();

use BitrixMainEngineContractControllerable;
use BitrixMainEngineActionFilter;

class CCustomAjax extends CBitrixComponent implements Controllerable
{
	/**
	 * @return array
	 */
	public function configureActions()
	{
		return [
			'test' => [
				'prefilters' => [
					new ActionFilterAuthentication(),
					new ActionFilterHttpMethod(
						array(ActionFilterHttpMethod::METHOD_GET, ActionFilterHttpMethod::METHOD_POST)
					),
					new ActionFilterCsrf(),
				],
				'postfilters' => []
			]
		];
	}

	function executeComponent()
	{
	}


	/**
	 * @param string $param2
	 * @param string $param1
	 * @return array
	 */
	public function testAction($param2 = 'qwe', $param1 = '')
	{
		return [
			'asd' => $param1,
			'count' => 200
		];
	}

}

Обращаю ваше внимание, на клиенте мы спрашиваем просто test, а по факту выполняется метод testAction.

Метод configureActions описывает пре- и постфильтры для запроса. В примере выше, чтобы запрос выполнился пользователь должен быть авторизован (префильтр ActionFilterAuthentication()), запрос должен идти методом GET или POST (префильтр ActionFilterHttpMethod) и если это POST запрос, то в запросе должна быть переменная sessid (префильтр ActionFilterCsrf). Это набор дефолтных префильтров. Если метод configureActions() ничего не будет возвращать, то именно эти префильтры по дефолту подставит битрикс. Если же метод возвращает массив, в одном из ключей которого есть описание метода test, то выполнятся именно пользовательские префильтры, а стандартные будут проигнорированы.

Выше я говорил, что можно убрать проверку csrf токена на бэкенде, это делается именно тут, в методе configureActions(). Чтобы csrf токен не проверялся нужно убрать ActionFilterHttpMethod и ActionFilterCsrf из возвращаемого массива для конкретного ajax метода, недостаточно убрать просто ActionFilterCsrf, если в ActionFilterHttpMethod разрешен POST, то префильтр ActionFilterCsrf автоматически включается.

На что еще обратить внимание. В методе testAction есть два аргумента — $param2 и $param1. Я намеренно поставил их в обратном порядке, чтобы показать как происходит маппинг данных из запроса. Если вы вернетесь в описание клиентской части, то увидите, что там передается param1, а param2 не передается. Битрикс проверяет имена переменных через php reflection и передает в аргументы метода ajax данные с тем же типом и названием. То есть другими словами не нужно задумываться какой у вас там аргумент первый, какой второй, как это все придет из браузера, в каком порядке, достаточно просто называть переменные одинаково на сервере и на клиенте.

В клиентском запросе мы указывали mode: ‘class’, если посмотреть jsdoc битриксовой функции, то оказывается, что она принимает еще и mode: ‘ajax’. На самом деле это практически то же самое, только подключается не class.php из папка компонента, а файл ajax.php. Ну и сам класс нужно наследовать немного иначе. Вот пример класса в файле ajax.php:

<?
if (!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED !== true) die();

use BitrixMainEngineController;

class CustomAjaxController extends Controller
{
	/**
	 * @return array
	 */
	public function configureActions()
	{
		return [
			'test' => [
				'prefilters' => []
			]
		];
	}

	/**
	 * @param string $param2
	 * @param string $param1
	 * @return array
	 */
	public static function testAction($param2 = 'qwe', $param1 = '')
	{
		return [
			'asd' => $param1,
			'count' => 300
		];
	}

}

То есть примерно то же самое, только убрали implements Controllerable, и отнаследовались от Controller.В остальном никаких отличий нет. Название класса так же как и с компонентом не важно.

Запрос к модулю:

Теперь попробуем отправлять запрос не к компоненту а к модулю. Я буду отправлять запросы к модулю local.lib.

Клиентская сторона

var request = BX.ajax.runAction('local:lib.api.test.example', {
    data: {
        param1: 'hhh'
    }
});

request.then(function(response){
    console.log(response);
});

Вот опять же код и jsdoc вызываемой функции

/**
 *
 * @param {string} action
 * @param {Object} config
 * @param {?string} [config.analyticsLabel]
 * @param {string} [config.method='POST']
 * @param {Object} [config.data]
 * @param {?Object} [config.headers]
 * @param {?Object} [config.timeout]
 * @param {Object} [config.navigation]
 * @param {number} [config.navigation.page]
 */
BX.ajax.runAction = function(action, config)
{
	config = prepareAjaxConfig(config);
	var getParameters = prepareAjaxGetParameters(config);
	getParameters.action = action;

	var url = BX.util.add_url_param('/bitrix/services/main/ajax.php', getParameters);

	return buildAjaxPromiseToRestoreCsrf({
		method: config.method,
		dataType: 'json',
		url: url,
		data: config.data,
		timeout: config.timeout,
		preparePost: config.preparePost,
		headers: config.headers
	});
};

Аналогичный запрос на jquery:

var query = {
    action: 'local:lib.api.test.example',
};

var data = {
    param1: 'eee',
    SITE_ID: 's1',
    //sessid: BX.message('bitrix_sessid')
};

var request = $.ajax({
    url: '/bitrix/services/main/ajax.php?' + $.param(query),
    method: 'POST',
    data: data
});

request.done(function (response) {
    console.log(response);
});

Принципы формирования sessid те же, что и для компонента.

Серверная сторона

На серверной стороне есть отличия. Для начала в папке с модулем нам понадобится файл .settings.php, именно в папке с модулем. Вот пример файла:

<?php
return [
	'controllers' => [
		'value' => [
			'namespaces' => [
				'\Local\Lib\Controller' => 'api'
			]
		],
		'readonly' => true
	]
];

В этом файле мы регистрируем целый неймспейс. Или другими словами регистрируем целую папку, вся папка будет содержать контроллеры для работы с аяксом.

Далее создаем папку lib/controller и файл test.php в этой папке:

<?php
namespace LocalLibController;

use BitrixMainEngineController;

class Test extends Controller
{
	/**
	 * @return array
	 */
	public function configureActions()
	{
		return [
			'example' => [
				'prefilters' => []
			]
		];
	}

	/**
	 * @param string $param2
	 * @param string $param1
	 * @return array
	 */
	public static function exampleAction($param2 = 'qwe', $param1 = '')
	{
		return [
			'asd' => $param1,
			'count' => 300
		];
	}
}

Содержимое тут примерно такое же как и в файле ajax.php компонента, так что останавливаться на этом не буду.

Ключевое для понимания работы аякс в модуле — как именно формируется action и как в итоге будет обрабатываться запрос. Разберем тестовый экшен local:lib.api.test.example

  • local:lib — модуль local.lib, где точку заменили на двоеточие
  • api — неймспейс который мы зарегистрировали в .settings.php модуля
  • test — название файла и класса в папке lib/controller
  • example — метод exampleAction в классе Test, без суффикса action

Каждый отдельный файл регистрировать в .settings.php модуля не нужно.

Выводы

В целом пока что впечатления от нового механизма положительные. Нужно еще оценить удобство на практике, но чувствуется, что над функционалом довольно долго думали, проектировали. Много нового функционала. Я не затронул еще одну интересную штуку про которую рассказывали на видео — механизм auto wiring (автоматическое связывание), простых примеров так сразу не смог придумать.

Пара идей куда это все эти новинки применить уже есть. Возможно после использования на практике я дополню статью. Пока что вся статья результат теоретического изучения в течение одного дня.

Если информация оказалась полезной, скажите спасибо, это мотивирует писать чаще 🙂

Пытаюсь использовать контроллеры битрикса для ajax запроса к своему модулю через BX.ajax.runAction.

В корне модуля лежит .settings.php со следующим содержимым:

return [
'controllers' => [
    'value' => [
        'namespaces' => [
            '\Evsyukov\Main\Controller' => 'api',
        ],
    ],
    'readonly' => true,
],

];

название модуля ‘testmodules’ далее /lib/controller/test.php
его код

namespace EvsyukovMainController;

class Book extends Controller {

public function configureActions()
{

    return [
        'greet' => [
            'prefilters' => [

            ]
        ]
    ];
}


public function addBookAction()
{
    $context = Application::getInstance()->getContext();
    $request = $context->getRequest();
    $arRequest = $request->getPostList()->toArray();

    $this->addbook($arRequest);

}

}

пытаюсь вызвать так

BX.ajax.runAction(‘evsyukov:main.api.book.addbook’, {}

задан 5 авг 2022 в 19:32

getforget's user avatar

В configureActions() у вас ключ greet, а метод назвали addBook, вот так должно быть. Также метод должен возвращать массив.

<?php

namespace EvsyukovMainController;

use BitrixMainApplication;
use BitrixMainEngineController;
use BitrixMainEngineActionFilter;

class Book extends Controller {
    
    public function configureActions()
    {
        return [
            'addBook' => [
                'prefilters' => [
                    new ActionFilterAuthentication(),
                    new ActionFilterHttpMethod(
                        [
                            ActionFilterHttpMethod::METHOD_GET,
                            ActionFilterHttpMethod::METHOD_POST,
                        ]
                    ),
                    //new ActionFilterCsrf(),
                ],
                'postfilters' => []
            ]
        ];
    }
    
    public function addBookAction()
    {
        $context = Application::getInstance()->getContext();
        $request = $context->getRequest();
        $arRequest = $request->getPostList()->toArray();
        
        return $arRequest;
    }
}

ответ дан 8 авг 2022 в 12:31

Firsov36's user avatar

Firsov36Firsov36

8941 золотой знак3 серебряных знака11 бронзовых знаков

2

Если название модуля не содержит «.», то битрикс считает, что модуль его и ищет среди своих модулей в namespace Bitrix. Чтобы для вашего модуля работал механизм контроллеров, имя модуля должно быть evsyukov.main

И в .settings.php надо добавить defaultNamespace:

return [
'controllers' => [
    'value' => [
        'defaultNamespace' => '\Evsyukov\Main\Controller',
        'namespaces' => [
            '\Evsyukov\Main\Controller' => 'api',
        ],
    ],
    'readonly' => true,
],

ответ дан 9 авг 2022 в 12:57

Виктор Карев's user avatar

Виктор КаревВиктор Карев

6754 серебряных знака9 бронзовых знаков

Содержание

Mysql query error: (1227)

Ошибка

Mysql query error: (1227) Access denied; you need (at least one of) the SYSTEM_VARIABLES_ADMIN or SESSION_VARIABLES_ADMIN privilege(s) for this operation (400)

Решение

GRANT SESSION_VARIABLES_ADMIN ON *.* TO 'bitrix_user'@'localhost';

Ошибка в типе содержимого

ERR_CONTENT_DECODING_FAILED

Добавить в htaccess

zlib.output_compression = on

или добавить в bitrix/php_interface/init.php (или dbconf.php)

define('BX_COMPRESSION_DISABLED',true);

Ошибка сокетов в site_checker

Причин много (корявый hosts файл, нет модуля sockets итд).

Но оказывается всё из-за того, что на /bitrix/admin/ установлен htpasswd (логин/пароль).

Браво Битрикс! Райская система. Всем рекомендую.

Не меняется memory_limit

Ещё одна дикая проблема Битрикса. Тому кто это придумал надо пожать руку, а потом оторвать её.

Не меняется memory_limit для PHP. Меняю в php.ini, меняю в htaccess, меняю где только можно, а результата нет. Смотрю phpinfo там правильный и master value и local value.

Не первый день в айти. Надо просто копнуть глубже

# grep -r --include=*.php "memory_limit" /var/www/foobar.com/

Какой-то сверх мозг в этой шайтан конторе решил, что будет весело прибить memory_limit гвоздями, потом полить супер-клеем и ещё сверху залить бетоном всё это дело. Ха-ха-ха, !@#$% смешно.

/var/www/foobar/bitrix/modules/main/admin/site_checker.php:                  @ini_set("memory_limit", "512M");
/var/www/foobar/bitrix/modules/main/classes/general/file.php:                        $memoryLimit = CUtil::Unformat(ini_get('memory_limit'));
/var/www/foobar/bitrix/modules/main/classes/general/site_checker.php:                        array('check_memory_limit' => GetMessage('SC_T_MEMORY')),
/var/www/foobar/bitrix/modules/main/classes/general/site_checker.php:        function check_memory_limit()
/var/www/foobar/bitrix/modules/main/classes/general/site_checker.php:                        $cur = ini_get('memory_limit');
/var/www/foobar/bitrix/modules/main/install/wizard/wizard.php:               $memoryLimit = WelcomeStep::unformat(ini_get('memory_limit'));
/var/www/foobar/bitrix/modules/main/install/wizard/wizard.php:                       $memoryLimit = WelcomeStep::unformat(get_cfg_var('memory_limit'));
/var/www/foobar/bitrix/modules/main/install/wizard/wizard.php:                       @ini_set("memory_limit", "64M");
/var/www/foobar/bitrix/modules/main/install/wizard/wizard.php:                       $memoryLimit = WelcomeStep::unformat(ini_get('memory_limit'));
/var/www/foobar/bitrix/modules/main/install/wizard/wizard.php:                                       '.($memoryLimit > 0 && $memoryLimit < $this->memoryMin*1048576 ? $this->ShowResult(ini_get('memory_limit'), "ERROR") : $this->ShowResult(ini_get('memory_limit'), "OK")).'
/var/www/foobar/bitrix/modules/main/install/wizard/wizard.php:                       $fileContent .= "n@ini_set("memory_limit", "1024M");n";
/var/www/foobar/bitrix/modules/main/install/wizard/wizard.php:                       $memoryLimit = WelcomeStep::unformat(ini_get('memory_limit'));
/var/www/foobar/bitrix/modules/main/install/wizard/wizard.php:                               $memoryLimit = WelcomeStep::unformat(get_cfg_var('memory_limit'));
/var/www/foobar/bitrix/modules/main/install/wizard/wizard.php:                               @ini_set("memory_limit", "512M");
/var/www/foobar/bitrix/modules/main/install/wizard/wizard.php:                               $memoryLimit = WelcomeStep::unformat(ini_get('memory_limit'));
/var/www/foobar/bitrix/modules/main/install/wizard/wizard.php:                                       $fileContent .= "n@ini_set("memory_limit", "512M");n";
/var/www/foobar/bitrix/modules/main/lang/en/admin/site_checker.php:PHP defines the memory limit in php.ini by setting the <b>memory_limit</b> parameter. However, this may be overridden on shared hostings. You should not trust this parameter.
/var/www/foobar/bitrix/modules/main/lang/en/admin/site_checker.php:The test attempts to increase the value of <b>memory_limit</b> using the code:
/var/www/foobar/bitrix/modules/main/lang/en/admin/site_checker.php:<code>ini_set(&quot;memory_limit&quot;, &quot;512M&quot;)
/var/www/foobar/bitrix/modules/main/lang/en/admin/site_checker.php:$MESS["SC_MEMORY_CHANGED"] = "The value of memory_limit was increased from #VAL0# to #VAL1# using ini_set while testing.";
/var/www/foobar/bitrix/modules/main/lang/ru/admin/site_checker.php:$MESS["SC_MEMORY_CHANGED"] = "Значение memory_limit было увеличено с #VAL0# до #VAL1# на момент тестирования через ini_set.";
/var/www/foobar/bitrix/modules/main/lang/ru/admin/site_checker.php:Основной параметр ограничения памяти в php.ini - это <b>memory_limit</b>. Но доверять значению параметра нельзя, т.к. на хостингах могут быть установлены дополнительные ограничения памяти.
/var/www/foobar/bitrix/modules/main/lang/ru/admin/site_checker.php:Обратите внимание, тест пытается увеличить значение <b>memory_limit</b> используя код:
/var/www/foobar/bitrix/modules/main/lang/ru/admin/site_checker.php:<code>ini_set(&quot;memory_limit&quot;, &quot;512M&quot;)
/var/www/foobar/bitrix/php_interface/dbconn.php:@ini_set("memory_limit", "128M");
/var/www/foobar/cli/parse_list.php:ini_set('memory_limit', '2048M');
/var/www/foobar/local/php_interface/dbconn.example.php:@ini_set("memory_limit", "128M");

Вот почему у меня парсер в админке не работал. Из-за 128M memory_limit. Даже не знаю какой мемчик бы подошёл для этой ситуации лучше — Друзь с его вы там ох!@#$% или Винни Пух ныряющий в болото. Битрикс💩, а WordPress, Joomla, Drupal по сравнению с этим платным💩 просто топчик.

DB query error. Please try later

В файле /bitrix/php_interface/dbconn.php установить

$DBDebug = true;

Обновляем страницу сайта, чтобы увидеть в чем проблема.

The script encountered an error

The script encountered an error and will be aborted. To view extended error messages, enable this feature in .settings.php.

Установить ‘debug’ ⇒ true в bitrix/.settings.php (в старых версиях bitrix/php_interface/dbconn.php).

This website uses cookies. By using the website, you agree with storing cookies on your computer. Also you acknowledge that you have read and understand our Privacy Policy. If you do not agree leave the website.More information about cookies

Понравилась статья? Поделить с друзьями:
  • Bitrix catalog 404 ошибка
  • Bitrix 410 error processing file
  • Bitmos oxy 6000 o2 error
  • Bitmap manager error file could not be found 3ds max
  • Bitlocker driver ошибка 24620 windows 10