Хостинг WCF сервиса на IIS 7 для Silverlight приложения

Опубликовано Апр 26, 2009 в ASP.NET, Silverlight, WCF | 3 комментария

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

Поэтому я собрал в одном месте минимально достаточные инструкции по настройке WCF сервиса. Исходные возможности такие: хостинг с IIS7, ASP.NET 2.0, .NET Framework 3.5 SP1 (хостинг-провайдер masterhost, конфигурация Windows 2008 хостинга на текущее время), WCF сервис для Silverlight2 приложения. Отсутствует возможность запуска в командной строке сервера любых утилит для настроек IIS под WCF а также запуска IIS Manager Administration Tool (masterhost предоставляет возможность использовать для IIS7 Manager Administration Tool, но эта утилита не будет работать если ваш доступ в интернет осуществляется через proxy-сервер). Необходимо добиться устойчивой работы WCF сервиса.

Шаг 1. Single WCF Service in multiple domains

Деплоим Web проект на сервер хостинга и проверяем работу WCF сервиса: http://domain.tld/MyService.svc

Появляется сообщение об ошибке «Server Error in ‘/’ Application.» и предложение установить в файле конфигурации Web.config тэг <customErrors mode=»Off»> для возможности узнать детальные сведения об ошибке. Устанавливаем  требуемые настройки, не забывая, что позднее их необходимо будет вернуть в прежнее состояние для production сервера.

<configuration> 
  <system.web> 
    <customErrors mode="Off"/> 
  </system.web> 
</configuration>

Снова тестируем сервис: http://domain.tld/MyService.svc

Теперь мы видим сообщение об ошибке, оно гласит:

[ServiceActivationException: The service ‘/WcfService.svc’ cannot be activated due to an exception during compilation.  The exception message is: This collection already contains an address with scheme http.  There can be at most one address per scheme in this collection.  Parameter name: item.]

Многие источники рекомендуют в данной ситуации написать свой ServiceHostFactory, но этого делать не надо! Для исправления этой ошибки достаточно добавить в файл конфигурации тэг baseAddressPrefixFilters с указанием протокола, хоста и порта сервера на котором размещается ваш сервис. Например, если ваш сервис располагается по адресу http://domain.tld:8080/Services/WcfService.svc, то порт необходимо указать, а вот путь /Services/WcfService.svc — нет, т.е. мы используем строку «http://domain.tld:8080/».

Было:

<configuration> 
  <system.serviceModel> 
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true"/> 
  </system.serviceModel> 
</configuration>

Стало:

<configuration> 
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true"> 
      <baseAddressPrefixFilters> 
        <add prefix="http://domain.tld"/> 
      </baseAddressPrefixFilters> 
    </serviceHostingEnvironment> 
</configuration>

Полностью секция system.serviceModel должна выглядеть так:

<configuration> 
  <system.serviceModel> 
    <behaviors> 
      <serviceBehaviors> 
        <behavior name="Namespace.WcfServiceBehavior"> 
          <serviceMetadata httpGetEnabled="true"/> 
          <serviceDebug includeExceptionDetailInFaults="false"/> 
        </behavior> 
      </serviceBehaviors> 
    </behaviors> 
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true"> 
      <baseAddressPrefixFilters> 
        <add prefix="http://domain.tld"/> 
      </baseAddressPrefixFilters> 
    </serviceHostingEnvironment> 
    <services> 
      <service behaviorConfiguration="Namespace.WcfServiceBehavior" name="Namespace.WcfService"> 
        <endpoint address="" binding="basicHttpBinding" contract="Namespace.WcfService"/> 
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/> 
      </service> 
    </services> 
  </system.serviceModel> 
</configuration>

Тестируем: http://domain.tld/MyService.svc, сервис работает.

Отлично, однако, это только начало.

Демонстрация работы WCF сервиса

Учтите, что локально WCF сервис при этом перестанет работать, а указать несколько префиксов с разными доменами, но одинаковым протоколом невозможно, поэтому ввиду большого числа различий между файлами конфигурации Web.config на хостинге и на компьютере разработчика, рекомендуется редактировать их раздельно и отключить deploy этого файла на production сервер.

На компьютере разработчика соответственно префикс должен быть таким:

        <add prefix="http://localhost:1656/"/>

