前言
上一篇的学习中碰到一个问题,用地址http://localhost:8080/mex 访问元数据的时候一直提示400 bad request 错误,因为时间太晚了,查了好几遍代码,也没有发现问题。刚刚又试验了一下,解决方案分两步
①用管理员方式运行vs,
② 将
<serviceMetadata httpGetEnabled="true" />
改成
<serviceMetadata httpGetEnabled="true" httpGetUrl="mex"/>
也就是 我们给他指定一个httpGetUrl,至于为什么这么做,是参考的serviceMetadata。
最后,要说关于mex endpoint的作用,通俗的来讲就是只要客户端访问这个地址,他就可以知道关于这个WCF服务一些说明介绍。
第四集 WCF service implementing multiple service contracts
假设有这样的场景,有个公司需要通过http 给外网提供一个公共服务,同时又需要通过tcp协议给公司防火墙内的内容用户提供另一个服务,并且,还不想写多个WCF服务。
所以这一集主要两点,
- 在一个WCF服务中实现两个contract
- 通过endpoint配置对外界提供这两个服务 contract
关于如何搭建一个WCF服务,可以参考上一篇,这集只讲一些其他的重点。
首先是新建一个类库,然后添加一个WCF服务项,取名CompanyService。然后打开ICompanyService.cs,删除里面原先的ICompanyService接口,添加如下两个接口。
[ServiceContract] public interface ICompanyPublicService { [OperationContract] string GetPublicInformation(); } [ServiceContract] public interface ICompanyConfidentialService { [OperationContract] string GetConfidentialInformation(); }
解释一下,我们定义了两个合约,一个是公用PublicService ,一个是机密的ConfidentialService,里面分别有各自的GetInformation方法。
然后打开CompanyService.cs 文件,修改里面的CompanyService 类为如下内容:
public class CompanyService : ICompanyPublicService, ICompanyConfidentialService { public string GetConfidentialInformation() { return "这是机密内网的服务"; } public string GetPublicInformation() { return "这是外网的公共服务"; } }
解释一下,我们的服务名称依旧没有改变,还是CompanyService,但是他实现了上面定义的两个有ServiceContract特性的契约接口。
类定义完毕,然后是配置文件部分。关于整体的配置文件,请看上一篇,下面是完整代码。
<system.serviceModel> <services> <service name="Part4.CompanyService" behaviorConfiguration="mexBehavior"> <endpoint address="CompanyService" binding="basicHttpBinding" contract="Part4.ICompanyPublicService"></endpoint> <endpoint address="CompanyService" binding="netTcpBinding" contract="Part4.ICompanyConfidentialService"></endpoint> <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"></endpoint> <host> <baseAddresses> <add baseAddress="http://localhost:8080/"/> <add baseAddress="net.tcp://localhost:8090/"/> </baseAddresses> </host> </service> </services> <behaviors> <serviceBehaviors > <behavior name="mexBehavior"> <serviceMetadata httpGetEnabled="true" httpGetUrl="mex"/> </behavior> </serviceBehaviors> </behaviors> </system.serviceModel>
因为自己也是初学者,所以在手写的时候还是会碰到一些问题。比如 service节点 的name 属性,表示服务的名字,内容是实现接口的带命名空间的类名;而endpoint里面的contract的名字是定义的时候的带命名空间的接口名字。同时,为了让客户端可以通过添加服务引用的方式来调用,不要忘记了mex endpoint ,以及serviceMetadata元素。
然后新建一个控制台程序,用来托管这个服务。关于如何创建,依旧查看上一篇。启动服务。
完成之后编写客户端代码来调用。
我们新建一个Asp.net 的空网站,然后添加服务引用:
可以看到,CompanyService里面包含了两个服务。点击确定,查看一下网站的web.config 文件。
<system.serviceModel> <bindings> <basicHttpBinding> <binding name="BasicHttpBinding_ICompanyPublicService" /> </basicHttpBinding> <netTcpBinding> <binding name="NetTcpBinding_ICompanyConfidentialService" /> </netTcpBinding> </bindings> <client> <endpoint address="http://localhost:8080/CompanyService" binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_ICompanyPublicService" contract="CompanyService.ICompanyPublicService" name="BasicHttpBinding_ICompanyPublicService" /> <endpoint address="net.tcp://localhost:8090/CompanyService" binding="netTcpBinding" bindingConfiguration="NetTcpBinding_ICompanyConfidentialService" contract="CompanyService.ICompanyConfidentialService" name="NetTcpBinding_ICompanyConfidentialService"> <identity> <userPrincipalName value="LOUSAIBIAO\Sheldon" /> </identity> </endpoint> </client> </system.serviceModel>
这些都是自动生成的代码,用来配合我们客户端的调用。
然后给网站添加一个新页面,拖两个button ,两个label (你们都懂得)大致就这个效果。
编写两个button的点击事件。
protected void Button1_Click(object sender, EventArgs e) { //因为有多个endpoint ,所以要指定名字 var client = new CompanyPublicServiceClient("BasicHttpBinding_ICompanyPublicService"); Label1.Text = client.GetPublicInformation(); } protected void Button2_Click(object sender, EventArgs e) { var client = new CompanyConfidentialServiceClient("NetTcpBinding_ICompanyConfidentialService"); Label2.Text = client.GetConfidentialInformation(); }
调试运行该网站,并点击按钮,得到如下结果。
至此,实现方面的介绍完毕,有一点要说明的,因为我们是在vs上本机调试,说以,两个button都能获取的数据,但如果是过防火墙的外网来访问,在没有给控制面板中的防火墙添加额外的入站出站规则的时候,GetConfidential 方法是无效的。
介绍完毕,Thank you。