Общие сведения о веб-службах ASP.NET AJAX

Скотт Кейт

Веб-службы являются неотъемлемой частью платформы .NET Framework, которая предоставляет кроссплатформенное решение для обмена данными между распределенными системами. Хотя веб-службы обычно используются для разных операционных систем, объектных моделей и языков программирования для отправки и получения данных, их также можно использовать для динамического внедрения данных в ASP.NET страницы AJAX или отправки данных со страницы во серверную систему. Все это можно сделать, не прибегая к операциям обратной передачи.

Вызов веб-служб с помощью ASP.NET AJAX

Дэн Валин (Dan Wahlin)

Веб-службы являются неотъемлемой частью платформы .NET Framework, которая предоставляет кроссплатформенное решение для обмена данными между распределенными системами. Хотя веб-службы обычно используются для разных операционных систем, объектных моделей и языков программирования для отправки и получения данных, их также можно использовать для динамического внедрения данных в ASP.NET страницы AJAX или отправки данных со страницы во серверную систему. Все это можно сделать, не прибегая к операциям обратной передачи.

Хотя элемент управления ASP.NET AJAX UpdatePanel предоставляет простой способ включения любой страницы ASP.NET, иногда требуется динамический доступ к данным на сервере без использования UpdatePanel. В этой статье вы узнаете, как это сделать путем создания и использования веб-служб на ASP.NET страницах AJAX.

В этой статье основное внимание уделяется функциям, доступным в основных ASP.NET расширениям AJAX, а также элементу управления с поддержкой веб-службы в ASP.NET AJAX Toolkit, который называется AutoCompleteExtender. Рассматриваются такие темы, как определение веб-служб с поддержкой AJAX, создание клиентских прокси-серверов и вызов веб-служб с помощью JavaScript. Вы также увидите, как можно выполнять вызовы веб-службы непосредственно в методы ASP.NET страницы.

Конфигурация веб-служб

При создании проекта веб-сайта с помощью Visual Studio 2008 файл web.config содержит ряд новых дополнений, которые могут быть незнакомы пользователям предыдущих версий Visual Studio. Некоторые из этих изменений сопоставляют префикс asp с ASP.NET элементами управления AJAX, чтобы их можно было использовать на страницах, в то время как другие определяют обязательные httpHandlers и HttpModules. В листинге 1 показаны изменения, внесенные <httpHandlers> в элемент в web.config, влияющие на вызовы веб-службы. HttpHandler по умолчанию, используемый для обработки вызовов ASMX, удаляется и заменяется классом ScriptHandlerFactory, расположенным в сборке System.Web.Extensions.dll. System.Web.Extensions.dll содержит все основные функции, используемые ASP.NET AJAX.

Листинг 1. конфигурация обработчика веб-службы AJAX ASP.NET

<httpHandlers>
     <remove verb="*" path="*.asmx"/>
     <add verb="*" path="*.asmx" validate="false"
          type="System.Web.Script.Services.ScriptHandlerFactory,
          System.Web.Extensions, Version=1.0.61025.0, Culture=neutral,
          PublicKeyToken=31bf3856ad364e35"/>
</httpHandlers>

Эта замена HttpHandler выполняется для того, чтобы разрешить вызовы нотации объектов JavaScript (JSON) из ASP.NET страниц AJAX в веб-службы .NET с помощью прокси-сервера веб-службы JavaScript. ASP.NET AJAX отправляет сообщения JSON в веб-службы в отличие от стандартных вызовов ПРОТОКОЛА SOAP, обычно связанных с веб-службами. Это приводит к меньшему размеру запросов и ответных сообщений в целом. Это также обеспечивает более эффективную обработку данных на стороне клиента, так как библиотека JavaScript ASP.NET AJAX оптимизирована для работы с объектами JSON. В листингах 2 и 3 показаны примеры сообщений запросов и ответов веб-службы, сериализованных в формате JSON. Сообщение запроса, показанное в листинге 2, передает параметр country со значением "Belgium", а ответное сообщение в листинге 3 передает массив объектов Customer и связанные с ними свойства.

Листинг 2. Сообщение запроса веб-службы сериализовано в JSON

{"country":"Belgium"}

> [! Примечание] имя операции определяется как часть URL-адреса веб-службы; Кроме того, сообщения запросов не всегда отправляются через JSON. Веб-службы могут использовать атрибут ScriptMethod с параметром UseHttpGet, для которого задано значение true, что приводит к передаче параметров через параметры строки запроса.

Листинг 3. Ответное сообщение веб-службы сериализовано в JSON

