wcf - 以编程方式使用 HTTPS 配置 ServiceHost 端点?

标签 wcf servicehost

我正在使用无文件激活,这是服务器端的完整 web.config,它有两个端点:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <configSections>
    <section name="entityFramework" 
             type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" 
             requirePermission="false" />
  </configSections>
  <connectionStrings>
    <add name="RedStripe"
         connectionString="Data Source=S964;Initial Catalog=MyDatabase;Persist Security Info=True;User ID=sa;Password=***;MultipleActiveResultSets=True"
         providerName="System.Data.SqlClient" />
  </connectionStrings>
  <system.web>
    <compilation debug="true" targetFramework="4.5" />
    <httpRuntime targetFramework="4.5" />
  </system.web>
  <entityFramework>
    <defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework">
      <parameters>
        <parameter value="mssqllocaldb" />
      </parameters>
    </defaultConnectionFactory>
    <providers>
      <provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
    </providers>
  </entityFramework>
  <system.web>
    <customErrors mode="Off"/>
  </system.web>
  <system.serviceModel>
    <serviceHostingEnvironment>
      <!-- where virtual .svc files are defined -->
      <serviceActivations>     
        <add service="Company.Project.Business.Services.AccountClassService" 
             relativeAddress="Account/AccountClassService.svc" 
             factory="Company.Project.WebHost.CustomServiceHostFactory"/>

        <add service="Company.Project.Business.Services.AccountService"
             relativeAddress="Account/AccountService.svc"
             factory="Company.Project.WebHost.CustomServiceHostFactory"/>

      </serviceActivations>
    </serviceHostingEnvironment>
  </system.serviceModel>
</configuration>

这是我的 CustomServiceHostFactory:

public class CustomServiceHostFactory : ServiceHostFactory
{
    protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
    {
        return new CustomServiceHost(serviceType, baseAddresses);
    }
}

这是我的 CustomServiceHost:

public class CustomServiceHost : ServiceHost
{        
    public CustomServiceHost(Type serviceType, Uri[] baseAddresses)
        : base(serviceType, baseAddresses)
    {
    }

    protected override void InitializeRuntime()
    {
        AddServiceDebugBehavior();
        AddWcfMessageLoggingBehavior();
        AddGlobalErrorHandlingBehavior();
        AddServiceCredentialBehavior();
        AddEndpoints();
        ConfigureThrottling();
        base.InitializeRuntime();
    }

    private void AddEndpoints()
    {
        var wsHttpBinding = WcfHelpers.ConfigureWsHttpBinding();

        foreach (Uri address in BaseAddresses)
        {
            var endpoint = new ServiceEndpoint(
                ContractDescription.GetContract(Description.ServiceType),
                wsHttpBinding, new EndpointAddress(address));

            AddServiceEndpoint(endpoint);

            //adding mex
            AddServiceMetadataBehavior();
            AddServiceEndpoint(
                ServiceMetadataBehavior.MexContractName,
                MetadataExchangeBindings.CreateMexHttpBinding(),
                address.AbsoluteUri + "/mex");

            break;
        }
    }
    private void AddGlobalErrorHandlingBehavior()
    {
        var errorHanlderBehavior = Description.Behaviors.Find<GlobalErrorBehaviorAttribute>();

        if (errorHanlderBehavior == null)
        {
            Description.Behaviors.Add(new GlobalErrorBehaviorAttribute(typeof(GlobalErrorHandler)));
        }
    }

    private void AddServiceCredentialBehavior()
    {
        var credentialBehavior = Description.Behaviors.Find<ServiceCredentials>();

        if (credentialBehavior == null)
        {
            var customAuthenticationBehavior = new ServiceCredentials();
            customAuthenticationBehavior.UserNameAuthentication.UserNamePasswordValidationMode = UserNamePasswordValidationMode.Custom;
            customAuthenticationBehavior.UserNameAuthentication.CustomUserNamePasswordValidator = new CustomUserNamePasswordValidator();
            Description.Behaviors.Add(customAuthenticationBehavior);
        }
    }
    private void AddServiceDebugBehavior()
    {
        var debugBehavior = Description.Behaviors.Find<ServiceDebugBehavior>();

        if (debugBehavior == null)
        {
            Description.Behaviors.Add(
                new ServiceDebugBehavior() {IncludeExceptionDetailInFaults = true});
        }
        else
        {
            if (!debugBehavior.IncludeExceptionDetailInFaults)
                debugBehavior.IncludeExceptionDetailInFaults = true;
        }
    }
    private void AddServiceMetadataBehavior()
    {
        var metadataBehavior = Description.Behaviors.Find<ServiceMetadataBehavior>();

        if (metadataBehavior == null)
        {
            ServiceMetadataBehavior serviceMetadataBehavior = new ServiceMetadataBehavior();
            serviceMetadataBehavior.HttpsGetEnabled = true;
            Description.Behaviors.Add(serviceMetadataBehavior);
        }
    }
    private void AddWcfMessageLoggingBehavior()
    {
        var messageInspectorBehavior = Description.Behaviors.Find<WcfMessageInspector>();

        if (messageInspectorBehavior == null)
        {
            Description.Behaviors.Add(new WcfMessageInspector());
        }
    }
    private void ConfigureThrottling()
    {
        var throttleBehavior = Description.Behaviors.Find<ServiceThrottlingBehavior>();

        if (throttleBehavior != null) return;

        throttleBehavior = new ServiceThrottlingBehavior
        {
            MaxConcurrentCalls = 100,
            MaxConcurrentInstances = 100,
            MaxConcurrentSessions = 100
        };

        Description.Behaviors.Add(throttleBehavior);
    }
}

