Пример Web-интерфейса для работы с NAV 2009. Часть 1.

В предыдущем выпуске был приведен ряд статей, рассказывающих об основах функционирования Web Services в Dynamics NAV. Помимо основ, были приведены несколько небольших примеров того, как можно реализовать Web Service под нужды конкретной задачи. В качестве клиентов выступали Windows Service и Win Forms, которые обращались к NAV…

Но чего в предыдущем выпуске (да и во всех остальных тоже) не было, так это реализации хотя бы элементарного Web Interface для NAV. Собственно, решением данной задачи мы и займемся.

Пару месяцев назад довелось мне участвовать в оживленной дискуссии на http://mibuso.com/forum/. Суть заключалась в том, что автор темы не мог справиться с некоторыми трудностями, которые возникли у него во время реализации веб-клиента для NAV… Когда же автор того топика разрешил удаленно приконнектиться с помощью TeamViewer к его машине и посмотреть  на того самого клиента, я был удивлен: передо мной был web-интерфейс классического клиента. Один в один. Дальше углубляться не стали, он пооткрывал пару форм, но этого хватило, чтобы произвести немалое впечатление… Реализация, хотя бы отчасти, более/менее функционального web-клиента, прежде всего, поможет им обойти ограничение по количеству конкурентных пользователей. Хотя тут тоже не все так просто… Причем все – абсолютно легально…

Итак, какие же инструменты и технологии пригодятся нам сегодня:

  • IIS (Internet Information Services);
  • PHP (PHP: Hypertext Preprocessor);
  • WCF (Windows Communication Foundation);
  • ASP.NET, AJAX.

Перечислено много чего, но взято будет совсем понемногу от каждой составляющей…

Итак, давайте представим, что мы хотим реализовать динамическую веб-страницу, которая имеет пару списочных полей, пару текстовых полей и пару кнопок для вызова функций. Конечный ее вид будет примерно следующим:

 picture-1.png

В левом ListBox будет отображаться список клиентов в формате [Номер];[Название]. В правой части будет отображаться список комментариев по клиенту, который активируется в левом окне. В классическом же клиенте комментарии по клиенту можно увидеть, нажав последовательно кнопки Клиент -> Комментарии:

picture-2.png

Для вывода всех клиентов на web-страницу, необходимо нажать кнопку «Get Customer». Если ввести название любого клиента в левый нижний input, в SQL-запросе будет наложен фильтр по названию клиента.

Если выделить строку с клиентом, то в правом ListBox должны отразиться все комментарии по данному клиенту. После ввода нового комментария в правом нижнем input-поле, он тут же отразится в списке всех комментариев, и, конечно, будет внесен в базу. Для вывода данных (клиентов и комментариев по ним) будем работать с базой посредством PHP-скрипта, вызываемого JavaScript’ом с web-странички. Для добавления комментариев по клиенту будем использовать web-службу, реализованную в контракте WCF (Windows Communication Foundation) службы.

Для реализации этой нехитрой функциональности нам необходимо правильно установиться IIS (можно воспользоваться и Apache, кому как больше по душе). Инструкцию по установке IIS можно найти, к примеру, здесь: http://msdn.microsoft.com/en-us/library/ms751518.aspx.

IIS понадобится нам для того чтобы:

  • в нем располагалась наша динамическая страница;
  • на сервере IIS хостилась WCF-служба.

Подробнее с технологией WCF можно ознакомиться на бесчисленном количестве ресурсов. Но, как мне кажется, одним из наиболее продвинутых является этот (собственно, ресурс вендора). WCF рекомендую начать изучать всем, кто так или иначе хочет нормально писать web-приложения для Навижина…

Помимо IIS, необходимо, чтобы наш браузер поддерживал JavaScript. Ну и не забудем о PHP. Собственно, можем запустить онлайновый инсталлятор, который и PHP нам установит, и IIS сконфигурирует как надо: http://php.iis.net/

После установки необходимых компонентов, приступим к реализации проекта в Visual Studio. Нам необходимо выбрать проект с шаблоном «WCF Service». Разместим его на IIS-сервере, выбрав в Visual Studio последовательность действий File->New->Web Site:

picture-3.png

По-умолчанию, создаются конфигурационный файл Web.config (содержит информацию по конфигурации WCF-службы), файл Service.svc с информацией, необходимой для хостинга службы, а также файлы IService.cs и Service.cs.. Файл IService.cs можно удалить, поскольку он содержит демо-пример интерфейса IService, который реализуется в классе Service одноименного файла…

