Почему функция 'СтрНайтиПоРегулярномуВыражению' не определяется в тонком/веб-клиенте, даже при запуске в режиме толстого клиента 1С?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При разработке решений на платформе 1С:Предприятие 8.3 с использованием управляемых форм, разработчики часто сталкиваются с ошибкой "Функция не определена" для методов, которые, казалось бы, должны быть доступны. Одна из таких ситуаций возникает, когда мы пытаемся использовать функцию СтрНайтиПоРегулярномуВыражению в модуле формы обработки и получаем ошибку: "Функция 'СтрНайтиПоРегулярномуВыражению' не определена [Тонкий клиент, Web-клиент]". Это происходит даже в том случае, если в настройках запуска конфигурации у нас указан "Толстый клиент" (версия платформы 8.3.27 и выше), и по описанию эта функция доступна на сервере и в толстом клиенте. Давайте подробно разберем, почему возникает эта проблема и как ее корректно решить.

Основная причина этой ошибки кроется в клиент-серверной архитектуре управляемых форм. Даже если мы запускаем 1С в режиме толстого клиента, код в модулях форм управляемого приложения может выполняться как на клиенте, так и на сервере. Функция СтрНайтиПоРегулярномуВыражению доступна только на сервере, толстом клиенте (в контексте именно серверной части толстого клиента, а не клиентской части управляемых форм), внешнем соединении и мобильном приложении (сервере), но она недоступна в контексте тонкого или веб-клиента. Это означает, что, несмотря на настройку запуска "Толстый клиент", часть нашего кода обработки попыталась выполнить эту функцию в клиентском контексте, где она попросту отсутствует.

Для корректного использования функции СтрНайтиПоРегулярномуВыражению нам необходимо обеспечить ее вызов именно на сервере. Рассмотрим подробнее, как это достигается с помощью директив компиляции и правильной организации кода.

1. Понимание клиент-серверной архитектуры в управляемых формах

В управляемых формах 1С код разделяется на две основные части: клиентскую и серверную. Клиентский код выполняется на компьютере пользователя (в тонком клиенте, веб-клиенте или даже в клиентской части толстого клиента), а серверный код — на сервере 1С:Предприятия или на том же компьютере, если это файловый вариант базы, но в отдельном процессе, имитирующем работу сервера. Функции, работающие с файловой системой сервера, базой данных, или требующие больших вычислений, как правило, доступны только на сервере. К таким функциям относится и СтрНайтиПоРегулярномуВыражению.

2. Использование директив компиляции в модулях форм

