Создание WCF сервиса для JSON

Источник: msug

В субботу я впервые выступил с докладом в Southern California Code Camp о том, как использовать JQuery, для выполнения AJAX-операций к конечной точке(endpoint) в WCF. Сегодня я хочу показать, как создать WCF сервис с поддержкой JSON и каким образом создавать сообщения, которые могут сереализоваться в JSON и обратно.

Первым шагом будет добавление WCF сервиса к веб-сайту. Это может быть ваш текущий проект, хотя это не обязательно. При добавлении сервиса через Visual Studio 2008 у нас создаётся svc-файл, а также контент, который, в нашем случае, необходимо удалить.

Visual Studio 2008 and .NET 3.5

Visual Studio 2010 вот-вот появится, поэтому я попробую сделать это двумя способами. В течении нескольких дней я напишу о том, как сделать это с помощью Visual Studio 2010. В диалогом окне Add New Item выберите WCF Service. После этого к вашему веб-сайту добавится файл .svc, а так же ещё два файла и некоторые ненужные секции в файле web.config.

Начнём с файлов, добавленных в проект. Файл (.vb или .cs) определяет интерфейс, реализуемый сервисом. В нём так же находятся атрибуты, говорящие WCF о том, как использовать сервис. К .svc файлу также добавляется code-behind файл, в котором описана реализация сервиса. Оба эти файла можно использовать, однако я предпочитаю удалять их. Вместо этого я создам библиотеку классов, которая будет содержать и интерфейс, и реализацию. В конечном итоге это более понятный путь для дальнейшего управления кодом ваших WCF сервисов. Во-первых это позволяет легче создавать unit тесты (я опишу это в другом докладе). Во-вторых это даёт определённую гибкость при добавлении другого поставщика, реализующего интерфейс сервиса. Вы увидите, что такой путь не усложнит разработку, а всего лишь потребует добавления ссылки на библиотеку с классом.

Файл Web.config слегка загрязнился, потому что были добавлены ненужные секции behaviors, services и endpoint в секцию system.serviceModel. Ниже приведен пример добавленной конфигурации, которую можно смело удалять из файла.

<behaviors>
  <serviceBehaviors>
    <behavior name="Web.TestsvcBehavior">
      <serviceMetadata httpGetEnabled="true" />
      <serviceDebug includeExceptionDetailInFaults="false" />
    </behavior>
  </serviceBehaviors>
</behaviors>
<services>
  <service behaviorConfiguration="Web.TestsvcBehavior" name="Web.Testsvc">
    <endpoint address="" binding="wsHttpBinding" contract="Web.ITestsvc">
      <identity>
        <dns value="localhost" />
      </identity>
    </endpoint>
    <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
  </service>
</services>
<bindings>
  <basicHttpBinding>
    <binding name="basicHttp" maxReceivedMessageSize="2147483647">
      <security mode="None"/>
    </binding>
  </basicHttpBinding>
</bindings>

В конечном итоге это сводится к конфигурации сервиса, и я не буду отрицать, что мало знаю о конфигурировании WCF. У меня есть люди для этого. Поэтому, двинемся дальше, а в этом месте вам придётся поверить мне на слово.

Единственное, что осталось изменить - это .svc файл для корректной реализации нашего сервиса. Вот пример директивы, которая по умолчанию задаётся в .svc файле.

<%@ servicehost language="C#" debug="true" service="Web.Testsvc" codebehind="Testsvc.svc.cs" %>

Ниже показана новая версия директивы, в которой указано, какой класс будет использоваться для реализации сервиса, а так же подключен класс WebScriptServiceHostFactory, который позволяет использовать веб протоколы и добавляет JSON сериализацию, которая нам необходима для работы нашего сервиса с JQuery Ajax.

<%@ ServiceHost Language="C#"
    Service="WCFJQuery.ContactBLL.Implementation.TestSvc"
    Factory="System.ServiceModel.Activation.WebScriptServiceHostFactory" %>

Библиотека классов

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

Атрибуты WCF находятся в пространстве имён System.ServiceModel, поэтому вам нужно добавить на него ссылку. Если вы используете инструмент R#, то он сам добавит ссылку, как только вы напишете первый атрибут. Первый файл, который мы добавим - это интерфейс сервиса. В нашем примере я использую контактную форму, поэтому сервис называется ContactUs. Следовательно интерфейс будется называться IContactUs.cs.

Если вы помните, в интерфейсе, который мы удалили, класс был отмечен атрибутом ServiceContract. Сейчас мы определим пространство имён для интерфейса, имени сервиса, режима сессии и уровня защиты.

[ServiceContract(Namespace = "urn:WCFJQuery.ContactBLL.Contracts",
    Name = "ContactUs",
    SessionMode = SessionMode.NotAllowed,
    ProtectionLevel = ProtectionLevel.None)]

Теперь мы можем добавить сигнатуры методов в наш интерфейс. Для нашего приложения добавим 4 метода: PostNewContact, GetContact, GetActiveContacts и BadMethod. Каждый метод описывается как указано ниже:

[OperationContract(IsTerminating = false,
    IsInitiating = true,
    IsOneWay = false,
    AsyncPattern = false,
    Action = "PostNewContact",
    ProtectionLevel = ProtectionLevel.None)]
