WCF服务以控制台程序为宿主时的跨域访问问题(附源码)

前几天研究SilverLight时碰到了这个问题,本以为很轻松的试验结果很不顺利,查了很多资料才解决了这个问题,在此把解决问题的方法写出来,也方便其它朋友借鉴。

      问题是这样产生了,我在测试SilverLight时为了测试与WCF的通讯,创建了一个控制台的应用程序做为WCF服务的宿主程序。

     同时创建了一个SilverLight应用程序和一个承载SilverLight的Web程序.

     解决方案如图:

   

 

   控制台程序:

   1.添加 WCF服务

     命名为WCFService.cs,并添加服务,代码如下:

namespace WCFBase
{
    // 注意: 如果更改此处的类名 "WCFService",也必须更新 App.config 中对 "WCFService" 的引用。
    public class WCFService : IWCFService
    {
        public int DoWork(int a,int b)
        {
            return a + b;
        }

        public int DoWork2(int a, int b)
        {
            return a - b;
        }
    }
}

系统会自动添加App.config文件和相关配置节信息

内容如下:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <system.serviceModel>
      <serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>
        <behaviors>
            <serviceBehaviors>
                <behavior name="WCFBase.WCFServiceBehavior">
                    <serviceMetadata httpGetEnabled="true" />
                    <serviceDebug includeExceptionDetailInFaults="false" />
                </behavior>
            </serviceBehaviors>
         </behaviors>
      
        <services>
            <service behaviorConfiguration="WCFBase.WCFServiceBehavior" name="WCFBase.WCFService">
                <endpoint address="" binding="basicHttpBinding" contract="WCFBase.IWCFService">
                    <identity>
                        <dns value="localhost" />
                    </identity>
                </endpoint>
                <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
                <host>
                    <baseAddresses>
                        <add baseAddress="http://localhost:9090/WCFService/" />
                    </baseAddresses>
                </host>
            </service>
            </services>
     
    </system.serviceModel>
 
</configuration>

2.加载WCF服务

 

打开主程序入口Main方法,代码如下

namespace WCFBase
{
    class Program
    {
        static void Main(string[] args)
        {
            ServiceHost host = new ServiceHost(typeof(WCFService));
            host.Open();

            Console.WriteLine("I'am Here.");
            Console.ReadKey();
            host.Close();


        }
    }
}

至此运行该控制台程序,WCF服务启可使用。

但当垮域访问时会运行错误,提示没有仅限操作。这时需要如下操作

3.使WCF可以垮域访问

   A.在控制台程序根目添加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中若没有<socket-resource port="4502-4530" protocol="tcp" />则会出现异常

详细内容为:

未能连接到 net.tcp://localhost:4503/Service。连接尝试的持续时间为 00:00:00.3593750。TCP 错误代码 10013: 试图以其访问权限所禁止的方式访问套接字。。原因可能是,试图以跨域的方式访问某服务,而该服务的配置不支持跨域访问。您可能需要与服务的所有者联系,以公开通过 HTTP 的套接字跨域策略,并在允许的套接字端口范围 4502-4534 之内承载该服务。

所以完整的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"/>
        <socket-resource port="4502-4530" protocol="tcp" />
      </grant-to>
    </policy>
  </cross-domain-access>
</access-policy>

 

 

   B.添加接口 IDomainService (名称可自定义)

 

 首先要添加System.ServiceModel.Web 引用

代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml;
using System.ServiceModel;
using System.ServiceModel.Channels;

namespace WCFBase
{
    /**/
    /// <summary>
    /// 跨域访问策略服务接口
    /// 注意:如果你更新了ICrossDomainService接口,需要同时更新App.Config
    /// </summary>
    [ServiceContract]
    public interface IDomainService
    {
        [OperationContract]
        [WebGet(UriTemplate = "ClientAccessPolicy.xml")]
        Message ProvidePolicyFile();
    }
}