Для того чтобы явно указать, в каком контексте должна выполняться та или иная процедура или функция в модуле формы управляемого приложения, мы используем специальные директивы компиляции. Эти директивы определяют, будет ли код выполняться на клиенте или на сервере. Давайте разберем основные из них:

  1. &НаСервере: Эта директива указывает, что процедура или функция должна быть выполнена на сервере. При этом, если процедура или функция находится в модуле формы, ей будет доступен контекст формы (т.е. реквизиты формы, элементы управления и их значения будут переданы на сервер). После выполнения серверного кода управление возвращается на клиент.
  2. Например, если нам нужно получить данные из реквизита формы, обработать их на сервере с помощью СтрНайтиПоРегулярномуВыражению, а затем обновить другой реквизит формы, мы можем использовать следующую структуру:

    
    &НаКлиенте
    Процедура КомандаОбработатьНаСервере(Команда)
        // Вызываем серверную процедуру
        ОбработатьТекстНаСервере();
    КонецПроцедуры
    
    &НаСервере
    Процедура ОбработатьТекстНаСервере()
        // Здесь мы имеем доступ к реквизитам формы, например, к РеквизитТекста
        Если Не ЗначениеЗаполнено(РеквизитТекста) Тогда
            Возврат;
        КонецЕсли;
    
        // Используем функцию СтрНайтиПоРегулярномуВыражению, которая доступна на сервере
        НайденныеСовпадения = СтрНайтиПоРегулярномуВыражению(РеквизитТекста, "(\d+)");
            
        // Обновляем реквизит формы, который будет отображен на клиенте
        РеквизитРезультата = "Найдено: " + НайденныеСовпадения.Количество();
    КонецПроцедуры
    
  3. &НаСервереБезКонтекста: Эта директива также указывает, что процедура или функция должна быть выполнена на сервере. Однако, в отличие от &НаСервере, ей не будет доступен контекст формы. Это означает, что реквизиты формы не будут автоматически переданы на сервер. Такой подход является более производительным, так как не происходит передачи контекста формы между клиентом и сервером. Мы передаем на сервер только те параметры, которые явно указаны в сигнатуре процедуры/функции.
  4. Мы используем эту директиву, когда серверной процедуре не нужен прямой доступ к реквизитам формы, а достаточно только переданных параметров. Например:

    
    &НаКлиенте
    Процедура КомандаОбработатьБезКонтекста(Команда)
        // Получаем значение с формы на клиенте
        ТекстДляОбработки = РеквизитТекста;
            
        // Вызываем серверную процедуру, передавая только необходимый параметр
        РезультатОбработки = ОбработатьТекстНаСервереБезКонтекста(ТекстДляОбработки);
            
        // Обновляем реквизит формы на клиенте
        РеквизитРезультата = РезультатОбработки;
    КонецПроцедуры
    
    &НаСервереБезКонтекста
    Функция ОбработатьТекстНаСервереБезКонтекста(ИсходныйТекст)
        Если Не ЗначениеЗаполнено(ИсходныйТекст) Тогда
            Возврат "";
        КонецЕсли;
    
        // Используем функцию СтрНайтиПоРегулярномуВыражению
        НайденныеСовпадения = СтрНайтиПоРегулярномуВыражению(ИсходныйТекст, "(\d+)");
            
        Возврат "Найдено: " + НайденныеСовпадения.Количество();
    КонецФункции
    

    Важно: Если для процедуры или функции в модуле формы не указана ни одна из этих директив, по умолчанию она будет выполняться на сервере с контекстом формы (т.е. эквивалентно &НаСервере).

  5. &НаКлиенте: Эта директива указывает, что процедура или функция должна быть выполнена на клиенте. Код в таких процедурах не может обращаться к серверным функциям напрямую.
  6. Например:

    
    &НаКлиенте
    Процедура ПриОткрытии()
        Сообщить("Форма открыта на клиенте.");
    КонецПроцедуры
    

3. Вызов серверных функций из клиентского кода

Мы видим, что из клиентской процедуры (отмеченной &НаКлиенте) можно вызвать серверную процедуру (отмеченную &НаСервере или &НаСервереБезКонтекста). При таком вызове происходит клиент-серверный вызов: управление передается на сервер, серверный код выполняется, и после его завершения управление возвращается обратно на клиент. Это позволяет нам выполнять необходимые серверные операции, включая использование функции СтрНайтиПоРегулярномуВыражению, а затем продолжать работу на клиенте.

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

4. Использование общих модулей для разделения логики

Для более структурированного и поддерживаемого кода мы часто выносим общую логику в общие модули. Если функция СтрНайтиПоРегулярномуВыражению используется в таком модуле, необходимо правильно настроить свойства этого общего модуля:

  1. Установите флаг "Сервер": Это означает, что код из этого модуля может выполняться на сервере.
  2. Если предполагается вызов функций из этого модуля с клиента (например, из клиентской процедуры формы), также установите флаг "Вызов сервера". Это позволит клиентскому коду инициировать выполнение серверных процедур из этого общего модуля через клиент-серверный вызов.