или с другим портом в соответствии с вашими настройками. Либо лучше вообще закомментировать в файле конфигурации разработчика всю секцию baseAddressPrefixFilters, т.к. порт может меняться, и его правка причинит неудобства.

Кстати при разработке WCF сервисов для удобства разработчика желательно настроить использование постоянного порта для сервера встроенного в среду разработки (Visual Studio Development Server).

Не забудте отредактировать на сервере хостинга файл Web.config, убрав тэг <customErrors mode=»Off»/>. Заодно для production сервера рекомендуется отключить режим отладки в тэге <compilation debug=»true»>.

Шаг 2.

Другая ошибка, с которой вы можете столкнуться: IIS не видит .svc файл, сообщая об ошибке 404 — файл не найден.

Один из советов заключается в том, что необходимо установить Windows Communication Foundation в Панели управления->Установка и удаление->Включение и отключение компонентов Windows. Но так как нам недоступны такие операции на сервере хостинга, то проблема решается редактированием в файле Web.config специфичной для IIS7 секции system.webServer. Мы добавляем в нее Handler для обработки *.svc файлов.

<configuration> 
  <system.webServer> 
    <handlers> 
      <remove name="SvcHandler"/> 
      <add name="SvcHandler" path="*.svc" verb="*" type="System.ServiceModel.Activation.HttpHandler, System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" resourceType="Unspecified" preCondition="integratedMode" /> 
    </handlers> 
  </system.webServer> 
</configuration>

Шаг 3.

Теперь нам необходимо настроить Silverlight приложение на работу с WCF сервисом расположенным не на компьютере разработчика, а на сервере хостинга.

Рассмотрим файл ServiceReferences.Config расположенный в корне Silverlight приложения:

<configuration> 
  <system.serviceModel> 
    <bindings> 
      <basicHttpBinding> 
        <binding name="BasicHttpBinding_WcfService" maxBufferSize="2147483647" maxReceivedMessageSize="2147483647"> 
          <security mode="None" /> 
        </binding> 
      </basicHttpBinding> 
    </bindings> 
    <client> 
      <endpoint address="http://localhost:1656/WcfService.svc" binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_WcfService" contract="WcfReference.WcfService" name="BasicHttpBinding_WcfService" /> 
    </client> 
  </system.serviceModel> 
</configuration>

Меняем адрес сервиса на реальный:

<endpoint address="http://domain.tld/WcfService.svc" binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_WcfService" contract="WcfReference.WcfService" name="BasicHttpBinding_WcfService" />

Шаг 4. Cross domain WCF access

Запускаем Silverlight приложение. При попытке обращения к WCF сервису оно выдает сообщение об ошибке:

An error occurred while trying to make a request to URI ‘http://domain.tld/WcfService.svc’. This could be due to attempting to access a service in a cross-domain way without a proper cross-domain policy in place, or a policy that is unsuitable for SOAP services. You may need to contact the owner of the service to publish a cross-domain policy file and to ensure it allows SOAP-related HTTP headers to be sent. Please see the inner exception for more details.

Для устранения этой ошибки необходимо разместить в корне вашего Web-сервера (обратите внимание — в корне сервера, а не приложения) файл clientaccesspolicy.xml со следующим содержимым:

<?xml version="1.0" encoding="utf-8" ?> 
<access-policy> 
  <cross-domain-access> 
    <policy> 
      <allow-from http-request-headers="SOAPAction"> 
        <domain uri="*"/> 
      </allow-from> 
      <grant-to> 
        <resource path="/" include-subpaths="true"/> 
      </grant-to> 
    </policy> 
  </cross-domain-access> 
</access-policy>

3 комментария : “Хостинг WCF сервиса на IIS 7 для Silverlight приложения”

  1. >> Для устранения этой ошибки необходимо разместить в корне вашего Web-сервера (обратите внимание – в корне сервера, а не приложения) файл clientaccesspolicy.xml

    Веб-сервера, на котором находится silverlight приложение или все таки по адресу веб-сервиса?

  2. admin пишет:

    Если на веб-сервере несколько доменов, то файл clientaccesspolicy.xml должен быть доступен в корне того домена, в котором расположено приложение с WCF сервисом.

    Если WCF сервис доступен как http://domain.ru/services/my-service.svc

    то clientaccesspolicy.xml должен быть тут:

    http://domain.ru/clientaccesspolicy.xml

Оставить комментарий