[{"__type":"Model.Customer","Country":"Belgium","CompanyName":"Maison
     Dewey","CustomerID":"MAISD","ContactName":"Catherine
     Dewey"},{"__type":"Model.Customer","Country":"Belgium","CompanyName":"Suprêmes
     délices","CustomerID":"SUPRD","ContactName":"Pascale
     Cartrain"}]

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

Создание веб-служб AJAX-Enabled

Платформа ASP.NET AJAX предоставляет несколько различных способов вызова веб-служб. Вы можете использовать элемент управления AutoCompleteExtender (доступный в наборе средств AJAX ASP.NET) или JavaScript. Однако перед вызовом службы необходимо включить AJAX, чтобы ее можно было вызывать с помощью кода клиентского скрипта.

Независимо от того, новичок ли вы в ASP.NET веб-службах, вам будет просто создать службы и включить AJAX. Платформа .NET framework поддерживает создание веб-служб ASP.NET с момента ее первоначального выпуска в 2002 году, а расширения ASP.NET AJAX предоставляют дополнительные функции AJAX, которые создаются на основе набора функций по умолчанию платформы .NET Framework. Visual Studio .NET 2008 Beta 2 имеет встроенную поддержку создания asMX-файлов веб-службы и автоматически наследует связанный код помимо классов из класса System.Web.Services.WebService. При добавлении методов в класс необходимо применить атрибут WebMethod, чтобы их вызывали потребители веб-службы.

В листинге 4 показан пример применения атрибута WebMethod к методу GetCustomersByCountry().

Листинг 4. Использование атрибута WebMethod в веб-службе

[WebMethod]
public Customer[] GetCustomersByCountry(string country)
{
     return Biz.BAL.GetCustomersByCountry(country);
}

Метод GetCustomersByCountry() принимает параметр country и возвращает массив объектов Customer. Значение страны, передаваемое в метод, перенаправлено в класс бизнес-уровня, который, в свою очередь, вызывает класс уровня данных для получения данных из базы данных, заполнения свойств объекта Customer данными и возврата массива.

Использование атрибута ScriptService

Хотя добавление атрибута WebMethod позволяет вызывать метод GetCustomersByCountry() клиентами, которые отправляют стандартные сообщения SOAP в веб-службу, он не позволяет выполнять вызовы JSON из ASP.NET приложений AJAX по умолчанию. Чтобы разрешить вызовы JSON, необходимо применить атрибут расширения ASP.NET AJAX ScriptService к классу веб-службы. Это позволяет веб-службе отправлять ответные сообщения в формате JSON и позволяет клиентскому скрипту вызывать службу, отправляя сообщения JSON.

В листинге 5 показан пример применения атрибута ScriptService к классу веб-службы с именем CustomersService.

Листинг 5. Использование атрибута ScriptService для включения AJAX веб-службы

[System.Web.Script.Services.ScriptService]
[WebService(Namespace = "http://xmlforasp.net")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class CustomersService : System.Web.Services.WebService
{
     [WebMethod]
     public Customer[] GetCustomersByCountry(string country)
     {
          return Biz.BAL.GetCustomersByCountry(country);
     }
}

Атрибут ScriptService выступает в качестве маркера, который указывает, что его можно вызвать из кода скрипта AJAX. Он фактически не обрабатывает какие-либо задачи сериализации или десериализации JSON, которые происходят в фоновом режиме. ScriptHandlerFactory (настроенный в web.config) и другие связанные классы выполняют основную часть обработки JSON.

Использование атрибута ScriptMethod

Атрибут ScriptService является единственным ASP.NET атрибутом AJAX, который должен быть определен в веб-службе .NET, чтобы его можно было использовать для ASP.NET страниц AJAX. Однако другой атрибут с именем ScriptMethod также можно применять непосредственно к веб-методам в службе. ScriptMethod определяет три свойства, включая UseHttpGetи ResponseFormatXmlSerializeString. Изменение значений этих свойств может быть полезно в тех случаях, когда тип запроса, принимаемого веб-методом, необходимо изменить на GET, когда веб-метод должен возвращать необработанные XML-данные в форме XmlDocument объекта или XmlElement или когда данные, возвращаемые службой, всегда должны быть сериализованы как XML, а не JSON.

Свойство UseHttpGet можно использовать, если веб-метод должен принимать запросы GET, а не запросы POST. Запросы отправляются по URL-адресу с входными параметрами веб-метода, преобразованными в параметры QueryString. Свойство UseHttpGet по умолчанию имеет значение false и должно иметь значение true только в том случае, если операции известны как безопасные и если конфиденциальные данные не передаются в веб-службу. В листинге 6 показан пример использования атрибута ScriptMethod со свойством UseHttpGet.

Листинг 6. Использование атрибута ScriptMethod со свойством UseHttpGet.

[WebMethod]
[ScriptMethod(UseHttpGet = true)]
public string HttpGetEcho(string input)
{
     return input;
}

Ниже приведен пример заголовков, отправляемых при вызове веб-метода HttpGetEcho, показанного в листинге 6:

GET /CustomerViewer/DemoService.asmx/HttpGetEcho?input=%22Input Value%22 HTTP/1.1

Помимо разрешения веб-методов принимать HTTP-запросы GET, атрибут ScriptMethod также можно использовать, когда необходимо возвращать XML-ответы из службы, а не JSON. Например, веб-служба может получить RSS-канал с удаленного сайта и вернуть его в виде объекта XmlDocument или XmlElement. Затем обработка XML-данных может выполняться на клиенте.

В листинге 7 показан пример использования свойства ResponseFormat, чтобы указать, что XML-данные должны возвращаться из веб-метода.

Листинг 7. Использование атрибута ScriptMethod со свойством ResponseFormat.

[WebMethod]
[ScriptMethod(ResponseFormat = ResponseFormat.Xml)]
public XmlElement GetRssFeed(string url)
{
     XmlDocument doc = new XmlDocument();
     doc.Load(url);
     return doc.DocumentElement;
}

Свойство ResponseFormat также можно использовать вместе со свойством XmlSerializeString. Свойство XmlSerializeString имеет значение по умолчанию false, что означает, что все возвращаемые типы, кроме строк, возвращаемых из веб-метода, сериализуются как XML, если свойству ResponseFormat присвоено значение ResponseFormat.Xml. Если XmlSerializeString задано значение true, все типы, возвращаемые веб-методом, сериализуются как XML, включая строковые типы. Если свойство ResponseFormat имеет значение ResponseFormat.Json XmlSerializeString, свойство игнорируется.

В листинге 8 показан пример использования свойства XmlSerializeString для принудительной сериализации строк в формате XML.

Листинг 8. Использование атрибута ScriptMethod со свойством XmlSerializeString

[WebMethod]
[ScriptMethod(ResponseFormat = ResponseFormat.Xml,XmlSerializeString=true)]
public string GetXmlString(string input)
{
     return input;
}

Значение, возвращаемое при вызове веб-метода GetXmlString, показанное в листинге 8, отображается далее:

<?xml version="1.0"?>
     <string>Test</string>

Хотя формат JSON по умолчанию минимизирует общий размер сообщений запросов и ответов и более легко используется клиентами ASP.NET AJAX в межбраузерном режиме, свойства ResponseFormat и XmlSerializeString можно использовать, когда клиентские приложения, такие как Internet Обозреватель 5 или выше, ожидают, что XML-данные будут возвращены из веб-метода.

Работа со сложными типами

В листинге 5 показан пример возврата сложного типа с именем Customer из веб-службы. Класс Customer определяет несколько различных простых типов внутри как свойства, такие как FirstName и LastName. Сложные типы, используемые в качестве входного параметра или возвращаемого типа в веб-методе с поддержкой AJAX, автоматически сериализуются в JSON перед отправкой на стороне клиента. Однако вложенные сложные типы (определенные внутри другого типа) по умолчанию недоступны клиенту как автономные объекты.

В случаях, когда на клиентской странице также необходимо использовать вложенный сложный тип, используемый веб-службой, в веб-службу можно добавить атрибут GenerateScriptType ASP.NET AJAX. Например, класс CustomerDetails, показанный в листинге 9, содержит свойства Address и Gender, представляющие вложенные сложные типы.

Листинг 9. Класс CustomerDetails, показанный здесь, содержит два вложенных сложных типа.

public class CustomerDetails : Customer
{
     public CustomerDetails()
     {
     }
     Address _Address;
     Gender _Gender = Gender.Unknown;
     public Address Address
     {
          get { return _Address; }
          set { _Address = value; }
     }
     public Gender Gender
     {
          get { return _Gender; }
          set { _Gender = value; }
     }
}

Объекты Address и Gender, определенные в классе CustomerDetails, показанном в листинге 9, не будут автоматически доступны для использования на стороне клиента через JavaScript, так как они являются вложенными типами (Address — это класс, а Gender — перечисление). В ситуациях, когда вложенный тип, используемый в веб-службе, должен быть доступен на стороне клиента, можно использовать атрибут GenerateScriptType, упомянутый выше (см. листинг 10). Этот атрибут можно добавить несколько раз в случаях, когда служба возвращает различные вложенные сложные типы. Его можно применить непосредственно к классу веб-службы или выше определенных веб-методов.

Листинг 10. Использование атрибута GenerateScriptService для определения вложенных типов, которые должны быть доступны клиенту.

[System.Web.Script.Services.ScriptService]
[System.Web.Script.Services.GenerateScriptType(typeof(Address))]
[System.Web.Script.Services.GenerateScriptType(typeof(Gender))]
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class NestedComplexTypeService : System.Web.Services.WebService
{
     //Web Methods
}

Применив GenerateScriptType атрибут к веб-службе, типы Address и Gender будут автоматически доступны для использования клиентскими ASP.NET коде JAVAScript AJAX. Пример JavaScript, который автоматически создается и отправляется клиенту путем добавления атрибута GenerateScriptType в веб-службу, показан в листинге 11. Вы узнаете, как использовать вложенные сложные типы далее в этой статье.

Листинг 11. Вложенные сложные типы, доступные ASP.NET странице AJAX.

if (typeof(Model.Address) === 'undefined')
{
     Model.Address=gtc("Model.Address");
     Model.Address.registerClass('Model.Address');
}
Model.Gender = function() { throw Error.invalidOperation(); }
Model.Gender.prototype = {Unknown: 0,Male: 1,Female: 2}
Model.Gender.registerEnum('Model.Gender', true);

Теперь, когда вы узнали, как создавать веб-службы и делать их доступными для ASP.NET страниц AJAX, давайте рассмотрим, как создавать и использовать прокси-серверы JavaScript, чтобы данные можно было извлекать или отправлять в веб-службы.

Создание прокси-серверов JavaScript

Вызов стандартной веб-службы (.NET или другой платформы) обычно включает в себя создание прокси-объекта, который защищает вас от сложностей отправки запросов и ответов SOAP. С помощью ASP.NET вызовов веб-службы AJAX можно создавать прокси-серверы JavaScript и использовать их для простого вызова служб, не беспокоясь о сериализации и десериализации сообщений JSON. Прокси-серверы JavaScript можно создавать автоматически с помощью элемента управления ASP.NET AJAX ScriptManager.

Создание прокси-сервера JavaScript, который может вызывать веб-службы, выполняется с помощью свойства Services ScriptManager. Это свойство позволяет определить одну или несколько служб, которые ASP.NET страница AJAX может асинхронно вызывать для отправки или получения данных без операций обратной передачи. Служба определяется с помощью элемента управления ASP.NET AJAX ServiceReference и назначения URL-адреса веб-службы свойству элемента управления Path . В листинге 12 показан пример ссылки на службу с именем CustomersService.asmx.

<asp:ScriptManager ID="ScriptManager1" runat="server">
     <Services>
          <asp:ServiceReference Path="~/CustomersService.asmx" />
     </Services>
</asp:ScriptManager>

Листинг 12. Определение веб-службы, используемой на странице ASP.NET AJAX.

Добавление ссылки на CustomersService.asmx с помощью элемента управления ScriptManager приводит к динамическому созданию прокси-сервера JavaScript, на которую ссылается страница. Прокси-сервер внедряется с помощью тега <скрипта> и динамически загружается путем вызова файла CustomersService.asmx и добавления /js в его конец. В следующем примере показано, как прокси-сервер JavaScript внедряется на страницу при отключении отладки в web.config:

<script src="CustomersService.asmx/js" type="text/javascript"></script>

>[! ПРИМЕЧАНИЕ. Если вы хотите просмотреть фактический созданный код прокси-сервера JavaScript, введите URL-адрес нужной веб-службы .NET в поле адреса интернет-Обозреватель и добавьте /js в его конец.

Если отладка включена в web.config на страницу будет внедрена отладочная версия прокси-сервера JavaScript, как показано ниже:

<script src="CustomersService.asmx/jsdebug" type="text/javascript"></script>

Прокси-сервер JavaScript, созданный с помощью ScriptManager, также можно внедрить непосредственно на страницу, а не ссылаться с помощью <атрибута src тега скрипта> . Это можно сделать, присвоив свойству InlineScript элемента управления ServiceReference значение true (значение по умолчанию — false). Это может быть полезно, если прокси-сервер не используется на нескольких страницах и вы хотите уменьшить количество сетевых вызовов, выполненных на сервере. Если для InlineScript задано значение true, скрипт прокси-сервера не будет кэшироваться браузером, поэтому рекомендуется использовать значение по умолчанию false в тех случаях, когда прокси-сервер используется несколькими страницами в приложении ASP.NET AJAX. Ниже показан пример использования свойства InlineScript:

<asp:ServiceReference InlineScript="true" Path="~/CustomersService.asmx"/>

Использование прокси-серверов JavaScript

После того как на веб-службу ссылается страница ASP.NET AJAX с помощью элемента управления ScriptManager, можно выполнить вызов веб-службы, а возвращаемые данные можно обрабатывать с помощью функций обратного вызова. Веб-служба вызывается путем ссылки на ее пространство имен (если таковое существует), имя класса и имя веб-метода. Любые параметры, передаваемые в веб-службу, можно определить вместе с функцией обратного вызова, которая обрабатывает возвращаемые данные.

Пример использования прокси-сервера JavaScript для вызова веб-метода с именем GetCustomersByCountry() показан в листинге 13. Функция GetCustomersByCountry() вызывается, когда пользователь нажимает кнопку на странице.

Листинг 13. Вызов веб-службы с помощью прокси-сервера JavaScript.

function GetCustomerByCountry()
{
     var country = $get("txtCountry").value;
     InterfaceTraining.CustomersService.GetCustomersByCountry(country, OnWSRequestComplete);
}
function OnWSRequestComplete(results)
{
     if (results != null)
     {
          CreateCustomersTable(results);
          GetMap(results);
     }
}

Этот вызов ссылается на пространство имен InterfaceTraining, класс CustomersService и веб-метод GetCustomersByCountry, определенный в службе. Он передает значение страны, полученное из текстового поля, а также функцию обратного вызова с именем OnWSRequestComplete, которая должна вызываться при возврате асинхронного вызова веб-службы. OnWSRequestComplete обрабатывает массив объектов Customer, возвращенных службой, и преобразует их в таблицу, отображаемую на странице. Выходные данные, созданные в результате вызова, показаны на рис. 1.

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

Рис. 1. Привязка данных, полученных с помощью асинхронного вызова AJAX к веб-службе. (Щелкните для просмотра полноразмерного изображения)

Прокси-серверы JavaScript также могут выполнять односторонние вызовы веб-служб в тех случаях, когда необходимо вызвать веб-метод, но прокси-сервер не должен ждать ответа. Например, может потребоваться вызвать веб-службу, чтобы запустить процесс, например рабочий поток, но не ждать возвращаемого значения от службы. В случаях, когда необходимо выполнить односторонний вызов к службе, функцию обратного вызова, показанную в листинге 13, можно просто опустить. Так как функция обратного вызова не определена, прокси-объект не будет ждать, пока веб-служба вернет данные.

Обработка ошибок

При асинхронных обратных вызовах веб-служб могут возникать различные типы ошибок, таких как отключение сети, недоступность веб-службы или возврат исключения. К счастью, объекты прокси JavaScript, созданные ScriptManager, позволяют определить несколько обратных вызовов для обработки ошибок и сбоев в дополнение к обратному вызову успешного выполнения, показанного выше. Функция обратного вызова ошибки может быть определена сразу после стандартной функции обратного вызова в вызове веб-метода, как показано в листинге 14.

Листинг 14. Определение функции обратного вызова ошибок и отображение ошибок.

function GetCustomersByCountry() 
{
     var country = $get("txtCountry").value;
     InterfaceTraining.CustomersService.GetCustomersByCountry(country, 
          OnWSRequestComplete, OnWSRequestFailed);
}
function OnWSRequestFailed(error)
{
     alert("Stack Trace: " + error.get_stackTrace() + "/r/n" +
          "Error: " + error.get_message() + "/r/n" +
          "Status Code: " + error.get_statusCode() + "/r/n" +
          "Exception Type: " + error.get_exceptionType() + "/r/n" +
          "Timed Out: " + error.get_timedOut());
}

Любые ошибки, возникающие при вызове веб-службы, активируют функцию обратного вызова OnWSRequestFailed(), которая принимает объект, представляющий ошибку в качестве параметра. Объект error предоставляет несколько различных функций, чтобы определить причину ошибки, а также определить, истекло ли время ожидания вызова. В листинге 14 показан пример использования различных функций ошибок, а на рис. 2 показан пример выходных данных, созданных функциями.

Выходные данные, созданные вызовом ASP.NET функций ошибок AJAX.

Рис. 2. Выходные данные, созданные при вызове функций ASP.NET ошибок AJAX. (Щелкните для просмотра полноразмерного изображения)

Обработка XML-данных, возвращаемых веб-службой

Ранее вы видели, как веб-метод может возвращать необработанные XML-данные с помощью атрибута ScriptMethod вместе со свойством ResponseFormat. Если параметр ResponseFormat имеет значение ResponseFormat.Xml, данные, возвращаемые веб-службой, сериализуются в формате XML, а не в формате JSON. Это может быть полезно, если XML-данные необходимо передать непосредственно клиенту для обработки с помощью JavaScript или XSLT. В настоящее время Internet Обозреватель 5 или более поздней версии предоставляет лучшую объектную модель на стороне клиента для анализа и фильтрации XML-данных благодаря встроенной поддержке MSXML.

Получение XML-данных из веб-службы ничем не отличается от получения других типов данных. Начните с вызова прокси-сервера JavaScript, чтобы вызвать соответствующую функцию и определить функцию обратного вызова. После возврата вызова можно обработать данные в функции обратного вызова.

В листинге 15 показан пример вызова веб-метода с именем GetRssFeed(), который возвращает объект XmlElement. GetRssFeed() принимает один параметр, представляющий URL-адрес rss-канала для извлечения.

Листинг 15. Работа с XML-данными, возвращаемыми веб-службой.

function GetRss()
{
     InterfaceTraining.DemoService.GetRssFeed(
          "https://blogs.interfacett.com/dan-wahlins-blog/rss.xml",
          OnWSRequestComplete);
}
function OnWSRequestComplete(result)
{
     if (document.all) //Filter for IE DOM since other browsers are limited
     {
          var items = result.selectNodes("//item");
          for (var i=0;i<items.length;i++)
          {
               var title = items[i].selectSingleNode("title").text;
               var href = items[i].selectSingleNode("link").text;
               $get("divOutput").innerHTML +=
               "<a href='" + href + "'>" + title + "</a><br/>";
          }
     }
     else
     {
          $get("divOutput").innerHTML = "RSS only available in IE5+";
     }
}

Этот пример передает URL-адрес в RSS-канал и обрабатывает возвращенные XML-данные в функции OnWSRequestComplete(). OnWSRequestComplete() сначала проверяет, является ли браузер интернет-Обозреватель узнать, доступно ли средство синтаксического анализа MSXML. Если это так, то для поиска всех <тегов элементов> в RSS-канале используется оператор XPath. Затем выполняется итерация каждого элемента, а связанные <теги заголовка> и <ссылки> находятся и обрабатываются для отображения данных каждого элемента. На рисунке 3 показан пример выходных данных, созданных при выполнении вызова ASP.NET AJAX через прокси-сервер JavaScript для веб-метода GetRssFeed().

Обработка сложных типов

Сложные типы, принимаемые или возвращаемые веб-службой, автоматически предоставляются через прокси-сервер JavaScript. Однако вложенные сложные типы недоступны напрямую на стороне клиента, если атрибут GenerateScriptType не применяется к службе, как описано выше. Зачем использовать вложенный сложный тип на стороне клиента?

Чтобы ответить на этот вопрос, предположим, что на странице ASP.NET AJAX отображаются данные клиента и пользователи могут обновлять адрес клиента. Если веб-служба указывает, что тип Address (сложный тип, определенный в классе CustomerDetails) можно отправить клиенту, то процесс обновления можно разделить на отдельные функции для более эффективного повторного использования кода.

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

Рис. 3. Выходные данные, создаваемые при вызове веб-службы, которая возвращает данные RSS. (Щелкните для просмотра полноразмерного изображения)

В листинге 16 показан пример кода на стороне клиента, который вызывает объект Address, определенный в пространстве имен Model, заполняет его обновленными данными и назначает его свойству Address объекта CustomerDetails. Затем объект CustomerDetails передается в веб-службу для обработки.

Листинг 16. Использование вложенных сложных типов

function UpdateAddress()
{
     var cust = new Model.CustomerDetails();
     cust.CustomerID = $get("hidCustomerID").value;
     cust.Address = CreateAddress();
     InterfaceTraining.DemoService.UpdateAddress(cust,OnWSUpdateComplete);
}
function CreateAddress()
{
     var addr = new Model.Address();
     addr.Street = $get("txtStreet").value;
     addr.City = $get("txtCity").value;
     addr.State = $get("txtState").value;
     return addr;
}
function OnWSUpdateComplete(result)
{
     alert("Update " + ((result)?"succeeded":"failed")+ "!");
}

Создание и использование методов страницы

Веб-службы предоставляют отличный способ предоставления повторно используемых служб различным клиентам, включая ASP.NET страниц AJAX. Однако могут возникать случаи, когда странице необходимо извлечь данные, которые никогда не будут использоваться или совместно использоваться другими страницами. В этом случае создание ASMX-файла для предоставления странице доступа к данным может показаться чрезмерным, так как служба используется только одной страницей.

ASP.NET AJAX предоставляет еще один механизм для выполнения вызовов, похожих на веб-службу, без создания автономных ASMX-файлов. Для этого используется метод, называемый "методами страницы". Методы страниц — это статические (общие в VB.NET) методы, внедренные непосредственно в файл страницы или кода, к которым применяется атрибут WebMethod. Применив атрибут WebMethod, их можно вызывать с помощью специального объекта JavaScript с именем PageMethods, который динамически создается во время выполнения. Объект PageMethods выступает в качестве прокси-сервера, который защищает вас от процесса сериализации и десериализации JSON. Обратите внимание, что для использования объекта PageMethods необходимо задать свойству EnablePageMethods scriptManager значение true.

<asp:ScriptManager ID="ScriptManager1" runat="server" EnablePageMethods="true">
</asp:ScriptManager>

В листинге 17 показан пример определения двух методов страницы в ASP.NET классе, отличном от кода. Эти методы извлекают данные из класса бизнес-уровня, расположенного в папке App_Code веб-сайта.

Листинг 17. Определение методов страницы.

[WebMethod]
public static Customer[] GetCustomersByCountry(string country)
{
     return Biz.BAL.GetCustomersByCountry(country);
}
[WebMethod]
public static Customer[] GetCustomersByID(string id)
{
     return Biz.BAL.GetCustomersByID(id);
}

Когда ScriptManager обнаруживает наличие веб-методов на странице, создается динамическая ссылка на упомянутый ранее объект PageMethods. Вызов веб-метода осуществляется путем ссылки на класс PageMethods, за которым следует имя метода и все необходимые данные параметров, которые должны быть переданы. В листинге 18 показаны примеры вызова двух методов страницы, показанных ранее.

Листинг 18. Вызов методов страницы с помощью объекта JavaScript PageMethods.

function GetCustomerByCountry() 
{
     var country = $get("txtCountry").value;
     PageMethods.GetCustomersByCountry(country, OnWSRequestComplete);
}
function GetCustomerByID() 
{
     var custID = $get("txtCustomerID").value;
     PageMethods.GetCustomersByID(custID, OnWSRequestComplete);
}
function OnWSRequestComplete(results) 
{
     var searchResults = $get("searchResults");
     searchResults.control.set_data(results);
     if (results != null) GetMap(results[0].Country,results);
}

Использование объекта PageMethods очень похоже на использование прокси-объекта JavaScript. Сначала необходимо указать все данные параметров, которые должны быть переданы в метод page, а затем определить функцию обратного вызова, которая должна вызываться при возврате асинхронного вызова. Можно также указать обратный вызов сбоя (пример обработки сбоев см. в листинге 14).

AutoCompleteExtender и ASP.NET AJAX Toolkit

Набор средств ASP.NET AJAX (доступный в https://www.devexpress.com/Products/AJAX-Control-Toolkit) предлагает несколько элементов управления, которые можно использовать для доступа к веб-службам. В частности, набор средств содержит полезный элемент управления с именем AutoCompleteExtender , который можно использовать для вызова веб-служб и отображения данных на страницах без написания кода JavaScript.

Элемент управления AutoCompleteExtender можно использовать для расширения существующих функциональных возможностей текстового поля и упрощения поиска нужных данных. При вводе в текстовое поле элемент управления можно использовать для запроса веб-службы и динамически отображать результаты под текстовым полем. На рисунке 4 показан пример использования элемента управления AutoCompleteExtender для отображения идентификаторов клиентов для приложения поддержки. Когда пользователь вводит различные символы в текстовое поле, под ним будут отображаться различные элементы в зависимости от введенных данных. Затем пользователи могут выбрать нужный идентификатор клиента.

Для использования автозаполненияExtender на странице ASP.NET AJAX требуется добавить сборку AjaxControlToolkit.dll в папку bin веб-сайта. После добавления сборки набора средств вы захотите сослаться на нее в web.config чтобы содержащиеся в ней элементы управления были доступны на всех страницах приложения. Это можно сделать, добавив следующий тег в тег элементов управления> web.config<:

<add namespace="AjaxControlToolkit" assembly="AjaxControlToolkit" tagPrefix="ajaxToolkit"/>

В случаях, когда необходимо использовать элемент управления только на определенной странице, можно сослаться на него, добавив директиву Reference в начало страницы, как показано далее, а не обновляя web.config:

<%@ Register Assembly="AjaxControlToolkit" Namespace="AjaxControlToolkit" 
     TagPrefix="ajaxToolkit" %>

Использование элемента управления AutoCompleteExtender.

Рис. 4. Использование элемента управления AutoCompleteExtender. (Щелкните для просмотра полноразмерного изображения)

После настройки веб-сайта для использования набора средств ASP.NET AJAX на страницу можно добавить элемент управления AutoCompleteExtender так же, как и обычный серверный элемент управления ASP.NET. В листинге 19 показан пример использования элемента управления для вызова веб-службы.

Листинг 19. С помощью элемента управления autocompleteExtender набора средств ASP.NET AJAX.

<ajaxToolkit:AutoCompleteExtender ID="extTxtCustomerID" runat="server"
     MinimumPrefixLength="1" ServiceMethod="GetCustomerIDs"
     ServicePath="~/CustomersService.asmx"
     TargetControlID="txtCustomerID" />

AutoCompleteExtender имеет несколько различных свойств, включая стандартные идентификаторы и свойства runat, доступные в серверных элементах управления. Кроме того, он позволяет определить, сколько символов вводит пользователь перед запросом данных веб-службы. Свойство MinimumPrefixLength, показанное в листинге 19, вызывает службу при каждом вводе символа в текстовое поле. Вам следует внимательно задать это значение, так как каждый раз, когда пользователь вводит символ, веб-служба будет вызываться для поиска значений, соответствующих символам в текстовом поле. Вызываемая веб-служба, а также целевой веб-метод определяются с помощью свойств ServicePath и ServiceMethod соответственно. Наконец, свойство TargetControlID определяет, к какому текстовому поле следует подключить элемент управления AutoCompleteExtender.

Вызываемая веб-служба должна иметь атрибут ScriptService, как описано выше, а целевой веб-метод должен принимать два параметра с именами prefixText и count. Параметр prefixText представляет символы, введенные конечным пользователем, а параметр count — количество возвращаемых элементов (значение по умолчанию — 10). В листинге 20 показан пример веб-метода GetCustomerIDs, вызываемого элементом управления AutoCompleteExtender, показанным ранее в листинге 19. Веб-метод вызывает метод бизнес-слоя, который, в свою очередь, вызывает метод уровня данных, который обрабатывает фильтрацию данных и возвращает соответствующие результаты. Код для метода уровня данных показан в листинге 21.

Листинг 20. Фильтрация данных, отправленных из элемента управления AutoCompleteExtender.

[WebMethod]
public string[] GetCustomerIDs(string prefixText, int count) 
{
     return Biz.BAL.GetCustomerIDs(prefixText, count);
}

Листинг 21. Фильтрация результатов на основе введенных пользователем данных.

public static string[] GetCustomerIDs(string prefixText, int count)
{
     //Customer IDs cached in _CustomerIDs field to improve performance
     if (_CustomerIDs == null)
     {
          List<string> ids = new List<string>();
          //SQL text used for simplicity...recommend using sprocs
          string sql = "SELECT CustomerID FROM Customers";
          DbConnection conn = GetDBConnection();
          conn.Open();
          DbCommand cmd = conn.CreateCommand();
          cmd.CommandText = sql;
          DbDataReader reader = cmd.ExecuteReader();
          while (reader.Read())
          {
               ids.Add(reader["CustomerID"].ToString());
          }
          reader.Close();
          conn.Close();
          _CustomerIDs = ids.ToArray();
     }
     int index = Array.BinarySearch(_CustomerIDs, prefixText, new CaseInsensitiveComparer());
     //~ is bitwise complement (reverse each bit)
     if (index < 0) index = ~index;
     int matchingCount;
     for (matchingCount = 0; matchingCount < count && index + matchingCount < _CustomerIDs.Length; matchingCount++)
     {
          if (!_CustomerIDs[index + matchingCount].StartsWith(prefixText, StringComparison.CurrentCultureIgnoreCase))
          {
               break;
          }
     }
     String[] returnValue = new string[matchingCount];
     if (matchingCount > 0)
     {
          Array.Copy(_CustomerIDs, index, returnValue, 0, matchingCount);
     }
     return returnValue;
}

Заключение

ASP.NET AJAX обеспечивает отличную поддержку вызова веб-служб без написания большого количества пользовательского кода JavaScript для обработки запросов и ответных сообщений. В этой статье вы узнали, как включить AJAX-веб-службы .NET, чтобы они могли обрабатывать сообщения JSON и как определять прокси JavaScript с помощью элемента управления ScriptManager. Вы также узнали, как прокси-серверы JavaScript можно использовать для вызова веб-служб, обработки простых и сложных типов и устранения сбоев. Наконец, вы узнали, как методы страницы можно использовать для упрощения процесса создания и выполнения вызовов веб-службы и как элемент управления AutoCompleteExtender может предоставлять помощь конечным пользователям при вводе. Хотя UpdatePanel, доступный в ASP.NET AJAX, безусловно, будет управлять выбором для многих программистов AJAX из-за своей простоты, знание того, как вызывать веб-службы через прокси JavaScript, может быть полезно во многих приложениях.

Биография

Дэн Wahlin (Microsoft Most Valuable Professional для веб-служб ASP.NET и XML) является инструктором по разработке .NET и консультантом по архитектуре в Interface Technical Training (http://www.interfacett.com). Дэн основал веб-сайт XML для разработчиков ASP.NET (www.XMLforASP.NET), находится в Бюро спикеров INETA и выступает на нескольких конференциях. Дэн является соавтором Профессиональная днк Windows (Wrox), ASP.NET: Советы, Учебники и код (Sams), ASP.NET 1.1 Insider Solutions, Professional ASP.NET 2.0 AJAX (Wrox), ASP.NET 2.0 MVP Hacks и автор XML для разработчиков ASP.NET (Sams). Когда он не пишет код, статьи или книги, Дэн любит писать и записывать музыку и играть в гольф и баскетбол со своей женой и детьми.

Скотт Кейт работает с веб-технологиями Майкрософт с 1997 года и является президентом myKB.com (www.myKB.com), где он специализируется на написании ASP.NET приложений, ориентированных на решения базы знаний. Скотт можно связаться по электронной почте по адресу scott.cate@myKB.com или его блог на ScottCate.com