Сообщений 1    Оценка 0        Оценить  
Система Orphus

Разработка и сопровождение корпоративных Silverlight-приложений с доступом через ISA-сервер и Load Balance System по протоколам http и https

Автор: Шилин Валентин Алексеевич
Опубликовано: 04.10.2011
Исправлено: 10.12.2016
Версия текста: 1.1
Введение
WCF клиент
WCF сервер
Заключение
Список литературы

Введение

При разработке корпоративных Silverlight (WCF) приложений очень часто допускается ошибка проектирования, связанная с отсутствием поддержки прокси-серверов. Эта ошибка является следствием того, что приложения, написанные с использованием предыдущих web-технологий, таких как ASP.NET, PHP, PERL и др., не нуждались в какой-либо специальной реализации функциональности поддержки прокси-серверов, за исключением случаев абсолютной адресации внутри приложения, что не являлось частой ситуацией. Тем не менее, корпоративные web-приложения редко используются напрямую, и переадресация запросов ISA серверами или системами распределения нагрузки с автоматическим изменением протоколов c https на http и наоборот является распространенной практикой. В данной статье будет продемонстрирован подход, не претендующий на универсальность, но позволяющий избежать многих проблем.

WCF клиент

Любое WCF-приложение имеет клиентскую часть, осуществляющую связь с серверной частью, являющейся набором WCF-сервисов. Но на пути следования запросов и отклика на них могут находиться различные системы, изменяющие адреса. Такими системами могут являться системы распределения нагрузки или прокси-сервера, такие как ISA-сервера. В некоторых случаях возможно комбинирование этих систем. Более того, может изменяться не только адрес, но и протокол, например с HTTP на HTTPS и наоборот. Такие сложные системы часто появляются вследствие политик безопасности компаний, и потому корпоративное WCF приложение должно поддерживать эти конфигурации. Для программистов клиентских приложений это означает отсутствие заранее определенного протокола связи с серверной частью и заранее определенных адресов WCF-сервисов.

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

<configuration>
  <system.serviceModel>
    <client>
      <endpoint binding="customBinding" contract="CONTRACT"name="NAME" />
    </client>
  </system.serviceModel>
</configuration>

Как можно видеть из приведенного XML, в нем не определяются специфические параметры связи с WCF-сервисом. Это сделано специально, чтобы приложение не зависело от какого-либо конкретного протокола. Все дополнительные параметры вынесены в код, где они динамически определяются и подставляются.

      namespace WCFServices
{
    using System;
    using System.ComponentModel.Composition;

    [Export(typeof(ICustomLoginService))]
    publicclass SecurityService : ICustomLoginService
    {
        privatereadonly CookieContainerWrapper _cookieContainerWrapper;
        privatereadonly LoginClient _client;

        [ImportingConstructor]
        public SecurityService(CookieContainerWrapper cookieContainerWrapper)
        {
            _cookieContainerWrapper = cookieContainerWrapper;
            _client = new LoginClient(new CustomBinding("CustomBinding_LoginClient", "", new BindingElement[]
            {
                new BinaryMessageEncodingBindingElement (), HtmlPage.Document.DocumentUri.Scheme.ToLower().Equals("https")? 
                new HttpsTransportBindingElement (){MaxReceivedMessageSize=2147483647}: 
                new HttpTransportBindingElementt(){ MaxReceivedMessageSize=2147483647, MaxBufferSize=2147483647}
            }), new EndpointAddress ("../Services/LoginService.svc"));

            _client.LoginCompleted += (s, e) =>
            {
                var userCallback = e.UserState as Action<bool, CustomException, Exception>;

                if (e.Error != null)
                {
                  var customError = e.Error as CustomException;
                  if (customError != null)
                  {
                      var securityFault = customError.Detail;
                      userCallback(false, customError, null, null);
                      return;
                  }
                  var exception = e.Error as Exception;
                  if (exception != null)
                  {
                      userCallback(false, null, exception, null);
                      return;
                  }
                  userCallback(false, null, null, e.Error);
                  return;
                }

                userCallback.Invoke(e.Result, null, null, null);
                return;
            };
        }

        publicvoid Login(string login, string password, Action<bool, CustomException, Exception> callback)
        {
            _client.LoginAsync(login, password, callback);
        }
    }
}

Данный код осуществляет асинхронное взаимодействие сервисов, и именно здесь определяются все необходимые параметры для их связи. Опустим описание стандартных механизмов MEF и остановимся только на существенных деталях.

  1. LoginClient – класс, сгенерированный Visual Studio при добавлении ссылки на сервис.
  2. ICustomLoginService - интерфейс для дальнейшего взаимодействия с сервисом из приложения, содержит только метод Login.
  3. ../Services/LoginServices.svc – путь к svc-файлам сервисов на сервере.

Наиболее интересным моментом в данном коде является создание объекта связи с сервисом _client. Как можно видеть, при задании протокола связи определяется схема, по которой было вызвано приложение. Для этого используется статическое свойство класса HtmlPage.DocumentUri.Scheme. В зависимости от того, какой адрес использовал пользователь для загрузки приложения(HTTP или HTTPS), подставляется HTTP- или HTTPS -транспорт, кроме того, используется относительный путь к сервису, что позволяет поддерживать случаи переписывания адресов системами распределения нагрузки.

