开卷有益。
最近的这个项目搞的我苦笑不得,一个拉了3-4年的业务系统(哎,悲催的项目啊),其中用到了WCF服务,此服务是IIS宿主服务,主要为后续的Silverlight程序提供相应的业务服务。在Silverlight程序中用到了ArcGIS,用来加载地图。Silverlight程序通过引用本地的WCF服务获取到了相应的代理。在我的Silverlight程序中,为了后续的地图服务地址的灵活配置将地图服务的地址放到了承载Silverlight网站的Web.Config文件配置节中。基本情况就是这样,结果发布系统后部署到服务器上之后,产生的问题真是千奇百怪!
问题1:Silverlight程序无法加载。
这个问题最常见,部署的问题。我们部署的Web服务器是WIN2003系统,在IIS6下需要给Silverlight承载网站添加MIME类型以处理xap和xaml。需要添加的两个MIME类型为:
[.xaml application/xaml+xml]
[.xap application/x-Silverlight-app]
Silverlight程序中动态读取承载网站上的SVC服务文件。
在我们开发的项目中,Silverlight程序的承载网站和WCF服务的宿主相同,这样我们的SVC地址可以可以通过下述代码获取。
new Uri(Application.Current.Host.Source, "../GIMHostService.svc")
另外,在项目初期的时候,为了防止最终程序部署时WCF服务与业务系统的分离,实现了Silverlight动态加载WCF服务,此方法也是基于网上的一些资料整理完成:
Silverlight程序中WCF服务动态加载的单例实现:
1 ///
2 /// 多线程单例模式
3 ///
4 public class SingletonClient
5 {
6 private static volatile GIMHostServiceClient instance = null;
7 private static object lockHelper = new object();
8 private SingletonClient() { }
9 public static GIMHostServiceClient Instance
10 {
11 get
12 {
13 if (instance == null)
14 {
15 lock (lockHelper)
16 {
17 if (instance == null)
18 {
19
20 string serviceUri = new Uri(Application.Current.Host.Source, "../GIMHostService.svc").ToString();
21 if (string.IsNullOrEmpty(serviceUri)) return null;
22
23 object[] paras = new object[2];
24 //此处的Binding格式必须和WCF服务的HOST主机上的配置一致,否则会返回404No Found错误。
25 BasicHttpBinding binding = new BasicHttpBinding(BasicHttpSecurityMode.None);
26 binding.MaxBufferSize = int.MaxValue;
27 binding.MaxReceivedMessageSize = int.MaxValue;
28 EndpointAddress address = new EndpointAddress(new Uri(serviceUri, UriKind.Absolute));
29
30 paras[0] = binding;
31 paras[1] = address;
32
33 ConstructorInfo constructor = null;
34
35 try
36 {
37 Type[] types = new Type[2];
38 types[0] = typeof(System.ServiceModel.Channels.Binding);
39 types[1] = typeof(System.ServiceModel.EndpointAddress);
40
41 constructor = typeof(GIMHostServiceClient).GetConstructor(types);
42 }
43 catch (Exception)
44 {
45 return null;
46 }
47
48 if (constructor != null)
49 instance = (GIMHostServiceClient)constructor.Invoke(paras);
50 }
51 }
52 }
53 return instance;
54 }
55 }
56 }
WCF服务SVC文件配置:
<%&#64; ServiceHost Language&#61;"C#" Debug&#61;"true" Service&#61;"HN.GIM.WCFService.GIMService" %>
WCF服务承载网站的服务配置&#xff1a;
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name&#61;"HN.GIM.WCFService.GIMServiceBehavior">
<serviceMetadata httpGetEnabled&#61;"true" />
<serviceDebug includeExceptionDetailInFaults&#61;"false" />
behavior>
serviceBehaviors>
behaviors>
<services>
<service behaviorConfiguration&#61;"HN.GIM.WCFService.GIMServiceBehavior" name&#61;"HN.GIM.WCFService.GIMService">
<endpoint address&#61;"" binding&#61;"basicHttpBinding" contract&#61;"HN.GIM.WCFService.Contract">
endpoint>
<endpoint address&#61;"mex" binding&#61;"mexHttpBinding" contract&#61;"IMetadataExchange" />
service>
services>
system.serviceModel>
上述的各项设置基本就完成了我们的WCF服务的开发以及Silverlight的引用。
Silverlight程序访问ArcGIS地图服务地址产生跨域问题。
在这个项目开发部署的过程中&#xff0c;这个跨域问题最头疼&#xff0c;但最终还是解决掉了。过程中&#xff0c;被逼无奈都看英文博客了&#xff08;以前从来没看过&#xff0c;一直惧怕英文&#xff0c;没想到咱还能看懂...
好神奇&#xff01;&#xff09;&#xff0c;而最终解决的方法更是让我惊呼不已&#xff0c;不怪天&#xff0c;不怪地&#xff0c;只怪自己手贱&#xff01;害人害己啊...且听我慢慢道来&#xff1a;
我们的服务器部署在局域网内的某台机器上&#xff0c;IP为192.168.2.101&#xff0c;ArcGIS Server也安装在这台机器上&#xff0c;那么ArcGIS默认的端口为80端口&#xff0c;如果想要在外网中访问相应的ArcGIS服务&#xff0c;就得需要路由器上的端口映射。另外&#xff0c;我们的业务系统部署在了IIS上通过8003端口进行访问&#xff0c;那么我们在客户端通过8003端口可以访问业务系统&#xff0c;之后可通过业务系统查看相应的Silverlight程序&#xff0c;在查看Silverlight程序的过程中&#xff0c;会通过80端口去请求GIS服务数据&#xff0c;在这个过程中就产生了跨域操作。
我们的跨域问题是从ArcGIS服务地址产生的&#xff0c;错误描述为&#xff1a;
Unhandled Error in Silverlight Application A security exception occured while trying to connect to the REST endpoint. Make sure you have a cross domain policy file available at the root for your server that allows for requests from this application.
OK&#xff0c;根据这个提示错误消息&#xff0c;在网上找了一系列的资料&#xff0c;通过配置两个跨域策略文件到wwwroot目录下即可解决&#xff08;网上是这么说的&#xff0c;我也就这么做了&#xff09;。可是&#xff0c;问题依旧&#xff0c;莫非是ArcGIS地图服务发布的有问题&#xff1f;可惜我对ArcGIS地图服务的熟悉程度仅仅限于应用的层次&#xff0c;原理、底层什么乱七八糟的根本一窍不通。研究了一天的ArcGIS服务的相关东西&#xff0c;企图从ArcGIS服务上去找到解决方案的思路我认为是行不通了。
之后&#xff0c;我想到既然ArcGIS的访问会产生跨域问题&#xff0c;Silverlight程序如果访问不同端口下的WCF服务应该也会产生跨域的操作&#xff0c;会不会引发同样的问题呢&#xff1f;如果照样引发跨域问题&#xff0c;那么只能说明跨域策略文件没有按照正确的跨域策略执行&#xff01;果然&#xff0c;我将服务器上的WCF服务配置到另外一个端口下&#xff0c;用原来的业务系统去访问另外一个端口下的WCF服务&#xff0c;结果报告了错误&#xff1a;
尝试向 URI“http://localhost:8001/AccountService.svc”发出请求时出错。这可能是由于试图以跨域方式访问服务而又没有正确的跨域策略&#xff0c;或策略不适用于 SOAP 服务。您可能需要与该服务的所有者联系&#xff0c;以发布跨域策略文件并确保该文件允许发送 SOAP 相关的 HTTP 标头。出现此错误也可能是由于使用的是 Web 服务代理中的内部类型而没有使用 InternalsVisibleToAttribute 属性。有关详细信息&#xff0c;请参阅内部异常。
OK&#xff0c;毫无疑问&#xff0c;只能说明服务器端的跨域文件没有按照正确的跨域策略执行。之后利用Fiddler监听了ArcGIS官网提供的地图服务和服务器上的ArcGIS服务&#xff0c;通过HTTP请求的比对发现&#xff1a;他们两个都能够读取到跨域文件clientaccesspolicy.xml&#xff0c;但他们的content-type却不一样&#xff0c;官网上的为text/xml&#xff0c;而服务器上的为application/xaml&#43;xmlt&#xff0c;震惊了&#xff01;怎么可能&#xff1f;为什么&#xff1f;&#xff1f;&#xff1f;莫非......MIME文件添加错了&#xff1f;赶紧去服务器上去看&#xff0c;原来当初部署系统&#xff08;不是我部署的~~&#xff09;的时候添加.xaml的MIME类型时&#xff0c;给添加错了&#xff0c;写成了.xml...&#xff0c;最要命的是&#xff0c;这个类型加载了默认网站上&#xff0c;而没有添加到业务系统的虚拟目录上&#xff01;结果导致了跨域策略始终无法正确解析。修改过来之后&#xff0c;OK了&#xff01;
终于告一段落了&#xff0c;还不错&#xff0c;在解决问题的过程中了解了其他方面的一些知识点。也算是对自己的一个安慰吧&#xff01;