PostNewContactResponse PostNewContact(PostNewContactRequest request);

Далее, мы можем добавить класс для реализации сервиса. Я помещу этот файл в папку Implementation. Создаём файл ContactUs.cs. Класс ContactUs реализует интерфейс IContactUs и описан атрибутом ServiceBehavior. В этом примере свойство IncludeExceptionDetailInFaults установлено в true. Это сделано для того, что бы сообщения об исключениях отправлялись на клиент сериализуясь в нужном виде. В нашем случае JQuery получит JSON данные.

Члены класса не обязательно описывать какими-нибудь атрибутами. Вы можете подключить их автоматически, используя SmartTag на имени интерфейса или инструменты типа R# или CodeRush.

Сейчас у вас должны быть ссылки на классы, которые не существуют. Мы должны создать их. Они так же добавляются в папку Implementation. Каждый из этих классов - класс сообщений. Эти классы содержат тело сообщений, которое является содержимым запроса к сервису и ответа от него.

Пока вы окончательно не потерялись, нужно кое что прояснить. Архитектура WCF и SOA основана на передаче сообщений и объектов. Обычно, каждый метод сервиса принимает и возвращает какой-либо объект.

Я всегда люблю проверять возвращаемый объект на клиенте. В этом различие между мной и тем, кто сфокусирован на разработке WCF части приложения. Первым, что меня натолкнуло на это, был метод, который добавлял новую запись в базу данных. Автор метода не возвращал никакого подтверждающего значения или вообще какого-либо значения, просто null. Он объяснял это тем, что если что то не сработает, то сгенерируется исключение. Я думаю это плохая практика в любом коде, так как вы не должны показывать исключение клиенту. Вместо этого, вы должны обработать исключение и вернуть осмысленное значение. Вы можете прочитать больше об этом в книге Брэда Абрамса.

Давайте вернёмся к коду и посмотрим что нужно передать в запрос для того, что бы вернуть объект "Контакт". Очевидно что методу сервиса необходим ContactId, который используется как ключ для объекта "Контакт".

Обратите внимание, я не говорю о записе в таблице "Контакты", потому что современные приложения работают с моделью данных, а не с базой на прямую. Например Entity Framework, oData или nHibernate. Фактически объект "Контакт" может быть составлен из записей разных таблиц.

Класс GetContactRequest должен быть сериализован с помощью WCF, поэтому нам необходимо добавить атрибут DataContact к классу и атрибут DataMember для каждого члена класса. В этом атрибуте определяется имя члена класса. Оно может быть любым, но вам нужно запомнить его для доступа на клиенте. Затем мы должны указать, что это значение обязательно(IsRequired=true), иначе мы попытаемся выполнить запрос без указания id контакта, который нам нужен. В конце нужно указать порядок расположения данных.(Order = 0). Это подразумевает то, что значение будет первым или с индексом 0 в сериализуемом объекте.

using System.Runtime.Serialization;

namespace WCFJQuery.ContactBLL.Implementation {

[DataContract(Namespace = "urn:WCFJQuery.ContactBLL.Implementation",
Name = "GetContactRequest")]

    public class GetContactRequest {
        [DataMember(Name = "ContactId", IsRequired = true, Order = 0)]
        public int ContactId { get; set; }

    }
}

Для класса GetContactResponse нужно подключить пространство имён System.Runtime.Serialization и добавить необходимые атрибуты. В этом классе находится всего один член - объект класса ContactInfo.

using System.Runtime.Serialization;

namespace WCFJQuery.ContactBLL.Implementation {

[DataContract(Namespace = "urn:WCFJQuery.ContactBLL.Implementation",
Name = "GetActiveContactsRequest")]

    public class GetContactResponse {
        [DataMember(Name = "ContactInfo", IsRequired = true, Order = 0)]
        public ContactInfo contactInfo { get; set; }

    }
}

Класс ContactInfo так же требует сериализации. Поэтому вы опять добавляете необходимые атрибуты. Класс ContaсtInfo содержит более одного члена класса, некоторые помечены атрибутом IsRequired, некоторые нет. В этот раз вы задаёте порядок расположения данных с индексом в диапазоне от 1 до 11.

using System.Runtime.Serialization;

namespace WCFJQuery.ContactBLL.Implementation {

    [DataContract(Namespace = "urn:WCFJQuery.ContactBLL.Implementation",
    Name = "ContactInfo")]
    public class ContactInfo {

        [DataMember(Name = "ContactId", IsRequired = false, Order = 0)]
        public int ContactId { get; set; }

        [DataMember(Name = "FirstName", IsRequired = true, Order = 1)]
        public string FirstName { get; set; }

    ...

        [DataMember(Name = "Coment", IsRequired = true, Order = 11)]
        public string Comment { get; set; }

    }
}

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

Теперь у нас есть готовый WCF веб-сервис, принимающий и возвращающий JSON объекты, которые могут быть использованы на клиенте. В следующем докладе мы рассмотрим инфраструктуру и реализацию AJAX вызовов, использующих JSON к WCF сервису.

Исходный код


Страница сайта http://test.interface.ru
Оригинал находится по адресу http://test.interface.ru/home.asp?artId=24040