ASP.NET

Настройка Visual Studio Development Server для разработки WCF сервисов

Веб сервер встроенный в среду разработки — Visual Studio Development Server, по умолчанию использует динамически назначаемый порт. Обычно это не причиняет нам никаких неудобств. Но когда мы разрабатываем WCF сервис, динамически назначаемый порт весьма неудобен. Дело в том, что при обновлении Service Reference делается попытка обновления с того порта, который использовался при создании Service Reference, но так как он меняется, то попытка обновления будет неудачной. Мы конечно можем изменить настройки, изменив порт в адресе сервиса, но делать это каждый раз неудобно:
Демонстрация работы WCF сервиса Демонстрация работы WCF сервиса
Демонстрация работы WCF сервиса
Решение этой проблемы, это задание постоянного порта для веб сервера Visual Studio.
Откройте свойства вашего Web проекта в котором находятся WCF сервисы и на закладке Web смените Auto-assign Port на Specific port:
Демонстрация работы WCF сервиса

Далее

Простая регистрация в ASP.NET приложении

Разрабатывая приложение на ASP.NET, требующее регистрации пользователей, мы сталкиваемся с проблемой слишком сложной процедуры регистрации. Во-первых по умолчанию требуется задавать достаточно сложный пароль, длинной от 7 символов с обязательным использованием не только цифр но и спец-символов типа @#$. Во-вторых требуется указать секретный вопрос и ответ. Это хорошие требования, но не все наши ASP.NET приложения настолько серьезны, что требуют длинного и сложного пароля и секретных вопросов.

Для того, чтобы упростить процедуру регистрации есть простой способ. Он заключается в изменении файла конфигурации Web.config. В секции
<providers></providers>нам необходимо добавить дополнительные атрибуты для Membership Provider.

<configuration>
  <system.web>
    <membership defaultProvider=»MembershipProvider»>
      <providers>
        <clear/>
        <add name=»MembershipProvider» connectionStringName=»MemberchipConnection» applicationName=»My.WebApplication» type=»System.Web.Security.SqlMembershipProvider»
             requiresQuestionAndAnswer=»false»
             requiresUniqueEmail=»true»
             minRequiredPasswordLength=»6″
             minRequiredNonalphanumericCharacters=»0″
             />
      </providers>
    </membership>
  </system.web>
</configuration>

Как видите, мы избавились от требований задавать секретные вопросы и ответы, использовать спец-символы в пароле, уменьшили минимальную длинну пароля но оставили требование уникальности Email адреса, если мы не хотим дублирующихся регистраций одного и того-же пользователя.

Если же вам нравится использовать регулярные выражения, то вы можете задать атрибут passwordStrengthRegularExpression=»…» и прописать в нем условие, каким бы вам хотелось видеть пароль.

Существует и множество дополнительных атрибутов для настройки MembershipProvider:

enablePasswordRetrieval=»false»
enablePasswordReset=»true»
passwordFormat=»Hashed»
maxInvalidPasswordAttempts=»5″
passwordAttemptWindow=»10″

Полный их список можно посмотреть в MSDN: http://msdn.microsoft.com/en-us/library/f1kyba5e.aspx

Далее

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

После переноса 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>
Далее