C.实现接口IDomainService

类:DomainService(可自定义)

代码如下:

    

namespace WCFBase
{
    public class DomainService:IDomainService
    {
        #region IDomainService 成员

        public System.ServiceModel.Channels.Message ProvidePolicyFile()
        {
             FileStream filestream = File.Open(@"ClientAccessPolicy.xml", FileMode.Open);
            // Either specify ClientAcessPolicy.xml file path properly
            // or put that in /Bin folder of the console application
            XmlReader reader = XmlReader.Create(filestream);
            System.ServiceModel.Channels.Message result = Message.CreateMessage(MessageVersion.None, "", reader);
           return result;
        }

        //CrossDomainServiceBehavior

        #endregion
    }
}

D.修改App.config文件

修改后为

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <system.serviceModel>
      <serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>
        <behaviors>
            <serviceBehaviors>
                <behavior name="WCFBase.WCFServiceBehavior">
                    <serviceMetadata httpGetEnabled="true" />
                    <serviceDebug includeExceptionDetailInFaults="false" />
                </behavior>
            </serviceBehaviors>
          <endpointBehaviors>
            <behavior name="DomainServiceBehavior">
              <webHttp/>
            </behavior>
          </endpointBehaviors>
        </behaviors>
      

      
        <services>
            <service behaviorConfiguration="WCFBase.WCFServiceBehavior" name="WCFBase.WCFService">
                <endpoint address="" binding="basicHttpBinding" contract="WCFBase.IWCFService">
                    <identity>
                        <dns value="localhost" />
                    </identity>
                </endpoint>
                <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
                <host>
                    <baseAddresses>
                        <add baseAddress="http://localhost:9090/WCFService/" />
                    </baseAddresses>
                </host>
            </service>
            <service name="WCFBase.DomainService">
                <endpoint address="" behaviorConfiguration="DomainServiceBehavior"
                    binding="webHttpBinding" contract="WCFBase.IDomainService" />
                <host>
                    <baseAddresses>
                        <add baseAddress="
http://localhost:9090/" />
                    </baseAddresses>
                </host>
            </service>
        </services>
     
    </system.serviceModel>
 
</configuration>

灰色区域为新添加的信息,还没有完成,继续...

E.修改控制台打开主程序入口Main方法

代码如下:

namespace WCFBase
{
    class Program
    {
        static void Main(string[] args)
        {
            ServiceHost host = new ServiceHost(typeof(WCFService));
            host.Open();

            ServiceHost crossDomainserviceHost = new ServiceHost(typeof(DomainService));
            crossDomainserviceHost.Open();

            Console.WriteLine("I'am Here.");
            Console.ReadKey();
            host.Close();


        }
    }
}

灰色区域为新添加代码

最终程序结构如下:

到此WCF服务即可实现垮域访问.

注意:需要将clientaccesspolicy.xml文件复制到 WCFBase.exe相同目录下。

4.SilverLight与WCF通讯测试

 运行控制台程序 WCFBase.exe

如图:

  在SilverLight程序中添加"服务引用",地址为http://localhost:9090/WCFService/

  SilverLight代码如下

private void btnTest_Click(object sender, RoutedEventArgs e)
        {
           ServiceReference1.WCFServiceClient wcf = new SilverlightApp.ServiceReference1.WCFServiceClient();
           wcf.DoWorkCompleted += new EventHandler<SilverlightApp.ServiceReference1.DoWorkCompletedEventArgs>(wcf_DoWorkCompleted);

           
            wcf.DoWorkAsync(1, 2);
        }

        void wcf_DoWorkCompleted(object sender, SilverlightApp.ServiceReference1.DoWorkCompletedEventArgs e)
        {
            MessageBox.Show(e.Result.ToString());
        }

   测试截图:

证明SilverLight可以正常调用WCF的DoWork方法,并正确返回结果。

源代码下载:

http://download.csdn.net/source/2014616

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值