zoukankan      html  css  js  c++  java
  • 面向服务的RIA应用系统开发中的异常处理

    概述:本文将介绍的话题与XML Web Service,WCF,SharePoint,Silverlight开发有关。具体来说,就是在SharePoint平台上,结合Silverlight(客户端技术)和XML Web Service 或者WCF(服务端技术)开发应用系统的一些常见问题,典型问题就是异常处理,以及大数据传输问题。总结起来,算是我个人的一点经验之谈,抛砖引玉,供大家参考参考

    这一篇,主要谈谈异常处理

    异常处理

    异常处理是重要的,而且相当重要。这是一个基础性设计问题, 我先讲一些通用的概念吧

    我自己做过,也看过很多系统,在这方面其实都做得不怎么理想,有两个典型表现:

    1. 没有做异常处理

    2. 没有做合适的异常处理

    没有做异常处理的情况,一个典型例子如下:

    image

    这是会吓到用户的,不是吗?看到这一堆红的,很专业的错误消息,用户会做何感想呢?

    好吧,你可能不在乎用户的感受,那么看看下面这个吧

    image

    不错,连接字符串(包括密码等)都一五一十地告诉用户了,我不知道你的老板看到这样的情况,会做如何感想?

    没有做合适的异常处理的情况,一个典型的例子如下

    image

    毫无疑问,你确实做了异常处理,你用了try…catch…不是吗?这是一个进步了

    但是其实你什么都没有做

    image

    这个画面自然没有之前那个那么可怕,但是对用户而言,却没有本质上的帮助。

    那么,一个较为合适的异常处理策略是怎么样的呢?简单而言,我觉得主要就是:

    1. 尽量精确地捕获异常,

    2. 根据异常类型,提供不同的响应。


    像上面这样的例子,如果我们能做类似如下的处理,可能会好不少

    image

    image

    这至少有了很大进步:你处理了异常,而且对异常有区别的对待,然后还提供了针对不同异常不同的响应。这是异常处理中的最基本的原则。

    但还有其他的原则。从架构上而言,我们不太推荐直接在客户端里面访问后台的数据库等资源的。上面的例子,页面(aspx)其实是客户端的概念,在极其简单的应用程序中,你确实可以像上面这样访问数据库,并且进行异常处理。

    但是,你不觉得这样做其实很复杂吗?每个页面都可以访问数据库,你每个页面里面都需要写很多重复的代码,这些代码很难维护的。

    在ASP.NET应用系统中,我们可以利用Global.asax中统一处理Error事件

    image

    并且同时使用CustomError的重定向功能,转到错误页面

    image

    关于这两个技术的使用,有兴趣的朋友,可以参考 这里 的介绍

    所以,接下来个原则就是:

    3. 尽量不要在客户端中直接地处理异常

    4. 你得有一个异常处理的策略,而不是仅仅知道对异常进行处理,而且真的那么辛苦一个一个去处理

    那么,在多层架构中,到底在哪一层处理异常比较好,并且如何把握处理异常的度呢?说实在话,不是很好掌握,这得靠你琢磨,体会,我是很难告诉你的

    是不是鸭梨很大呢?Nerd smile

    好吧,我给大家推荐一个资源,也是异常处理的一个比较好的框架:微软的企业库(Enterprise Library),里面有一个专门做异常处理的模块,相当不错

    你可以通过 http://entlib.codeplex.com/ 下载到所有你想要找的资源,例如源代码,范例,动手实验等等

    博客园的Terry以前写过这方面的文章可以参考一下,虽然他当时写的时候,版本还比较低

    http://www.cnblogs.com/Terrylee/archive/2005/11/14/275659.html 

    http://www.cnblogs.com/Terrylee/archive/2005/11/16/277557.html

    好了,关于异常处理,我们该做的铺垫,都做了。如果你已经开始意识到异常处理不是那么简单和容易的事情,那么我的目的已经基本达到了。Hot smile

    下面要来谈一谈你还不了解的秘密:

    在Silverlight中访问WCF或者XML Web Service时的异常处理

    我们先来看一下XML Web Service吧,我知道在一些比较有历史的应用系统中,大量使用了XML Web Service。它也确实很容易使用,至少写个Helloworld是这样。我们就来看一个这样的例子吧

    image

    【备注】这可不是一个好惹的“Helloworld”,它随时可以出错Broken heart。这没有什么好奇怪的,世事难料,谁能知道你后台的服务会不会出错呢?

    接下来,很多同学已经利用刚才学到的知识,在编写客户端代码的时候,注意了异常处理。

    在Silverlight中,你不会用TRY…CATCH, 因为Silverlight都是异步调用服务的,它提供了一个异步模型,如下面这样

    image

    wow,看起来不错,有这个xxxxxCompleted事件(这是在添加服务引用时自动生成的),一切看起来都是比较舒服的。

    代码逻辑很清晰,如果出错,则显示错误消息,否则显示正常结果。但是实际运行起来,情况却有点不同。

    我们发现,如果出错的话,会提供这样一个错误消息。NotFound。这是怎么回事?

    image

    谢天谢地,我们还有IE自带的一个开发工具,可以了解一下到底后台发生了什么事情

    image

    这里倒确实可以看到服务端返回的错误消息了

    image

    但是,你不可能要求用户自己去这样看错误消息吧?这未免难度太高了点儿。

    而且,问题是为什么服务器明明返回了错误消息,但客户端(准确地说,是Silverlight客户端)却收不到呢?

    这里原因就在于Silverlight的特有安全模型,我们都知道,Silverlight是运行在浏览器内部的,它的安全模型有时候也成为沙箱(sandbox),简单地说,它只有一定的权限。他只能做浏览器允许它做的事情。

    本例而言,既然服务器返回了500这个状态码,而大家要知道,500这个状态码的意思是Internal Server Error。浏览器会认为说,这个错误与应用程序无关,而不会将详细的信息传递给Silverlight运行时。

    那么,有没有办法改变这个行为,毕竟我们最关心的是如何解决这个问题。

    说起来也不是很难,我们可以在Silverlight应用程序启动的时候,通知宿主的浏览器,我们需要处理这一类的错误. 通过WebRequest.RegisterPrefix来实现这样的需求。

    WebRequest.RegisterPrefix("http://", WebRequestCreator.ClientHttp)

    再次运行的话,我们看到的错误消息就是下面这样的

    image

    这个至少能从一定程度上解决我们的问题:我们拿到了真正的异常消息,而不是一个谁都看不懂的NotFound错误

    准确地说,这是跟Silverlight有关,而与服务端无关,服务端确实返回了消息,只不过客户端解析的机制导致了之前的问题。

    这个问题在WCF很类似,但也有些不同的特点。下面是一个例子

    image

    如果不注册WebRequest.RegisterPrefix("http://", WebRequestCreator.ClientHttp)

    则异常同样是NotFound

    image

    但如果注册了,则会遇到下面的情况

    image

    这是什么意思呢?因为默认情况下,WCF是不会向客户端发送异常的详细消息的。这个可以通过在服务端进行必要的配置来启用

    image

    启用了这个开关后,再运行之后,就可以看到真正的错误消息了

    image

    通过如上的讲解,大家应该能清楚地看到在Silverlight中访问服务时,异常处理的一些特殊性,而且我们也提供了解决方案。

    高级部分

    实际上,除了在Silverlight中修改http处理的策略(默认是浏览器处理,我们可以修改为应用程序自己处理)之外,我们可能更倾向于在服务端做一些改进。关于这一点,在XML Web Service这个层面上,很难再做改善。所以,我们现在一般在选择架构的时候,都优先考虑WCF作为服务架构,因为它具有更好的扩展性。

    我们来想想服务器改善是什么样的一个思路:既然客户端无法显示那个真正的错误消息,是由于服务器返回了500这个状态码的回复,那么能不能修改这个状态码,强制修改为200呢?

    在WCF中,你可以做到这一点,通过EndPointBehavior来扩展。下面就是一个例子

    // This is an auto-generated file to enable WCF faults to reach Silverlight clients.
    
    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Linq;
    using System.Runtime.Serialization;
    using System.Net;
    using System.ServiceModel;
    using System.ServiceModel.Channels;
    using System.ServiceModel.Description;
    using System.ServiceModel.Dispatcher;
    
    namespace SilverlightApplicationSample.Web
    {
        public class SilverlightFaultBehavior : Attribute, IServiceBehavior
        {
            private class SilverlightFaultEndpointBehavior : IEndpointBehavior
            {
                public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
                {
                }
    
                public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
                {
                }
    
                public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
                {
                    endpointDispatcher.DispatchRuntime.MessageInspectors.Add(new SilverlightFaultMessageInspector());
                }
    
                public void Validate(ServiceEndpoint endpoint)
                {
                }
    
                private class SilverlightFaultMessageInspector : IDispatchMessageInspector
                {
                    public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
                    {
                        return null;
                    }
    
                    public void BeforeSendReply(ref Message reply, object correlationState)
                    {
                        if((reply != null) && reply.IsFault)
                        {
                            HttpResponseMessageProperty property = new HttpResponseMessageProperty();
                            property.StatusCode = HttpStatusCode.OK;
                            reply.Properties[HttpResponseMessageProperty.Name] = property;
                        }
                    }
                }
            }
    
            public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
            {
            }
    
            public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
            {
                foreach(ServiceEndpoint endpoint in serviceDescription.Endpoints)
                {
                    endpoint.Behaviors.Add(new SilverlightFaultEndpointBehavior());
                }
            }
    
            public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
            {
            }
        }
    }
    

    本例中,我们实现了一个特殊的EndPointBehavior,还有一个特殊的ServiceBehavior。

    如何使用他们呢?很简单,像下面这样就可以了

    using System;
    using System.Linq;
    using System.Runtime.Serialization;
    using System.ServiceModel;
    using System.ServiceModel.Activation;
    
    namespace SilverlightApplicationSample.Web
    {
        [ServiceContract(Namespace = "")]
        [SilverlightFaultBehavior]
        [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
        public class HelloSilverlighWCFService
        {
            [OperationContract]
            public void DoWork()
            {
                // Add your operation implementation here
                return;
            }
    
            // Add more operations here and mark them with [OperationContract]
        }
    }
    
     

    这样的话,就能让客户端受到异常消息了。

    呵呵,有的朋友已经发现了吧,这个类型不是我编写的,其实Visual Studio自带了这样一个模板,你可以很容易生成那个类型

    image

    理解WCF的扩展机制,对于大家用好WCF将极其重要,有兴趣的朋友可以参考

    http://msdn.microsoft.com/en-us/library/gg132853.aspx

    关于在SharePoint中使用WCF,有很多特殊性,也会遇到一些问题,请参考下面的文章

    http://msdn.microsoft.com/en-us/library/ff521581.aspx

    总结

    本文主要讲解了XML Web Service和WCF中异常处理,以及一些扩展机制

  • 相关阅读:
    HDU 1106 排序
    strtok函数()
    HDU 2187汶川地震
    HDU 1716 排列2
    Rightmost Digit
    Text Reverse
    快速幂
    插入排序的一个应用-调整负数在前,正数在后,原来相对位置不变
    cuda 5.0配置vs2008+Visual Assist X +安装问题解决
    vc 热键、组合键的用法
  • 原文地址:https://www.cnblogs.com/chenxizhang/p/2221009.html
Copyright © 2011-2022 走看看