Подтвердить что ты не робот

Как я могу объединить конфигурацию служб WCF для http и https в одном web.config?

Я потратил много времени на то, как настроить мои службы WCF, чтобы они работали на https в рабочей среде.

В принципе, мне нужно было сделать это:

<behaviors>
  <serviceBehaviors>
    <behavior name="MyServiceBehavior">
      <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
      <serviceDebug includeExceptionDetailInFaults="true" />
    </behavior>
  </serviceBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" aspNetCompatibilityEnabled="true" />
<services>
  <service name="MyNamespace.MyService" behaviorConfiguration="MyServiceBehavior">
    <endpoint address="" bindingNamespace="https://secure.mydomain.com" binding="basicHttpBinding" bindingConfiguration="HttpsBinding" contract="MyNamespace.IMyService"/>
  </service>
</services>
<bindings>
  <basicHttpBinding>
    <binding name="HttpsBinding">
      <security mode="Transport">
        <transport clientCredentialType="None"></transport>
      </security>
    </binding>
  </basicHttpBinding>
</bindings>

Добавление атрибута bindingNamespace к конечной точке - последнее, что заставило его работать.

Но эта конфигурация не работает в моей локальной среде, где я работаю под обычным http. Итак, моя конфигурация:

<behaviors>
  <serviceBehaviors>
    <behavior name="MyServiceBehavior">
      <serviceMetadata httpGetEnabled="true" httpsGetEnabled="false" />
      <serviceDebug includeExceptionDetailInFaults="true" />
    </behavior>
  </serviceBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" aspNetCompatibilityEnabled="true" />
<services>
  <service name="MyNamespace.MyService" behaviorConfiguration="MyServiceBehavior">
    <endpoint address="" binding="basicHttpBinding" contract="MyNamespace.IMyService"/>
  </service>
</services>

Различия здесь в том, что я установил атрибут httpsGetEnabled в значение false, и я удалил bindingConfiguration и bindingNamespace.

Проблема заключается в следующем: как создать один блок конфигурации, который обрабатывает BOTH?

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

Я пробовал что-то вроде этого:

<behaviors>
  <serviceBehaviors>
    <behavior name="MyServiceBehavior">
      <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
      <serviceDebug includeExceptionDetailInFaults="true" />
    </behavior>
  </serviceBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" aspNetCompatibilityEnabled="true" />
<services>
  <service name="MyNamespace.MyService" behaviorConfiguration="MyServiceBehavior">
    <endpoint address="" binding="basicHttpBinding" contract="MyNamespace.IMyService"/>
    <endpoint address="" bindingNamespace="https://secure.mydomain.com" binding="basicHttpBinding" bindingConfiguration="HttpsBinding" contract="MyNamespace.IMyService"/>
  </service>
</services>
<bindings>
  <basicHttpBinding>
    <binding name="HttpsBinding">
      <security mode="Transport">
        <transport clientCredentialType="None"></transport>
      </security>
    </binding>
  </basicHttpBinding>
</bindings>

Я полагал, что включение обеих конечных точек даст ему два варианта поиска при активации службы. Однако это не работает. Я получаю эту ошибку:

Не удалось найти базовый адрес, соответствующий схеме https для конечной точки со связыванием BasicHttpBinding. Схемы зарегистрированных базовых адресов: [http].

От взгляда вокруг SO и остальной части Интернета, похоже, что у других были проблемы с убийством этого дракона.

4b9b3361

Ответ 1

Ну, одна проблема с вашей комбинированной конфигурацией заключается в том, что ваши две конечные точки находятся на одном адресе - это не сработает.

Если вы размещаете в IIS, то ваш сервер, виртуальный каталог и необходимый файл *.svc будут определять ваш основной адрес - это будет что-то вроде:

http://yourservername/VirtualDirectory/YourService.svc

Если вы хотите иметь две конечные точки, по крайней мере один из них должен определить относительный адрес:

<services>
    <service name="MyNamespace.MyService" 
             behaviorConfiguration="MyServiceBehavior">
       <endpoint 
           address="basic" 
           binding="basicHttpBinding" 
           contract="MyNamespace.IMyService"/>
       <endpoint 
           address="secure" 
           binding="basicHttpBinding" bindingConfiguration="HttpsBinding"  
           contract="MyNamespace.IMyService"/>
    </service>
</services>

В этом случае у вас будет конечная точка HTTP:

http://yourservername/VirtualDirectory/YourService.svc/basic

и ваша защищенная конечная точка HTTPS:

https://yourservername/VirtualDirectory/YourService.svc/secure

Кроме того: ваша защищенная конечная точка использует конфигурацию HttpsBinding - но вам не хватает такой конфигурации привязки - все, что у вас есть:

<bindings>
  <basicHttpBinding>
    <binding name="HttpBinding">
      <security mode="None">
        <transport clientCredentialType="None"></transport>
      </security>
    </binding>
  </basicHttpBinding>