Непосредственно к реализации службы мы пока не приступаем, поскольку нет еще web-интерфейса, который будет выступать в роли клиента по отношению к данной службе. К реализации веб-страницы мы сейчас и приступим.

Добавим новый элемент в наш проект: «AJAX Web Form». Данный элемент позволит задействовать возможность работы как с клиентским JavaScript, так и с технологией AJAX (Asynchronous Javascript and XML). AJAX нужен будет нам для того, чтобы не перегружать нашу web-страницу полностью при очередном обновлении данных, либо при очередной их выборке из базы. Вообще ASP.NET AJAX – один из наиболее популярных каркасов в построении web-приложений. Он не просто содержит развитую библиотеку клиентских классов, но и множество элементов управления.

Итак, вот как выглядит наш проект на текущем этапе:

picture-4.png

Наберем в строке браузера следующую строку: http://localhost/WCFService/AJAXWebForm4NAV.aspx

Откроется пустая страница. А пустая она потому, что мы еще не определили на ней ни одного элемента. Для того, чтобы сделать это, внесем следующий код в тело тега <form>:

<table>
  <tr style=”height:500px;vertical-align:top;“>
    <td style=”width:400px;height:400px;“>Select a customer:<br />
      <asp:ListBox onclick=”SetMode(’Comments’);” ID=”CustomersListBox” runat=”server” Width=”100%” Height=”100%”>
     
</asp:ListBox>
    </td>
    <td style=”width:400px;height:400px;“>Comments:<br />
      <asp:ListBox ID=”CommentsByCustomerListBox” runat=”server” Width=”100%” Height=”100%”>
     
</asp:ListBox>
    </td>
  </tr>
  <tr style=”height:100px;vertical-align:top;“>
    <td style=”width:400px;“>Enter the customer’s name:<br />
      <asp:TextBox id=”CustomerNameTextBox” runat=”server” name=”CustomerNameTextBox” width=”95%” />
      <input id=”GetCustomerInput” runat=”server” type=”button” value=”Get Customer” onclick=”SetMode(’Customers’);” />
    </td>
    <td style=”width:400px;“>Enter the comment by customer:<br />
      <asp:TextBox id=”CustomerCommentTextBox” runat=”server” name=”CustomerCommentTextBox” width=”95%” />
      <input id=”SetCustomerCommentInput” runat=”server” type=”button” value=”Set Comment” onclick=”SetCustomerComment();” />
    </td>
  </tr>
</table>Во-первых, мы определили основные элементы страницы. А во-вторых, подписались на события onclick в 3-х элементах их 4-х:

  • при активации любой строки с клиентом в левом ListBoxe, вызывается метод SetMode с текстовым параметром ‘Comments’. Этот метод обновляет правый ListBox: заносит в него информацию по комментариям, связанным с выделенным в левом ListBox’е клиентом;
  • при нажатии кнопки «Get Customer» срабатывает метод SetMode с текстовым параметром ‘Customers’. Этот метод обновляет левый ListBox: заносит в него информацию либо по абсолютно всем клиентам (когда поле «CustomerNameTextBox» пустое), либо по клиентам, название которых удовлетворяет введенному в поле «CustomerNameTextBox» (срабатывает SQL-запрос с инструкцией LIKE);
  • при нажатии кнопки «Set Comment» срабатывает процедура SetCustomerComment, связывающая с выделенным клиентом введенный пользователем комментарий.

Реализация функций SetMode, и GetCustomerDB, которую она вызывает, приведена ниже:

 

m1_picture-5.png

Сперва происходит создание XMLHttpRequest объекта, который и будет использоваться нами для «общения» клиента (страницы) с PHP-скриптом. При этом перезагрузки страницы происходить не будет. Далее мы подписываемся на события, которые происходят при каждой смене статуса состояния объекта XMLHttpRequest. А затем, в зависимости от режима (хотим вывести клиентов/комментарии), отправляем запрос на сервер.

Здесь я приведу пример того PHP-скрипта, который будет выдавать клиентов/комментарии по ним, в зависимости от передаваемых в скрипт параметров:

<?php header(”Content-type: text/html; charset=windows-1251″);
header(”Cache-Control: no-store, no-cache, must-revalidate”);
header(”Cache-Control: post-check=0, pre-check=0″, false);// DB & server parameters
$server = “Romul”;
$db = “Demo_Database_NAV_6_0″;
$user = “sa”;
$password = “sa”; // connect to the NAV DB
$dbhandle = mssql_connect($server,$user,$password) or die(”Can’t connect to the $server server”);
$selecteddb = mssql_select_db($db,$dbhandle) or die(”Can’t connect to the $db database”); if (isset($_GET[”isGetCust”]))
{
   // select data from the Customer table… 
 
$query = “select * from dbo.[CRONUS International Ltd_”.’$’.”Customer]”;
    if(!empty($_GET[”customerName”]))
   {
               $where = “Name LIKE ‘”.mssql_real_escape_string($_GET[”customerName”]).”%’”;
               $query .= ” WHERE $where”;
   }    // executing the query
   $result = mssql_query($query);
   if (mssql_num_rows($result) < 1)
               echo “No data found.”;
   else
   {
               $i = 0;
               $customers = null;
               while($row = mssql_fetch_array($result))
               {
                          $customers[$i] = $row[”No_”].”;”.$row[”Name”];
                          $i = $i + 1;
               }
               echo json_encode($customers); // return to js-script the array of Customers using one of the function of Javascript Object Notation (JSON)
   }
      unset ($_GET[”isGetCust”]);
}
elseif (isset($_GET[”isGetCommentsByCust”]))
{
   $query = “select * from dbo.[CRONUS International Ltd_$”.”Comment Line]”;
   if(!empty($_GET[”customerNo”]))
   {
               $where = “[Table Name] = 1 and No_ = ‘”.mssql_real_escape_string($_GET[”customerNo”]).”‘”;
               $query .= ” WHERE $where”;
   }
      $result = mssql_query($query);
   if (mssql_num_rows($result) > 0)
   {
               $i = 0; 
             
$commentsByCustomer = null;
               while ($row = mssql_fetch_array($result))
               {
                          $commentsByCustomer[$i] = $row[”Comment”];
                          $i = $i + 1;
               }
               echo json_encode($commentsByCustomer);
   }
      unset ($_GET[”customerNo”]);
}// close connection…
mssql_close($dbhandle); // analogue of mysql_real_escape_string
function mssql_real_escape_string($string_to_escape)
{
   $replaced_string = str_replace(”‘”,”””,$string_to_escape);
   return $replaced_string;
} ?> 

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

  • если в момент обращения к серверу была инициализирована переменная isGetCust, выбираем данные по клиенту/клиентам (если фильтр наложен не был). Если количество возвращаемых из базы данных строк (информации по клиентам) > 0, тогда обрабатываем полученный кортеж данных, используя для этого функцию mssql_fetch_array. После чего «упаковываем» полученный массив данных $Customer в массив текстового формата обмена данными JSON (JavaScript Object Notation). Такой массив очень просто обработать с использованием JavaScript, когда настанет время парсить полученный от сервера ответ;
  • если в момент обращения к серверу была инициализирована переменная isGetCommentsByCust, то выбираем комментарии, связанные с этим клиентом и так же передаем браузеру результат в виде JSON-массива.
  • закрываем соединение с сервером

Функция mssql_real_escape_string, приведенная в самом конце скрипта, служит для экранирования и является аналогом встроенной в php функции mysql_real_escape_string (для работы с MySQL).Когда приходит ответ от сервера, необходимо обработать массив полученной информации, либо ошибку. Для обработки данных на стороне клиента используется функция HandleResponseFromDB:

m1_picture-6.png

Когда данные загружены с сервера (readyState == 4) и получен безошибочный ответ от сервера (200), происходит очистка обоих ListBox’ов:

m1_picture-7.png
После очистки ListBox’ов происходит распарсивание полученных ответов от сервера и заполнение нужного ListBoxa.
Ниже представлен результат на динамической странице:

m1_picture-8.png

 

А это – стандартный интерфейс классического клиента:
m1_picture-9.png

Метки: ,



Комментариев: 2

  1. Дмитрий пишет:

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

  2. Orwell пишет:

    Добрый день, Дмитрий.
    Извиняюсь за поздний ответ: давно не заходил на сайт.
    Если что-то не получается, то в первую очередь надо с разбираться с правами/аккаунтами/именем базы и прочими вещами, которые так или иначе могут влиять на работу с сервисами и с БД Навижина…
    К тому же, если Вы совсем новичек в web-технологиях, рекомендую все же сперва разобраться с основами IIS, PHP, ASP.NET, AJAX. А уже после этого приступать к реализации этого немудренного примера.
    Я отправил на Вашу почту архив со студийным проектом. Если что-то будет непонятно - задавайте вопросы.

Оставьте свой отзыв!