Следующей возможной проблемой может оказаться неправильная настройка среды конечного пользователя. Это может быть что угодно, вплоть до не установленного WCF или любой проблемы с сетью. Службе поддержки будет крайне сложно определить причину без какой-либо дополнительной информации со стороны приложения. Как правило, на сервере ведется логирование всех исключений и даже регулярных транзакций, но проблема может быть и не в серверной части, и даже не в приложении. Тем не менее, задача приложения – облегчить работу службы поддержки насколько это возможно. Для этого стоит добавить логирование на стороне клиента. Как видно из приведенного выше кода, шаблон объекта Action содержит последние 2 аргумента, передающие исключения:

  1. CustomException – это исключение передается с сервера в случае возникновения каких-либо заранее определенных исключений
  2. Exception – это исключение передается в случае каких-либо проблем с сетью, окружением и т. д. Его стоит показывать в интерфейсе приложения в user friendly-стиле, и именно оно поможет службе поддержки диагностировать и устранить проблему в окружении. Это тоже серверное исключение.
  3. LoginCompleted e.Error - все проблемы с сетью, с адресами сервисов, возникшие на клиенте, будут переданы именно в этот параметр. Именно его также следует вывести в интерфейсе.

WCF сервер

Следующий шаг конфигурирования WCF-ориентированного приложения – это серверная часть. Здесь уже не имеет особого значения, по какому протоколу, через какие прокси-сервера идет запрос к сервисам. Все что необходимо сделать для корректной работы приложения это правильно сформировать endpoint адреса сервисов и указать правильные протоколы передачи и кодировки данных. Из постановки задачи следует, что сервисы должны одновременно работать по протоколам HTTP и HTTPS. Таким образом, логично предположить, что необходимо наличие как минимум двух endpoint-адресов на каждый сервис, один для протокола HTTP, другой для протокола HTTPS и, соответственно, две конфигурации customBinding.

  <system.serviceModel>
    <bindings>
      <customBinding>
        <binding name="silverlightBinaryBindingSSL">
          <binaryMessageEncoding>
          </binaryMessageEncoding>
          <httpsTransport maxReceivedMessageSize="2147483647" maxBufferSize="2147483647">
          </httpsTransport>
        </binding>
        <binding name="silverlightBinaryBinding">
          <binaryMessageEncoding>
          </binaryMessageEncoding>
          <httpTransport maxReceivedMessageSize="2147483647" maxBufferSize="2147483647">
          </httpTransport>
        </binding>
      </customBinding>
    </bindings>
    <services>
      <service name="LoginService">
<endpoint address="http://.../Services/LoginService.svc" listenUri="/" binding="customBinding" bindingConfiguration="silverlightBinaryBinding" contract="ILoginService" />
<endpoint address="https://.../Services/LoginService.svc" listenUri="/" binding="customBinding" bindingConfiguration="silverlightBinaryBindingSSL" contract="ILoginService" />
      </service>
    </services>
    <behaviors>
      <endpointBehaviors>
        <behavior name="Personec.Negotiation.Web.Services.WindowDataServiceAspNetAjaxBehavior">
          <enableWebScript>
          </enableWebScript>
        </behavior>
      </endpointBehaviors>
      <serviceBehaviors>
        <behavior name="MetadataEnabled">
          <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true">     
    </serviceHostingEnvironment>
  </system.serviceModel>

Выше приведена секция ServiceModel из web.config файла. Как и прежде, остановимся лишь на существенных деталях:

  1. silverlightBinaryBindingSSL – binding для https endpoint-адресов
  2. silverlightBinaryBinding – binding для http endpoint-адресов
  3. listenUri=”/” и multipleSiteBindingsEnabled=true – поддержка множественного связывания для IIS
  4. все endpoint-адреса должны быть внешними, то есть теми, по которым обращаются пользователи, а не ISA-сервер.

Здесь есть одна существенная деталь – объявляя одновременно HTTP- и HTTPS-адреса, необходимо разрешить IIS использовать HTTPS.

Для этого необходимо:

  1. Установить сервер сертификатов.
  2. Создать запрос на сертификат.
  3. Отправить запрос на сертификат в Verisign или создать сертификат в openssl или создать self signed sertificate.
  4. Сертификат установить на сервере.
  5. Настроить IIS на 443 порт.
  6. Указать в настройках IIS требование устанавливать защищенное соединение.

Заключение

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

Список литературы

  1. Джувел Леве Создание служб WPF (2008)
  2. Дж. Смит Основы Windows Communication Foundation БХВ-Петербург (2008)
  3. Пабло С., Курт К., Фабио К., Йохан Г. Windows Communication Fundation и .Net 4 для профессионалов


Любой из материалов, опубликованных на этом сервере, не может быть воспроизведен в какой бы то ни было форме и какими бы то ни было средствами без письменного разрешения владельцев авторских прав.
    Сообщений 1    Оценка 0        Оценить