Мы рекомендуем разделять клиентскую и серверную логику на отдельные общие модули. Например, можно создать МодульКлиент с флагом "Клиент" и МодульСервер с флагами "Сервер" и "Вызов сервера". Такой подход помогает избежать проблем с надежным определением контекста выполнения и делает код более читаемым. Избегайте использования инструкций препроцессора (таких как #Если Клиент или #Если Сервер) для проверки контекста внутри одного общего модуля, особенно если этот модуль используется и на клиенте, и на сервере, так как это может привести к сложностям в отладке и неожиданному поведению. Для автоматической проверки корректности вызовов экспортных методов общих модулей можно использовать конфигурацию для анализа ошибок в программном коде.

Рассмотрим пример использования общего модуля:


// ОбщийМодульСервер.bsl (Свойства: Сервер = Истина, ВызовСервера = Истина)

Функция НайтиЧислаВТекстеНаСервере(Текст) Экспорт
    Если Не ЗначениеЗаполнено(Текст) Тогда
        Возврат 0;
    КонецЕсли;
    
    // СтрНайтиПоРегулярномуВыражению доступна здесь
    НайденныеЧисла = СтрНайтиПоРегулярномуВыражению(Текст, "\d+");
    Возврат НайденныеЧисла.Количество();
КонецФункции

// МодульФормыОбработки.bsl

&НаКлиенте
Процедура КомандаИспользоватьОбщийМодуль(Команда)
    ТекстИзПоля = Элементы.ПолеВводаТекста.Значение;
    Количество = ОбщийМодульСервер.НайтиЧислаВТекстеНаСервере(ТекстИзПоля);
    Сообщить("Найдено чисел: " + Количество);
КонецПроцедуры

5. Различия между директивами компиляции и инструкциями препроцессора

В ходе обсуждения часто всплывает вопрос о директивах типа #Если ТолстыйКлиент Тогда (как в сообщении 2). Важно понимать разницу между этими инструкциями препроцессора и директивами компиляции (&НаСервере, &НаКлиенте). Инструкции препроцессора (например, #Если Клиент, #Если Сервер, #Если ТолстыйКлиент, #Если ВебКлиент) определяют, какая часть кода будет компилироваться для данного типа клиента. Они фильтруют код на этапе компиляции.

Например:


#Если ТолстыйКлиент Тогда
    // Этот код будет скомпилирован и доступен только при работе в толстом клиенте
    Сообщить("Это сообщение только для толстого клиента");
#КонецЕсли

Однако, даже если код скомпилирован для толстого клиента, это не означает, что он автоматически будет выполняться в серверном контексте в управляемой форме. Если мы находимся в модуле формы управляемого приложения, то &НаСервере или &НаКлиенте определяют контекст выполнения процедуры/функции. Для корректной работы с серверной функцией в управляемой форме, приоритетнее использовать именно директиву &НаСервере, чтобы явно указать, что вызов должен произойти на сервере, независимо от типа клиента (тонкий, веб или толстый), в котором запущено управляемое приложение.

6. Дополнительные важные моменты

  1. Доступность функции: Функции СтрНайтиПоРегулярномуВыражению и СтрНайтиВсеПоРегулярномуВыражению появились в платформе 1С:Предприятие версии 8.3.23 и выше. Мы должны убедиться, что наша версия платформы (в данном случае 8.3.27) поддерживает эту функциональность на сервере.
  2. "Толстый клиент" и управляемые формы: Способность "толстого клиента" выполнять управляемые формы не отменяет разделение кода на клиентский и серверный контексты в управляемом приложении. Основное отличие толстого клиента при работе с управляемыми формами от тонкого клиента заключается в способе подключения к информационной базе (тонкий клиент может работать через HTTP/HTTPS, толстый – напрямую с файловой базой или по TCP/IP с кластером серверов) и требованиях к ресурсам. Логика разделения кода между клиентом и сервером остается неизменной.
  3. Оптимизация производительности: Для повышения производительности в управляемых формах мы стремимся минимизировать количество клиент-серверных вызовов и объем передаваемых данных. Основная часть логики, особенно ресурсоемкой, должна быть реализована в серверных процедурах. Мы стараемся передавать на сервер только минимально необходимый набор данных.

Таким образом, для успешного использования функции СтрНайтиПоРегулярномуВыражению в управляемых формах необходимо всегда явно указывать, что ее вызов должен происходить на сервере, используя директивы компиляции &НаСервере или &НаСервереБезКонтекста, либо вызывая соответствующую серверную функцию из общего модуля.

← На главную