开发WCF/Silverlight须知
发布:张逸 | 发布时间: 2009年3月22日ByteBlocks的博客文章中总结了开发WCF/Silverlight的注意事项,这样的经验之谈字字千钧,可以让后来的开发者少走许多弯路。
绑定的选择
毫无疑问,我们应该选择BasicHttpBinding,这也是Silverlight仅仅支持的一种绑定。
WCF异常的处理
Silverlight无法获取WCF异常(例如:FaultException)。如果WCF服务抛出WCF异常,在客户端应用程序只能够获得 HTTP 404错误。无疑,这会干扰调用者对异常的捕捉,同时也无法获知真正的异常信息。一种好的做法是在服务方法中定义一个out参数,在该参数中包含HTTP Status以及异常信息。可以定义一个返回信息的数据契约,例如:
public class CallResult
{
public CallResult()
{
StatusCode = 0;
StatusMessage = "OK";
}
[DataMember]
public int StatusCode
{get; set;}
[DataMember]
public string StatusMessage
{get; set;}
[DataMember]
public string ExceptionDetails
{get; set;}
}
编写服务方法时,可以采用如下方式:
{
status = new CallResult();
if (string.IsNullOrEmpty(from) ||
string.IsNullOrEmpty(to))
{
status.StatusCode = 2;
status.StatusMessage = "Invalid or empty curreny symbols specified";
return 0;
}
try
{
string config = ConfigurationManager.AppSettings["htmlparserconfig"];
var converter = new CurrencyConverter(config);
return converter.Convert(from, to);
}
catch (Exception ex)
{
status.StatusCode = 9;
status.StatusMessage = "Failed to get currency conversion rate";
status.ExceptionDetails = ex.Message;
//TODO: Log this message.
}
return 0;
}
WCF服务的部署
在将WCF服务部署在Web服务器上时,最好在配置文件中为WCF服务添加一个基地址。如果未来需要修改WCF服务的部署地址,仅仅需要修改配置文件的基地址即可,其余位置不需要做任何修改。
<baseAddresses>
<add baseAddress="http://www.myhostserver.com/MyWCFServices/"/>
</baseAddresses>
</host>
此外,至关重要的一点是要让服务跨域边界可用。Silverlight 在默认情况下只允许源站点通信。若要允许 Silverlight 控件访问其他域上的服务,该服务必须明确选择允许跨域访问。通过选择,服务声明它公开的操作可以由 Silverlight 控件安全地调用,而不会对该服务存储的数据造成具有潜在危害的结果。Silverlight 2.0 支持两种不同的机制供服务选择跨域访问:
(1)在承载服务的域的根目录中放置一个 clientaccesspolicy.xml 文件,以配置服务允许跨域访问。
(2)在承载服务的域的根目录中放置一个有效的 crossdomain.xml 文件。该文件必须将整个域标记为 public。
例如,创建如下的clientaccesspolicy.xml文件:
<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>
如果只允许从其他域中的一个进行访问(例如 http://agiledon.com),clientaccesspolicy.xml 应当包含以下配置:
<access-policy>
<cross-domain-access>
<policy>
<allow-from http-request-headers="*">
<domain uri="http://agiledon.com"/>
</allow-from>
<grant-to>
<resource path="/" include-subpaths="true"/>
</grant-to>
</policy>
</cross-domain-access>
</access-policy>
然后再将该文件保存到托管该服务的域的根目录中。例如,如果该服务在 http://agiledon.com 上承载,则文件必须位于 http://agiledon.com/clientaccesspolicy.xml。
如果进行如上的配置,在Silverlight跨域访问时,就会出现一个安全错误。
- 相关文章:
WCF中的Dispose (2009-3-12 11:32:19)
翻译Programming WCF Services第二版 (2009-3-9 16:27:49)
聚焦WCF行为的扩展 (2009-3-8 21:16:4)
WCF基础知识问与答 (2009-3-6 21:37:11)
在WCF中获取服务元数据信息 (2009-3-4 9:43:43)
WCF 3.5对HTTP编程的增强 (2009-3-3 22:51:19)
WCF宿主与服务托管 (2009-3-1 16:24:41)
WCF中的Stream操作 (2009-2-28 21:48:6)
WCF中的可信赖会话 (2009-2-27 12:38:1)
WCF中的操作 (2009-2-22 10:46:9)
- 1.dotAge
- 这对于SmartClient程序也一样,在.Net 2.0之下,我们好像也很难得到服务器端的FaultContract,导致WebMethod参数显得很丑陋,然而要让客户端升级到3.0以上,太费时费力了,都不好意思提出来麻烦网络管理员;.NET 3.5 Client Profile在安装了.Net 2.0的情况下竟然要全包230多M完全下载实在是令人很无奈。期望.Net4.0能够更智能一点,让客户端部署轻便一点,让我们程序员早日享受WCF的便捷和强大!
- 2009-3-23 9:58:26 回复该留言
- 2.张逸
- http://www.agiledon.com
- 我对.NET 4.0能解决这个问题不抱太大希望。
- 2009-3-23 10:57:03 回复该留言
- 3.guyazh
- 我的做法就是在服务器端捕获所有异常,并以一种特定的方式告诉客户端服务器出现什么异常了。总之,如果客户端调用服务器端服务,肯定有返回值,否则就是网络断掉或者服务器宕机了。
我个人觉得异常要想跨域跨进程跨机器传播是很可怕的,各个独立程序应该独自处理自己的异常。 - 2009-3-23 18:08:32 回复该留言
- 4.张逸
- http://www.agiledon.com
- 以out或ref方式返回异常信息的方式还是可行的,如楼上所说,此时就需要在服务端捕获所有异常。不过,out参数会破坏方法签名的优雅,而且也会给客户端调用带来不够直观的后果。在没有更好的解决办法之前,我宁愿将这种做法放在编码规范里,作为API编写与调用所必须遵循的标准。
- 2009-3-23 21:30:56 回复该留言
- 5.hbz
- http://huobazi.aspxboy.com
- 可不可以这样:
[DataContract]
public class FaltException
{
public FaltException()
{
StatusCode = 0;
StatusMessage = "OK";
}
[DataMember]
public int StatusCode
{get; set;}
[DataMember]
public string StatusMessage
{get; set;}
[DataMember]
public string ExceptionDetails
{get; set;}
}
[DataContract]
public class OpResult<T>
{
[DataMember]
T Result {get;set;}
[DataMember]
FaltException Ex{get;set;}
}
service
public OpResult<double> Convert(string from, string to){
OpResult<double> result = new OpResult<double>();
try{
result.Result = 3.00;
}
catch(Exception e){
FaltException ex = new FaltException();
ex...
ex...
result. FaltException = ex...
}
return result;
}
client:
OpResult<double> xx = yy.Convert(a,b);
if(xx.FaltException != null){
// exception...
} - 2009-3-24 10:11:23 回复该留言
- 6.张逸
- http://www.agiledon.com
- @hbz:
你这样的实现只是针对没有返回值的方法,所以可以通过返回特定的异常信息来完成,不够通用。而且本质上和用out参数并没有任何区别。hbz 于 2009-3-24 17:45:48 回复@楼上
OpResult<double>内是有返回值的.
个人觉得约定方法返回值包含 真实返回者和异常 比每个方法签名都带一个ref/out参数舒服些hbz 于 2009-3-24 17:50:15 回复OpResult<T>中的T就是方法返回值了.张逸 于 2009-3-25 14:09:35 回复恩,我没有看清楚,你这个方案完全可行,唯一的问题是,这对服务方法是有约束的。每个服务方法的方法签名中,返回值都必须是OpResult<T>。 - 2009-3-24 14:52:00 回复该留言
- 8.virus
- http://virusswb.cnblogs.com
- 我的需求是数据库的操作,客户端使用sl3,访问数据库使用wcf,客户端不要使用https,不要证书,客户端访问wcf需要验证,但是使用username和password就可以了,如何实现呢?
basicHttpBinding条件下,我配置了很多,自定义了验证类,可是没有运行,请帮我看看
不要证书,不要https,就用用户名密码,可以马
重写UserValidator,在basicHttpBinding的情况可以实现马?
msn:jorden008@hotmail.com
<serviceBehaviors>
<behavior name="WcfService.Service1Behavior">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="false"/>
<serviceCredentials >
<userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="WcfService.MyValidator, WcfService"/>
</serviceCredentials>
</behavior>
</serviceBehaviors>
这样配置,可是就没有运行到我的自定义验证啊,
public override void Validate(string userName, string password)
{
this._userName = userName;
this._password = password;
} - 2010-1-25 10:37:28 回复该留言
发表评论
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。






张逸(Bruce Zhang)