最后是定义绑定(bind)的 WcfHelper。这是位于共享位置,因此我可以使用相同的方式以编程方式配置客户端绑定(bind):

public class WcfHelpers
{
    public static WSHttpBinding ConfigureWsHttpBinding()
    {
        return new WSHttpBinding
        {
            Name = "myWSHttpBinding",                
            OpenTimeout = new TimeSpan(0, 10, 0),
            CloseTimeout = new TimeSpan(0, 10, 0),
            SendTimeout = new TimeSpan(0, 10, 0),
            MaxBufferPoolSize = 104857600,
            MaxReceivedMessageSize = 104857600,
            Namespace = Constants.RedStripeNamespace,
            ReaderQuotas = new XmlDictionaryReaderQuotas()
            {
                MaxDepth = 104857600,
                MaxStringContentLength = 104857600,
                MaxArrayLength = 104857600,
                MaxBytesPerRead = 104857600,
                MaxNameTableCharCount = 104857600
            },
            Security =
            {
                Mode = SecurityMode.TransportWithMessageCredential,
                Message = { ClientCredentialType = MessageCredentialType.UserName }
            }
        };

    }
}

当我发布此 WebHost 项目并尝试浏览到两个地址之一时,如下所示: https://myserver/Project/Account/AccountService.svc 我收到以下错误:

The provided URI scheme 'http' is invalid; expected 'https'. Parameter name: context.ListenUriBaseAddress

我注意到,在 CustomServiceHost AddEndpoints() 方法中,当循环 BaseAddresses 时,如果我在那里硬编码一个地址,如下所示: https://myserver/Project/Account/AccountService.svc 然后我就可以成功浏览到它。使用无文件激活和相对寻址时如何构建 BaseAddresses?我在哪里可以指定他们使用 https(他们现在似乎使用 http)?

提前致谢。


编辑 1: 这将解决问题,但似乎完全是黑客行为,我在哪里使用无文件激活指定 https,以便使用 https 构建相对地址?

var endpoint = new ServiceEndpoint(ContractDescription.GetContract(Description.ServiceType),
wsHttpBinding, new EndpointAddress(address.OriginalString.Replace("http:", "https:")));

编辑2:我想我正在了解这里发生的事情。感谢@Andreas K 为我指明了正确的方向。如果我进入 IIS 并查看该站点的绑定(bind),则会发现有多个绑定(bind),如图所示:enter image description here

在循环 BaseAddresses 时,我在 AddEndpoints() 方法中放置了一些代码来写入数据库。当我尝试使用浏览器访问服务时,如下所示:https://my.server.local/Project/Account/AccountService.svc ,在数据库中创建两个条目。 http://my.server.local/Project/Account/AccountService.svc https://my.server.local/Project/Account/AccountService.svc

因此,IIS 站点绑定(bind)似乎正在被拾取。但是,现在我不确定为什么数据库中没有更多的 BaseAddresses 条目。 net.pipe、net.tcp 等在哪里?

最佳答案

事实证明,BaseAddresses 来自更新 2 中提到的 IIS 绑定(bind),再次感谢 @Andreas K 为我指明了正确的方向。在 IIS 中,我有一个网站,其下有多个应用程序。我在这些绑定(bind)上启用了 http 和 https。我已将 CustomServiceHost 中的 AddEndpoings() 方法更新为如下所示:

private void AddEndpoints()
{
    var wsHttpBinding = WcfHelpers.ConfigureWsHttpBinding();

    foreach (var address in BaseAddresses.Where(a => a.Scheme == Uri.UriSchemeHttps))
    {
        var endpoint = new ServiceEndpoint(
            ContractDescription.GetContract(Description.ServiceType),
            wsHttpBinding, 
            new EndpointAddress(address));

        AddServiceEndpoint(endpoint);
        AddServiceMetadataBehavior();
    }
}

由于站点下的其他应用程序需要http,因此我的BaseAddresses始终包含两个(http和https)。我需要手动过滤 http 的,因为我不想将它们暴露给这个特定的站点。现在我知道他们是如何填充的,我很满意。谢谢大家。

关于wcf - 以编程方式使用 HTTPS 配置 ServiceHost 端点?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29994822/

相关文章:

c# - 使用回调从WCF发送混合声音

c# - MTOM 是否需要 WCF 中的 messageContract?

wcf - 如何删除 ServiceHost 持有的大对象堆上的 Byte[]

WCF 服务 - 运行时在接口(interface)上看不到 ServiceContract

c# - WCF ServiceHost 似乎为每条消息调用 CustomCertificateValidator

wcf - 为 WCF 服务自动创建和部署 SSL 证书

c# - REST 参数 : object vs native datatypes

c# - MVVM + WCF 异步回调

c# - 这是托管 WCF 服务的正确方法吗?

c# - 如何在不是本地管理员的情况下使用 IPC?