如何在一个跨域环境中用Silverlight客户端使用自托管的WCF服务

背景介绍:

当一个Silverlight客户端试图使用WCF服务时,默认情况下,它只被允许进行在原始站点下的通讯。例如,一个https://domainA/default.aspx托管的Silverlight控件默认情况下只能使用同一个域内的服务,如:https://domainA/WCFService.svc。而https://domainB/WCFservice.svc则不行。

为了使https://domainA/default.aspx托管的Silverlight可以连接到https://domainB/WCFservice托管的WCF服务,我们需要显式配置一个跨域选择策略,即clientaccesspolicy.xml或crossdomain.xml。有很多文章与这个主题相关:

建立可跨网域界限访问的服务

https://msdn.microsoft.com/en-us/library/cc197955(VS.95).aspx

警告:无法在'<URL>'定位跨域策略

https://msdn.microsoft.com/en-us/library/cc838225(VS.95).aspx

当我们的WCF服务托管在IIS中时,我们只需要在发布WCF服务的根文件夹中部署clientaccesspolicy.xml。例如:如果我们的WCF服务发布为:https://domainB/WCFservice.svc,那么跨域策略应该可以通过https://domainB/clientaccesspolicy.xml访问。

然而,自托管的WCF服务有些许不同。在这篇文章中,我们将演示Silverlight客户端如何在一个跨域环境中使用自托管的WCF服务。同时,因为Silverlight4 目前已支持NET.TCP绑定,这些针对NET.TCP绑定的步骤将与basicHttpBinding完全不同。在这篇文章中,我将分别介绍在basicHttpBinding和NET.TCP 绑定这两种不同情景下的步骤。

如何使用自托管WCF服务

场景1:自托管的WCF服务与basicHttpBinding

步骤1:

定义一个接口"IPolicyRetriever"

 [ServiceContract]

    public interface IPolicyRetriever

    {

        [OperationContract, WebGet(UriTemplate = "/clientaccesspolicy.xml")]

        Stream GetSilverlightPolicy();

        [OperationContract, WebGet(UriTemplate = "/crossdomain.xml")]

        Stream GetFlashPolicy();

}

步骤2:

在你的WCF服务中实现上述接口:

public class WCFService:IWCFService,IPolicyRetriever

    {

       //IWCFService 实现

                                ...

                                ...

//IPolicyRetriever 实现

      private Stream StringToStream(string result)

        {

            WebOperationContext.Current.OutgoingResponse.ContentType = "application/xml";

            return new MemoryStream(Encoding.UTF8.GetBytes(result));

        }

 

        public Stream GetSilverlightPolicy()

        {

            string result = @"<?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>";

            return StringToStream(result);

     }

步骤3:

发布一个endpoint,地址为"",基于HTTP schema的主机地址来为clientaccesspolicy.xml接收请求。我们可以通过编程或管理的方式达到此目的。

编程的方式:

Type serviceType = typeof(WCFService);

ServiceHost host = new ServiceHost(serviceType);

host.AddServiceEndpoint(typeof(IPolicyRetriever), new WebHttpBinding(), "").Behaviors.Add(new WebHttpBehavior());

管理的方式:

<behaviors>

    <endpointBehaviors>

       <behavior name="WebHttpNewBehavior">

          <webHttp />

            </behavior>

    </endpointBehaviors>

           ...

 </behaviors>

 <services>

       <service behaviorConfiguration="NewBehavior">

                       ...              

          <endpoint behaviorConfiguration="WebHttpNewBehavior" binding="webHttpBinding"

                    bindingConfiguration="" name="PolicyEndpoint" contract="WCFService.IPolicyRetriever" />

                ...

        </service>

 </services>

在以上步骤之后,当一个Seliverlight控件向跨域自托管的WCF 服务发出一个请求时,这个服务端的服务器应该能够检索它的客户端访问策略,然后决定请求是否被授权。

场景2:自托管的WCF服务与NET.TCP binding

即使如下文章宣布了Sliverlight4支持NET.TCP binding:

Sliverlight4 中的WCF NET.TCP协议

https://www.silverlightshow.net/items/WCF-NET.TCP-Protocol-in-Silverlight-4.aspx

在开始我们的步骤之前,我想特别指出一些与basicHttpBinding场景不同的地方。

1. 实际上NET.TCP schema并不能被Silverlight 4识别。至少当前如此。为了使用NET.TCP 绑定,我们需要使用customBinding。

2. TCP 端口范围被限制在4502-4534,这意味着你的WCF服务必须在以上端口范围中发布,从而使Silverlight客户端能够访问它。

为了使客户端能够访问策略,在Silverlight4 RC之前,TCP 943端口专门用来暴露策略。

参考如下文章以获得更多细节信息:

Silverlight中的网络安全访问限制

https://msdn.microsoft.com/en-us/library/cc645032(VS.95).aspx

为了使我们的工作变得简单,Microsoft的Tomasz Janczuk创建了一个免费的针对那些工作如TCP 套接字策略服务器的命令行程序的模板。

然而,Silverlight 4 RC中的NET.TCP协议希望能通过HTTP协议在域名的根目录中访问到套接字策略,而不是像之前那样通过TCP协议在943端口访问到。

参考如下文章以获悉这个改变:

Sliverlight 4 中使用WCF net.tcp 协议的发布/订阅例子

https://tomasz.janczuk.org/2009/11/pubsub-sample-with-wcf-nettcp-protocol.html

 在这里,我们展示针对于Silverlight 4 RC场景的步骤,这意味着套接字策略通过HTTP协议在域名的根目录下被访问到。

步骤1:

配置WCF 服务使用customBinding,和一个基于NET.TCP 模式的元数据交换端点。你可以参考如下配置:

<bindings>

        <customBinding>

          <binding name="NewBinding0">

            <binaryMessageEncoding />

            <tcpTransport />

          </binding>

        </customBinding>

 </bindings>

...

...

<services>

 <service behaviorConfiguration="NewBehavior" name="Demo.WCFService">

    <endpoint address="WCFService" binding="customBinding" bindingConfiguration="NewBinding0"

                    name="ServiceEndpoint" contract=" Demo.IWCFService " />

    <endpoint address="mex" binding="mexTcpBinding" bindingConfiguration=""

                    name="MEXEndpoint" contract="IMetadataExchange" />

    <host>

       <baseAddresses>

               <add baseAddress="net.tcp://[domainB]:4503/" />

        </baseAddresses>

    </host>

   </service>

 </services>

 

步骤2:

将clientaccesspolicy.xml部署到https://domainB的根目录下,以确保clientaccesspolicy.xml能够通过https://domainB/clientaccesspolicy.xml访问到。clientaccesspolicy.xml的内容应该与如下相似:

<access-policy>

  <cross-domain-access>

    <policy>

      <allow-from>

        <domain uri="*"/>

      </allow-from>

      <grant-to>

        <socket-resource port="4502-4534" protocol="tcp" />

      </grant-to>

    </policy>

  </cross-domain-access>

</access-policy>

 经过如上步骤后,你的Silverlight 应该能够成功地通过NET.TCP 绑定请求自托管的WCF 服务。

谢谢!

微软Internet开发者支持小组