WCF服务宿主及其跨域问题

WCF服务宿主及其跨域问题,第1张

概述WCF服务不是一个能单独执行的程序,需要寄宿在相关的可执行程序上执行,常见的宿主方式有IIS, 控制台(Winform,wpf), Windows Service这几种方式,在使用Silverlight调用发布Wcf服务会发生跨域错误如图: 什么是跨域呢? 一句话,同一个IP,同一个网络协议,同一个端口号,三者都同时满足就是同一个域,否则就是跨域访问,需要配置相应的跨域策略才能正常访问,所以Sil

WCF服务不是一个能单独执行的程序,需要寄宿在相关的可执行程序上执行,常见的宿主方式有IIS,控制台(Winform,wpf),windows Service这几种方式,在使用Silverlight调用发布Wcf服务会发生跨域错误如图:

什么是跨域呢? 一句话,同一个IP,同一个网络协议,同一个端口号,三者都同时满足就是同一个域,否则就是跨域访问,需要配置相应的跨域策略才能正常访问,所以Silverlight在调用wcf服务时由于出现端口号的不同所以发生跨域访问,所以需要配置相应的跨域策略文件,即clIEntaccesspolicy.xml。

本文主要介绍如何使用silverlight跨域访问wcf服务,顺带介绍wcf服务宿主控制台和windows service的方式,点击这里可以下载我测试的源码,开发环境为window7 vs2012 .NETFramework,Version=v4.5

1. @R_301_6954@案介绍

整个@R_301_6954@案如图:


整个@R_301_6954@案有5个项目,SLCallWcfService和SLCallWcfService.Web是Silverlight项目及其web项目用于调用wcf服务,WcfHostOnConsole是一个控制台项目,提供wcf的控制台宿主,WcfHostOnWindService是一个windows 服务项目,提供wcf的windows服务宿主,WcfServiceDll是一个类库项目,提供对wcf服务的设计和实现。

2. wcf服务的设计和实现

WcfServiceDll项目总的类如图:


ITestService是一个服务接口,只提供一个方法,返回一个字符串。

namespace WcfServiceDll{    /// <summary>    /// 服务接口    /// </summary>    [ServiceContract]    public interface ITestService    {        /// <summary>        /// 接口提供方法        /// </summary>        /// <returns></returns>        [OperationContract]        string GetTeststr();    }}

TestService是对ITestService 的实现

    /// <summary>    /// 服务实现    /// </summary>    public class TestService : ITestService    {        public string GetTeststr()        {            return "hello world !";        }    }

接下来是用于与跨域访问的服务及其实现IDomainService 和DomainService

IDomainService代码:

namespace WcfServiceDll{    [ServiceContract]    public interface IDomainService    {        [OperationContract]        [WebGet(UriTemplate = "ClIEntAccesspolicy.xml")]        Message ProvIDePolicyfile();    } }

DomainService代码:

    public class DomainService : IDomainService    {        static string fileContent = string.Empty;        public System.ServiceModel.Channels.Message ProvIDePolicyfile()        {            if (fileContent.Length == 0)            {                StreamReader fileStream = new StreamReader("ClIEntAccesspolicy.xml");                fileContent = fileStream.ReadToEnd();                fileStream.Close();            }            StringReader sr = new StringReader(fileContent);            XmlReader reader = XmlReader.Create(sr);            System.ServiceModel.Channels.Message result = Message.CreateMessage(MessageVersion.None,"",reader);            return result;        }    }

从上面的代码可以看出,读取了ClIEntAccesspolicy.xml的内容clIEntaccesspolicy.xml及我们跨域策略的配置文件:

<?xml version="1.0" enCoding="utf-8"?><access-policy>  <cross-domain-access>    <policy>      <allow-from http-request-headers="*">        <domain uri="*"/>      </allow-from>      <grant-to>        <resource path="/" include-subpaths="true"/>      </grant-to>    </policy>  </cross-domain-access></access-policy>

那么clIEntaccesspolicy.xml应该放在哪儿呢?这个后面会提到。这样我们的wcf服务类库就写好了,这只是一个类库文件是不能运行的,写成类库是方便其宿主在我们的控制台和windows服务程序中,这样我们的wcf配置文件App.config也应该写到相应的宿主中了。

注意要添加ystem.ServiceModel.dll,System.ServiceModel.Web.dll,System.Runtime.Serialization.dll的引用

3. wcf服务宿主控制台

wcf服务的宿主其实很简单,在我们的服务写好了之后无非就是创建服务,开启服务,然后在结束时关闭服务,那么我们的宿主程序中其实就是为wcf服务提供这样的一个执行环境吧(个人愚见),所以这里我们的WcfServiceDll中的服务要宿主在WcfHostOnConsole控制台中,则在控制台中开启我们的服务(注意有两个服务哦),然后配置好我们的App.config文件即可。

WcfHostOnConsole中的App.config文件配置:

<?xml version="1.0" enCoding="utf-8" ?><configuration>    <startup>         <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />    </startup>  <system.serviceModel>    <serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>    <behaviors>      <serviceBehaviors>        <behavior name="WCFCrossDomainService">          <serviceMetadata httpGetEnabled="true" />          <serviceDeBUG includeExceptionDetailinFaults="false" />        </behavior>      </serviceBehaviors>      <endpointBehaviors>        <behavior name="DomainServiceBehavior">          <webhttp/>        </behavior>      </endpointBehaviors>    </behaviors>    <services>      <service behaviorConfiguration="WCFCrossDomainService" name="WcfServiceDll.TestService">        <endpoint address="" binding="basichttpBinding" contract="WcfServiceDll.ITestService">          <IDentity>            <dns value="localhost" />          </IDentity>        </endpoint>        <endpoint address="mex" binding="mexhttpBinding" contract="IMetadataExchange" />        <host>          <baseAddresses>            <add baseAddress="http://localhost:9898/TestService/" />          </baseAddresses>        </host>      </service>      <service name="WcfServiceDll.DomainService">        <endpoint address="" behaviorConfiguration="DomainServiceBehavior"            binding="webhttpBinding" contract="WcfServiceDll.IDomainService" />        <host>          <baseAddresses>            <add baseAddress="http://localhost:9898/" />          </baseAddresses>        </host>      </service>    </services>  </system.serviceModel></configuration>

接下来就是开启服务:

    class Program    {        static voID Main(string[] args)        {            ServiceHost host = new ServiceHost(typeof(WcfServiceDll.TestService));            host.open();            Console.Writeline("数据服务开启");            ServiceHost crossDomainserviceHost = new ServiceHost(typeof(WcfServiceDll.DomainService));            crossDomainserviceHost.open();            Console.Writeline("跨域服务开启");            Console.Readline();            host.Close();        }    }
现在我们想想我们的跨域策略配置文件clIEntaccesspolicy.xml应该放在哪?因为我们的服务WcfServiceDll中的程序是被我们的控制台程序调用的,则起运行在我们控制台程序下,那么想要的读取配置策略文件clIEntaccesspolicy.xml也就应该放在控制台的跟目录中了,即bin/DeBUG中和WcfHostOnConsole.exe一个目录中。
注意要添加我们的服务项目WcfServiceDll的引用,还有System.ServiceModel.dll

这样我们生成我们的控制台程序,然后到项目bin/DeBUG中去找到控制台程序WcfHostOnConsole.exe即可运行,也可以设置WcfHostOnConsole为启动项运行,当然这样不方面后面的测试;


这样我们的数据服务和跨域服务都启动了,接下来在我们的silverlight项目SLCallWcfService中添加服务引用http://localhost:9898/TestService/如图:


然后我们的主界面MainPage.xaml中添加一个textBlock用于显示调用结果,一个button用户调用服务接口:

MainPage.xaml:

    <GrID x:name="LayoutRoot" Background="White">        <TextBlock name="txtblkStr" HorizontalAlignment="left" Height="25" margin="73,63,0" textwrapPing="Wrap" Text="" VerticalAlignment="top" WIDth="170"/>        <button Content="点击获取" HorizontalAlignment="left" Height="24" margin="73,93,0" VerticalAlignment="top" WIDth="72" Click="button_Click_1"/>    </GrID>

MainPage.xaml.cs:

    public partial class MainPage : UserControl    {        public MainPage()        {            InitializeComponent();        }        private voID button_Click_1(object sender,RoutedEventArgs e)        {            TestServiceClIEnt clIEnt = new TestServiceClIEnt();            clIEnt.GetTestStrCompleted += new EventHandler<GetTestStrCompletedEventArgs>(GetTestStrCompleted);            clIEnt.GetTestStrAsync();        }        private voID GetTestStrCompleted(object sender,GetTestStrCompletedEventArgs e)        {            this.txtblkStr.Text = e.Result.ToString();        }    }
好了,这样我们的在我们服务开始后就可以运行我们的silverlight项目然后调用测试

运行成功结果:


好了,这样我们的wcf宿主在控制台程序中的跨域问题就解决了,同样我们宿主在Winform和wpf程序中的@R_301_6954@案也是一样的。下面我们用同样的方法来测试宿主在windows 服务中。

4. wcf宿主在windows 服务中

什么是windows服务呢,简单的将就是长时间运行在你电脑上的程序,它可以在开始时自动运行,延迟运行,也可以被禁止运行,这样就可以为电脑上的其他运行程序长时间的提供数据提供服务。将我们的wcf服务宿主在windows服务上,这样我们的wcf在开机是变可以方便的启动,并且长时间运行在计算机上,是一种不错的宿主方式。

在我们的项目中WcfHostOnWindService即是一个windows服务项目,提供给wcf宿主的,那么怎么样将wcf服务宿主在windows服务上?怎么样注册我们的服务呢?下面则是具体的做法:

如图是WcfHostOnWindService项目的结构:


既然它为wcf提供宿主,则也需要配置我们的wcf服务端App.config这个配置和上面的控制台宿主配置一样,TestService是新建每个windows服务自带的一个类,我们查看代码发现提供的了OnStart和OnStop两个函数,自然明白了就是服务启动和关闭时执行的函数,这样我们很容易就想到了,我们要在windows服务启动时启动我们的wcf服务,关闭时关闭我们的wcf服务,代码:

    public partial class TestService : ServiceBase    {        public TestService()        {            InitializeComponent();        }        private ServiceHost host = null;        private ServiceHost crossDomainserviceHost = null;        protected overrIDe voID OnStart(string[] args)        {            host = new ServiceHost(typeof(WcfServiceDll.TestService));            host.open();            crossDomainserviceHost = new ServiceHost(typeof(WcfServiceDll.DomainService));            crossDomainserviceHost.open();        }        protected overrIDe voID OnStop()        {            host.Close();            crossDomainserviceHost.Close();            host = null;            crossDomainserviceHost = null;        }    }

这个我们的windows服务的启动逻辑写清楚了然后就是安装注册这个服务了:

一. 我们在TestService的设计界面右键选择“添加安装程序”如图:


来到我们的ProjectInstaller设计界面,设置serviceProcessInstaller1的Account属性为LocalSystem:


然后设置serviceInstaller1即我们的服务的一些属性,这里我们设置了服务的名称为TestService,描述为:测试wcf跨域

好了,到这步我们就可以安装注册我们的服务了,我们先生成这个windows服务项目WcfHostOnWindService,发现再bin/DeBUG中有了WcfHostOnWindService.exe,接下来就要用它来安装和注册服务,另外还需要一个需要用工具一起在dos中执行安装和卸载:

C:\windows\Microsoft.NET\Framework\v4.0.30319\InstallUtil.exe

安装命令:

1. 打开cmd窗口

2. 键入C:\windows\Microsoft.NET\Framework\v4.0.30319\InstallUtil.exe和服务路径(中间空格分隔)

则已我项目的命令为:C:\windows\Microsoft.NET\Framework\v4.0.30319\InstallUtil.exe E:\MyWorkSpace\WCFCrossDomain\WcfHostOnWindService\bin\DeBUG\WcfHostOnWindService.exe



运行结果:


查看服务安装成功:


卸载需要加上/u命令:

C:\windows\Microsoft.NET\Framework\v4.0.30319\InstallUtil.exe /u E:\MyWorkSpace\WCFCrossDomain\WcfHostOnWindService\bin\DeBUG\WcfHostOnWindService.exe

每次这样写命令很麻烦,则可以将这个两个命令写在批处理文件.bat文件:

安装并开启:

C:\windows\Microsoft.NET\Framework\v4.0.30319\InstallUtil.exe %cd%\WcfHostOnWindService.exe@net start TestService@echo ****************************************************************************@echo 注册成功@echo ****************************************************************************@pause

卸载:

C:\windows\Microsoft.NET\Framework\v4.0.30319\InstallUtil.exe /u %cd%\WcfHostOnWindService.exe@echo ****************************************************************************@echo 卸载成功@echo ****************************************************************************@pause

将这两个文件保存为.bat文件后与WcfHostOnWindService.exe一个目录即可方便安装卸载:(一下是卸载)


好了这个我们的windows服务就完成了,下面我们同样将我们的策略配置文件放到与WcfHostOnWindService.exe一个目录中,因为我的服务名称和服务地址都是一样的,不需要从新添加服务引用直接启动silverlight项目测试:

没有成功!!!!!!!!!!!!!至少我试了很多次都没有成功,这也是我记录的原因了,为什么没有成功呢,一样的方法在控制台成功在windows服务则失败了,唯一的可能就是windows服务程序找不到跨域策略配置文件clIEntaccesspolicy.xml,怎么办呢?

既然他自己找不到,那么我们就主动为它指定目录,修改我们的DomainService.cs,指定clIEntaccesspolicy.xml的路径:

        public System.ServiceModel.Channels.Message ProvIDePolicyfile()        {            string location = System.Reflection.Assembly.GetExecutingAssembly().Location;            location = location.Substring(0,location.LastIndexOf('\')) + "\ClIEntAccesspolicy.xml";            if (fileContent.Length == 0)            {                StreamReader fileStream = new StreamReader(location);                fileContent = fileStream.ReadToEnd();                fileStream.Close();            }            StringReader sr = new StringReader(fileContent);            XmlReader reader = XmlReader.Create(sr);            System.ServiceModel.Channels.Message result = Message.CreateMessage(MessageVersion.None,reader);            return result;        }

其实我们就是显式指定了程序的执行路径为clIEntaccesspolicy.xml的路径,当然我们也可以将clIEntaccesspolicy.xml放到其他路径,比如C:\bin\clIEntaccesspolicy.xml相应的StreamReader fileStream = new StreamReader("C:\bin\clIEntaccesspolicy.xml');即可,好了修改服务代码后我们需要卸载我们的宿主windows服务,从新生成后在注册开启后,并更新我们的silverlight的服务的引用再次测试成功:


经过以上测试windows 服务为宿主的wcf确实存在以上路径问题,至少我是遇到了,然后这样修改后成功了,如果有朋友有更好的解决方法欢迎指正!

源码下载:http://download.csdn.net/detail/dangercheng/6730461

总结

以上是内存溢出为你收集整理的WCF服务宿主及其跨域问题全部内容,希望文章能够帮你解决WCF服务宿主及其跨域问题所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/web/1016175.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-22
下一篇 2022-05-22

发表评论

登录后才能评论

评论列表(0条)

保存