C# WCF 多服务接口开发

配置文件写法: 

     <services>
      <service name="arManagement.arManage">
        <endpoint address="net.tcp://localhost:8008/arManageService" binding="netTcpBinding"
          bindingConfiguration="" contract="domain.IArManageService" />
      </service>
      <service name="ar.wcfhost.SystemAdmin">
        <endpoint address="net.tcp://localhost:8008/SystemAdminService" binding="netTcpBinding"
          bindingConfiguration="" contract="domain.ISystemAdminService" />
      </service>
    </services>

宿主程序写法:

 var varsystemAdminHost = new ServiceHost(typeof(SystemAdmin));
 systemAdminHost.Open();

 var arMgtHost = new ServiceHost(typeof(ARManage));
 arMgtHost.Open();
 LogFactory.Log.Info("WCF 服务已开启");

  其中SystemAdmin.cs为:

public class SystemAdmin : ISystemAdminService
    {
        public string GetData(int value)
        {
            return string.Format("You entered: {0}", value);
        }
}

接口实现为:

 [ServiceContract]
    public interface ISystemAdminService
    {
        [OperationContract]
        string GetData(int value);
    }

客户端访问封装:

public static IServiceFactory ServiceFactory
        {
            get
            {
                return ServiceLocator.Current.GetInstance(typeof(IServiceFactory)) as IServiceFactory;
            }
        }
       
        public static void UseService<TChannel>(string uri, Action<TChannel> action) where TChannel:class
        {
            var channel = ServiceFactory.CreateService<TChannel>(uri);
            action(channel);
            try
            {
                ((IClientChannel)channel).Close();
            }
            catch
            {
                ((IClientChannel)channel).Abort();
            }
        }

public static void SystemAdminInvoke(Action<ISystemAdminService> action)
{
    UseService(systemAdminURI,action);//const string systemAdminURI = "SystemAdminService";
}

IServiceFacotry实现:

 public T CreateService<T>(string uri) where T : class
        {
            var binding = new NetTcpBinding();            
            string addressUri = $"{ConfigHelper.GetAppConfig("devServiceAddress")}{uri}";           
            var address = new EndpointAddress(addressUri);
            var chanFactory = new ChannelFactory<T>(binding,address);
            T channel = chanFactory.CreateChannel();
            ((IClientChannel)channel).Open();
            return channel;
        }

另一个问题:身份验证问题,如果调用接口需要Log日志,需要获取用户的id等信息,有必要做验证。一个经过测试可行的做法如下。

1:改造服务器端:

 List<Type> hostType = new List<Type>()
            {
                typeof(SystemAdmin),
                typeof(ARManage)
            };
            hosts = new List<ServiceHost>();
            foreach (var item in hostType)
            {
                var host = new ServiceHost(item);
                host.Credentials.ServiceCertificate.SetCertificate("CN=localhost", StoreLocation.LocalMachine, StoreName.My);       
                host.Credentials.UserNameAuthentication.UserNamePasswordValidationMode
                    = System.ServiceModel.Security.UserNamePasswordValidationMode.Custom;
                host.Credentials.UserNameAuthentication.CustomUserNamePasswordValidator = new MyManagementCallValidator();

                if (host.State != CommunicationState.Opening)
                    host.Open();
                hosts.Add(host);
            }

netTcpBinding配置如下: 

 <netTcpBinding>
           <binding name="WcfNewBinding">
             <security mode="Message">
               <transport clientCredentialType="None" />
               <message clientCredentialType="UserName" />
             </security>
           </binding>
        </netTcpBinding>

 自定义的用户密码验证类:

public class MyManagementCallValidator : UserNamePasswordValidator
    {
        public override void Validate(string userName, string password)
        {            
        }
    }

客户端创建服务接口改造:

public T CreateService<T>(string uri) where T : class
        {
            var binding = new NetTcpBinding();
            var security = new NetTcpSecurity();
            security.Mode = SecurityMode.Message;
            security.Transport = new TcpTransportSecurity();
            security.Transport.ClientCredentialType = TcpClientCredentialType.None;
            security.Message = new MessageSecurityOverTcp();
            security.Message.ClientCredentialType = MessageCredentialType.UserName;
            binding.Security = security;            
            
            string addressUri =  $"{ConfigHelper.GetAppConfig("devServiceAddress")}{uri}";
            var address = new EndpointAddress(new Uri(addressUri), EndpointIdentity.CreateDnsIdentity("localhost"));
            var chanFactory = new ChannelFactory<T>(binding,address);
            chanFactory.Credentials.ServiceCertificate.Authentication.CertificateValidationMode = System.ServiceModel.Security.X509CertificateValidationMode.None;
            UserNamePasswordClientCredential credential = chanFactory.Credentials.UserName;
            credential.UserName = Global.LoginUserId;
            credential.Password = Global.LoginUserPwd;
            T channel = chanFactory.CreateChannel();          
            ((IClientChannel)channel).Open();
            return channel;
        }

报错1:

传出消息标识检查失败。所预期的远程终结点的 DNS 标识为“xx.xx”,但是远程终结点提供的 DNS 请求为“localhost”。 

解决办法:var address = new EndpointAddress(new Uri(addressUri), EndpointIdentity.CreateDnsIdentity("localhost")); 

报错2:

本地开发没有问题,部署到服务器上报错:无法使用以下搜索标准找到 X.509 证书: StoreName“My”、StoreLocation“LocalMachine”、FindType“FindBySubjectDistinguishedName”、FindValue“CN=localhost”。

参考:http://www.cnblogs.com/chucklu/p/4685783.html 

安装证书即可:

服务器端获取用户身份标识的写法:

 IIdentity id = OperationContext.Current.ServiceSecurityContext.PrimaryIdentity;
            string userId = id.Name;
            return composite;

调试程序,进入了断点:

以上为net.tcp的绑定,如果要同时支持http访问,只需增加一个http服务接口

  #region 添加http访问
                var httpHost = new ServiceHost(typeof(APIService));
                System.ServiceModel.Channels.Binding httpbinding = new BasicHttpBinding();
                httpHost.AddServiceEndpoint(typeof(IAPIService), httpbinding, "http://localhost:8002");
                if (httpHost.Description.Behaviors.Find<System.ServiceModel.Description.ServiceMetadataBehavior>() == null)
                {
                    ServiceMetadataBehavior behavior = new ServiceMetadataBehavior();
                    behavior.HttpGetEnabled = true;
                    behavior.HttpGetUrl = new Uri("http://localhost:8002/api");
                    httpHost.Description.Behaviors.Add(behavior);
                    httpHost.Open();
                    hosts.Add(httpHost);
                }
                #endregion

或者:var httpHost = new ServiceHost(typeof(APIService));

 httpHost.Open();

然后在配置文件中添加相应的配置:

  <system.serviceModel>
     <bindings>     
       <basicHttpBinding>
         <binding name="BasicHttpBindingDataService">
           <security mode="None">
             <transport clientCredentialType="None" />
             <message clientCredentialType="UserName" />
           </security>
         </binding>
       </basicHttpBinding>
     </bindings>
     <services>       
       <service  name="managgement.wcfhost.APIService"   behaviorConfiguration="httpBehavior">
         <host>
             <baseAddresses>
                  <add baseAddress="http://localhost:8002/api"/>           
           </baseAddresses>          
         </host>
         <endpoint address=""
            binding="basicHttpBinding" bindingConfiguration="BasicHttpBindingDataService"                    
            contract="managgement.wcfhost.IAPIService" />
       </service>
     </services>
    <behaviors>
      <serviceBehaviors>  
        <behavior name="httpBehavior">
          <serviceMetadata httpGetEnabled="True"/>
          <serviceDebug includeExceptionDetailInFaults="False"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值