</bindings>

Вам нужно добавить конфигурацию HttpsBinding!

<bindings>
  <basicHttpBinding>
    <binding name="HttpBinding">
      <security mode="None">
        <transport clientCredentialType="None"></transport>
      </security>
    </binding>
    <binding name="HttpsBinding">
      <security mode="Transport">
          <transport clientCredentialType="Windows" />
      </security>
    </binding>
  </basicHttpBinding>
</bindings>

Ответ 2

Проблема связана не с конфигурационным файлом, а с настройкой IIS. Вам необходимо включить HTTP и HTTPS в IIS. В IIS 7.5 перейдите на свой сайт и нажмите "Привязки" в разделе "Изменить действие сайта". Убедитесь, что добавлены как http, так и https. Затем вам нужно создать привязку для HTTP под <basicHttpBinding>, при этом режим безопасности не будет установлен. Добавьте вновь созданную конфигурацию привязки к конечной точке http. Тебе хорошо идти. Дайте мне знать, если вам нужна дальнейшая проблема.

Ответ 3

Решением для запуска в локальном домене, а также для работы в производственной и других средах, не полагаясь на память, чтобы что-либо изменить, является преобразование конфигурации. Они преобразуют скомпилированный файл web.config на основе выбранного профиля конфигурации. Локально я работаю в режиме Debug, в среде тестирования я публикую в профиль TestRelease, и у меня есть другой профиль:

Web.Config Transforms in Visual Studio

Если вы не можете расширить свой файл web.config, вы можете щелкнуть правой кнопкой мыши и добавить конфигурационные преобразования. Чтобы получить больше, чем Debug и Release, вы добавляете больше конфигураций через менеджер:

Configuration Manager in Visual Studio

Вот пример преобразования:

Web.Debug.config

<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
  <!--...-->
  <system.serviceModel>
    <protocolMapping>
      <add binding="basicHttpBinding" scheme="http" xdt:Transform="SetAttributes" />
    </protocolMapping>
    <bindings>
      <basicHttpBinding>
        <binding xdt:Locator="Match(name)" name="basicHttpBindingConfiguration">
          <security xdt:Transform="Remove">
            <transport xdt:Transform="Remove"/>
          </security>
        </binding>
        <binding xdt:Locator="Match(name)" name="fileTransferBinding">
          <security xdt:Transform="Remove">
            <transport xdt:Transform="Remove"/>
          </security>
        </binding>
      </basicHttpBinding>
    </bindings>
  </system.serviceModel>
</configuration>

Web.Release.config

<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
  <!--...-->
  <system.serviceModel>
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <serviceMetadata httpGetEnabled="false" httpsGetEnabled="false" xdt:Transform="Replace"/>
          <serviceDebug includeExceptionDetailInFaults="false" xdt:Transform="Replace"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <protocolMapping>
      <add binding="basicHttpsBinding" scheme="https" xdt:Transform="Replace"/>
    </protocolMapping>
    <bindings>
      <basicHttpBinding>
        <binding xdt:Locator="Match(name)" name="basicHttpBindingConfiguration">
          <security mode="Transport" xdt:Transform="Insert">
            <transport clientCredentialType="None" proxyCredentialType="None" />
          </security>
        </binding>
        <binding xdt:Locator="Match(name)" name="fileTransferBinding">
          <security mode="Transport" xdt:Transform="Insert">
            <transport clientCredentialType="None" proxyCredentialType="None" />
          </security>
        </binding>
      </basicHttpBinding>
    </bindings>
  </system.serviceModel>
  <system.webServer>
    <directoryBrowse enabled="false"  xdt:Transform="Replace"/>
  </system.webServer>
</configuration>

Ответ 4

Недавно мне пришлось сделать службу WCF 3.5 REST (webHttpBinding) доступной как по HTTP, так и по HTTPS в службе приложений Microsoft Azure (IIS). Это было веселое и мучительное приключение. Вот мои выводы и мой web.config <system.serviceModel>:

Примечание: эти примечания предназначены для веб-службы WCF REST, работающей с использованием файлов *.svc (@ServiceHost) в минимальном приложении ASP.NET 4.7 (с Global.asax) в IIS 10 на Windows Server 2016. Эти примечания не применяется к автономным службам WCF, службам WCF без REST (например, SOAP) или к платформам, более ранним, чем .NET Framework 4.7. Это также не относится к .NET Core.

  • Обновление до .NET Framework 4.7.2 или новее (это просто здравый смысл)
  • Самая важная часть - это использование элемента <serviceHostingEnvironment>, убедитесь, что установлен multipleSiteBindingsEnabled="true".
  • Вам вообще не нужен элемент <serviceMetadata> в вашем файле web.config.
    • Очевидно, этот элемент предназначен для метаданных WSDL, а службы RESTful не поддерживают WSDL.
    • Я указываю на это, потому что есть по крайней мере несколько лучших результатов поиска Google для статей и сообщений Qaru, где люди говорят, что это требуется. Эти люди не правы.
  • Не используйте абсолютные URI в ваших атрибутах <endpoint address="" - просто оставьте атрибут пустым.
    • Это было необходимо только в WCF 3.5.
    • Microsoft говорит оставить это поле пустым (или использовать относительный URI для конечных точек PATH_INFO -style) при размещении WCF в IIS:
      • Прочитайте эту статью: https://docs.microsoft.com/en-us/dotnet/framework/wcf/feature-details/deploying-an-internet-information-services-hosted-wcf-service

        Вы всегда должны использовать относительные адреса конечных точек для конечных точек служб IIS. Предоставление полностью определенного адреса конечной точки (например, http://localhost/MyService.svc) может привести к ошибкам при развертывании службы, если адрес конечной точки не указывает на приложение IIS, в котором размещается служба, предоставляющая конечную точку. Использование относительных адресов конечных точек для размещенных служб позволяет избежать этих потенциальных конфликтов.

  • WCF определит, можно ли достичь этого с использованием как HTTP, так и HTTPS, и выдаст ошибку, если файл web.config скажет WCF принять HTTPS-подключения, когда в хост-приложении (т.е. IIS) не включен HTTPS на его родительском веб-сайте.
    • Это было неожиданно для меня, потому что все остальные платформы веб-приложений, которые я использовал, не жалуются, если они настроены для HTTPS, а родительский веб-сервер - нет.
      • Особенно с учетом того, что в этом случае WCF работает с/внутри конвейера запросов .NET, а ASP.NET, безусловно, не заботится о привязке веб-сайтов IIS (поскольку ASP.NET не зависит от привязок родительских веб-сайтов)
      • Примечание. "Привязки веб-сайтов" относятся к привязкам веб-сайтов IIS, , а не - "привязкам WCF", которые представляют собой отдельную концепцию.
  • Чтобы это работало локально в Visual Studio и IIS Express, убедитесь, что родительский проект веб-приложения имеет "SSL Enabled = True" в окне "Свойства" (не то же самое, что окно "Свойства проекта" (да, я тоже кричал) ):
    • enter image description here
  • В .NET 4.6.1 Microsoft упростила настройку WCF, позволив элементу <services> опускаться и автоматически генерироваться за кадром, однако у меня не было согласованных результатов со службами RESTful с привязками двойного HTTP + HTTPS, поэтому я все еще указываю свой <services> вручную.

TL; ДР:

Вот мой элемент <system.serviceModel> из моего файла web.config:

<system.serviceModel>
    <services>
        <service name="WcfService1.MainService">
            <endpoint address="" binding="webHttpBinding" contract="WcfService1.IMainService" behaviorConfiguration="myWebBehavior" bindingConfiguration="myWebHttpBindingInsecure" />
            <endpoint address="" binding="webHttpBinding" contract="WcfService1.IMainService" behaviorConfiguration="myWebBehavior" bindingConfiguration="myWebHttpBindingSecure" />
        </service>
        <service name="WcfService1.AnotherService">
            <endpoint address="" binding="webHttpBinding" contract="WcfService1.IAnotherService" behaviorConfiguration="myWebBehavior" bindingConfiguration="myWebHttpBindingInsecure" />
            <endpoint address="" binding="webHttpBinding" contract="WcfService1.IAnotherService" behaviorConfiguration="myWebBehavior" bindingConfiguration="myWebHttpBindingSecure" />
        </service>
        <!--  etc... -->
    </services>
    <behaviors>
        <endpointBehaviors>
            <behavior name="myWebBehavior">
                <webHttp />
            </behavior>
        </endpointBehaviors>
        <serviceBehaviors>
            <behavior>
                <serviceDebug includeExceptionDetailInFaults="true" />
            </behavior>
        </serviceBehaviors>
    </behaviors>
    <protocolMapping>
        <!-- By default (in machine.config), the 'http' scheme is mapped to 'basicHttpBinding' (SOAP), not 'webHttpBinding' (REST) and the 'https' scheme is not mapped. -->
        <add binding="webHttpBinding" scheme="https" bindingConfiguration="myWebHttpBindingSecure" />
        <add binding="webHttpBinding" scheme="http"  bindingConfiguration="myWebHttpBindingInsecure" />
    </protocolMapping>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
    <bindings>
        <webHttpBinding>
            <!-- maxReceivedMessageSize="104857600" is 100MiB -->
            <binding name="myWebHttpBindingInsecure" maxReceivedMessageSize="104857600" transferMode="Streamed">
                <security mode="None" />
            </binding>
            <binding name="myWebHttpBindingSecure" maxReceivedMessageSize="104857600" transferMode="Streamed">
                <security mode="Transport" />
            </binding>
        </webHttpBinding>
    </bindings>
</system.serviceModel>