无需写try/catch,也能正常处理异常

对于企业应用的开发者来说,异常处理是一件既简单又复杂的事情。说其简单,是因为相关的编程无外乎try/catch/finally+throw而已;说其复杂,是因为我们往往很难按照我们真正需要的策略来处理异常。我一直有这样的想法,理想的企业应用开发中应该尽量让框架来完成对异常的处理,最终的开发人员在大部分的情况下无需编写异常处理相关的任何代码。在这篇文章中我们将提供一个解决方案来让ASP.NET应用利用EntLib的异常处理模块来实现自动化的异常处理。

源代码: 
Sample1[通过重写Page的OnLoad和OnRaisePostBackEvent方法] 
Sample2[通过自动封装注册的EventHandler]

一、EntLib的异常处理方式 
二、实例演示 
三、通过重写Page的OnLoad和RaisePostBackEvent方法实现自动异常处理 
四、IPostBackDataHandler 
五、EventHandlerWraper 
六、对控件注册事件的自动封装 
七、AlertHandler

一、EntLib的异常处理方式

所谓异常,其本意就是超出预期的错误。既然如此,异常处理的策略就不可能一成不变,我们不可能在开发阶段就制定一个完备的异常处理策略来处理未来发生的所有异常。异常处理策略应该是可配置的,能够随时进行动态改变的。就此而言,微软的企业库(以下简称EntLib)的异常处理应用块(Exception Handling Application Block)是一个不错的异常处理框架,它运行我们通过配置文件来定义针对具体异常类型的处理策略。

针对EntLib的异常处理应用块采用非常简单的编程方式,我们只需要按照如下的方式捕捉抛出的异常,并通过调用ExceptionPolicy的HandleException根据指定的异常策略进行处理即可。对于ASP.NET应用来说,我们可以注册HttpApplication的Error事件的形式来进行统一的异常处理。但是在很多情况下,我们往往需要将异常控制在当前页面之内(比如当前页面被正常呈现,并通过执行一段JavaScript探出一个对话框显示错误消息),我们往往需要将下面这段相同的代码结构置于所有控件的注册事件之中。

   1: try
   2: {
   3:     //业务代码
   4: }
   5: catch(Exception ex)
   6: {
   7:     if(ExceptionPolicy.HandleException(ex,"exceptionPolcyName"))
   8:     {
   9:         throw;
  10:     }
  11: }

我个人不太能够容忍完全相同的代码到处出现,代码应该尽可能地重用,而不是重复。接下来我们就来讨论如何采用一些编程上的手段或者技巧来让开发人员无须编写任何的异常处理代码,而抛出的确却能按照我们预先指定的策略被处理。

二、实例演示

为了让读者对“自动化异常处理”有一个直观的认识,我们来做一个简单的实例演示。我们的异常处理策略很简单:如果后台代码抛出异常,异常的相关信息按照预定义的格式通过Alert的方式显示在当前页面中。如下所示的是异常处理策略在配置文件中的定义,该配置中定义了唯一个名为“default”的异常策略,该策略利用自定义的AlertHandler来显示异常信息。配置属性messageTemplate定义了一个模板用于控制显示消息的格式。

   1: <configuration>
   2:   ...
   3:   <exceptionHandling>
   4:     <exceptionPolicies>
   5:       <add name="default">
   6:         <exceptionTypes>
   7:           <add type="System.Exception, mscorlib" 
   8:                   postHandlingAction="None" name="Exception">
   9:             <exceptionHandlers>
  10:               <add name="Alert Handler" type="AutomaticExceptionHandling.AlertHandler, AutomaticExceptionHandling" 
  11:                    messageTemplate="[{ExceptionType}]{Message}"/>
  12:             </exceptionHandlers>
  13:           </add>          
  14:         </exceptionTypes>
  15:       </add>
  16:     </exceptionPolicies>
  17:   </exceptionHandling>     
  18: </configuration>

现在我们定义一个简单的页面来模式自动化异常处理,这个页面是一个用于进行除法预算的计算器。如下所示的该页面的后台代码,可以看出它没有直接继承自Page,而是继承自我们自定义的基类PageBase,所有异常处理的机制就实现在此。Page_Load方法收集以QueryString方式提供的操作数,并转化成整数进行除法预算,最后将运算结果显示在表示结果的文本框中。计算按钮的Click事件处理方法根据用户输入的操作数进行除法运算。两个方法中均没有一句与异常处理相关的代码。

   1: public partial class Default : PageBase
   2: {
   3:     protected void Page_Load(object sender, EventArgs e)
   4:     {
   5:         if (!this.IsPostBack)
   6:         {
   7:             string op1 = Request.QueryString["op1"];
   8:             string op2 = Request.QueryString["op2"];
   9:             if (!string.IsNullOrEmpty(op1) && !string.IsNullOrEmpty(op2))
  10:             {
  11:                 this.txtResult.Text = (int.Parse(op1) / int.Parse(op2)).ToString();
  12:             }
  13:         }
  14:     }
  15:  
  16:     protected void btnCal_Click(object sender, EventArgs e)
  17:     {
  18:         int op1 = int.Parse(this.txtOp1.Text);
  19:         int op2 = int.Parse(this.txtOp2.Text);
  20:         this.txtResult.Text = (op1 / op2).ToString();
  21:     }
  22: }

现在运行我们程序,可以想象如果在表示操作数的文本框中输入一个非整数字符,调用Int32的Parse方法时将会抛出一个FormatException异常,或者将被除数设置为0,则会抛出一个DivideByZeroException异常。如下面的代码片断所示,在这两种情况下相应的错误信息按照我们预定义的格式以Alert的形式显示出来。

 

三、通过重写Page的OnLoad和RaisePostBackEvent方法实现自动异常处理

我们知道ASP.NET应用中某个页面的后台代码基本上都是注册到页面及其控件的事件处理方法,除了第一次呈现页面的Load事件,其他事件均是通过PostBack的方式出发的。所以我最初的解决方案很直接:就是提供一个PageBase,在重写的OnLoad和RaisePostBackEvent方法中进行异常处理。PageBase的整个定义如下所示:

   1: public abstract class PageBase: Page
   2: {
   3:     public virtual string ExceptionPolicyName { get; set; }
   4:     public PageBase()
   5:     {
   6:         this.ExceptionPolicyName = "default";
   7:     }
   8:  
   9:     protected virtual string GetExceptionPolicyName()
  10:     {
  11:         ExceptionPolicyAttribute attribute = this.GetType().GetCustomAttributes(true)
  12:             .OfType<ExceptionPolicyAttribute>().FirstOrDefault();
  13:         if (null != attribute)
  14:         {
  15:             return attribute.ExceptionPolicyName;
  16:         }
  17:         else
  18:         {
  19:             return this.ExceptionPolicyName;
  20:         }
  21:     }
  22:  
  23:     protected override void OnLoad(EventArgs e)
  24:     {
  25:         this.InvokeAndHandleException(() => base.OnLoad(e));
  26:     }
  27:  
  28:     protected override void RaisePostBackEvent(IPostBackEventHandler sourceControl, string eventArgument)
  29:     {
  30:         this.InvokeAndHandleException(()=>base.RaisePostBackEvent(sourceControl, eventArgument));
  31:     }
  32:  
  33:     private void InvokeAndHandleException(Action action)
  34:     {
  35:         try
  36:         {
  37:             action();
  38:         }
  39:         catch (Exception ex)
  40:         {
  41:             string exceptionPolicyName = this.GetExceptionPolicyName();
  42:             if (ExceptionPolicy.HandleException(ex, exceptionPolicyName))
  43:             {
  44:                 throw;
  45:             }
  46:         }
  47:     }
  48: }

如上面的代码片断所示,在重写的OnLoad和RaisePostBackEvent方法中,我们采用与EntLib异常处理应用块的编程方式调用基类的同名方法。我们通过属性ExceptionPolicyName 指定了一个默认的异常处理策略名称(“default”,也正是配置文件中定义个策略名称)。如果某个页面需要采用其他的异常处理策略,可以在类型上面应用ExceptionPolicyAttribute特性来制定,该特性定义如下:

   1: [AttributeUsage( AttributeTargets.Class, AllowMultiple = false)]
   2: public class ExceptionPolicyAttribute: Attribute
   3: {
   4:     public string ExceptionPolicyName { get; private set; }
   5:     public ExceptionPolicyAttribute(string exceptionPolicyName)
   6:     {
   7:         Guard.ArgumentNotNullOrEmpty(exceptionPolicyName, "exceptionPolicyName");
   8:         this.ExceptionPolicyName = exceptionPolicyName;
   9:     }
  10: }

四、IPostBackDataHandler

通过为具体Page定义基类并重写OnLoad和RaisePostBackEvent方法的方式貌似能够实现我们“自动化异常处理”的目标,而且针对我们提供的这个实例来说也是OK的。但是这却不是正确的解决方案,原因在于并非所有控件的事件都是在RaisePostBackEvent方法执行过程中触发的。ASP.NET提供了一组实现了IPostBackDataHandler接口的控件类型,它们会向PostBack的时候向服务端传递相应的数据,我们熟悉的ListControl(DropDownList、ListBox、RadioButtonList和CheckBoxList等)就属于此类。

   1: public interface IPostBackDataHandler
   2: {
   3:     bool LoadPostData(string postDataKey, NameValueCollection postCollection);
   4:     void RaisePostDataChangedEvent();
   5: }

当Page的ProcessRequest(这是对IHttpHandler方法的实现)被执行的的时候,会先于RaisePostBackEvent之前调用另一个方法RaiseChangedEvents。在RaiseChangedEvents方法执行过程中,如果目标类型实现了IPostBackDataHandler接口,会调用它们的RaisePostDataChangedEvent方法。很多表示输入数据改变的事件(比如ListControl的SelectedIndexChanged事件)就是被RaisePostDataChangedEvent方法触发的。如果可能,我们可以通过重写RaiseChangedEvents方法的方式来解决这个问题,不过很可惜,这个方法是一个内部方法。

五、EventHandlerWraper

要实现“自动化异常处理”的根本手段就是将页面和控件注册的事件处理方法置于一个try/catch块中执行,并采用EntLib的异常处理应用块的方式对抛出的异常进行处理。如果我们能够改变页面和控件注册的事件,使注册的事件处理器本身就具有异常处理的能力,我们“自动化异常处理”的目标也能够实现。为此我定义了如下一个用于封装EventHandler的EventHandlerWrapper,它将EventHandler的置于一个try/catch块中执行。对于EventHandlerWrapper的设计思想,在我两年前写的《如何编写没有Try/Catch的程序》一文中具有详细介绍。

   1: public class EventHandlerWrapper
   2: {
   3:     public object Target { get; private set; }
   4:     public MethodInfo Method { get; private set; }
   5:     public EventHandler Hander { get; private set; }
   6:     public string ExceptionPolicyName { get; private set; }
   7:  
   8:     public EventHandlerWrapper(EventHandler eventHandler, string exceptionPolicyName)
   9:     {
  10:         Guard.ArgumentNotNull(eventHandler, "eventHandler");
  11:         Guard.ArgumentNotNullOrEmpty(exceptionPolicyName, "exceptionPolicyName");
  12:  
  13:         this.Target = eventHandler.Target;
  14:         this.Method = eventHandler.Method;
  15:         this.ExceptionPolicyName = exceptionPolicyName;
  16:         this.Hander += Invoke;
  17:     }
  18:     public static implicit operator EventHandler(EventHandlerWrapper eventHandlerWrapper)
  19:     {
  20:         Guard.ArgumentNotNull(eventHandlerWrapper, "eventHandlerWrapper");
  21:         return eventHandlerWrapper.Hander;
  22:     }
  23:     private void Invoke(object sender, EventArgs args)
  24:     {
  25:         try
  26:         {
  27:             this.Method.Invoke(this.Target, new object[] { sender, args });
  28:         }
  29:         catch (TargetInvocationException ex)
  30:         {
  31:             if (ExceptionPolicy.HandleException(ex.InnerException, this.ExceptionPolicyName))
  32:             {
  33:                 throw;
  34:             }
  35:         }
  36:     }
  37: }

由于我们为EventHandlerWrapper定义了一个针对EventHandler的隐式转化符,一个EventHandlerWrapper对象能够自动被转化成EventHandler对象。我们现在的目标就是:将包括页面在内的所有控件注册的EventHandler替换成用于封装它们的EventHandlerWrapper。我们知道所有控件的基类Control具有如下一个受保护的只读属性Events,所有注册的EventHandler就包含在这里,而我们的目标就是要改变所有控件该属性中保存的EventHandler。

   1: public class Control
   2: {
   3:     protected EventHandlerList Events{get;}
   4: }

其实要改变Events属性中的EventHandler也并不是一件容易的事,因为其类型EventHandlerList 并不如它的名称表现出来的那样是一个可枚举的列表,而是一个通过私有类型ListEntry维护的链表。要改变这些注册的事件,我们不得不采用反射,而这会影响性能。不过对应并非访问量不高的企业应用来说,我觉得这点性能损失是可以接受的。整个操作被定义在如下所示的EventHandlerWrapperUtil的Wrap方法中。

   1: private static class EventHandlerWrapperUtil
   2: {
   3:     private static Type listEntryType;
   4:     private static FieldInfo handler;
   5:     private static FieldInfo key;
   6:     private static FieldInfo next;
   7:  
   8:     static EventHandlerWrapperUtil()
   9:     {
  10:         listEntryType = Type.GetType("System.ComponentModel.EventHandlerList+ListEntry, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089");
  11:         BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.NonPublic;
  12:         handler = listEntryType.GetField("handler", bindingFlags);
  13:         key     = listEntryType.GetField("key", bindingFlags);
  14:         next    = listEntryType.GetField("next", bindingFlags);
  15:     }
  16:  
  17:     public static void Wrap(object listEntry, string exceptionPolicyName)
  18:     {
  19:         EventHandler eventHandler = handler.GetValue(listEntry) as EventHandler;
  20:         if (null != eventHandler)
  21:         {
  22:             EventHandlerWrapper eventHandlerWrapper = new EventHandlerWrapper(eventHandler, exceptionPolicyName);
  23:             handler.SetValue(listEntry, (EventHandler)eventHandlerWrapper);
  24:         }
  25:             object nextEntry = next.GetValue(listEntry);
  26:         if(null != nextEntry)
  27:         {
  28:             Wrap(nextEntry,exceptionPolicyName);
  29:         }
  30:     }            
  31: }

六、对控件注册事件的自动封装

对包括页面在内的所有控件注册时间的自动封装同样实现在作为具体页面积累的PageBase中。具体的实现定义在WrapEventHandlers方法中,由于Control的Events属性是受保护的,所以我们还得采用反射。该方法最终的重写的OnInit方法中执行。此外,由于EventHandlerWraper仅仅能够封装EventHandler,但是很多控件的事件却并非EventHandler类型,所以这是一个挺难解决的问题。

   1: public abstract class PageBase : Page
   2: {
   3:     private static PropertyInfo eventsProperty;
   4:     private static FieldInfo headField;
   5:  
   6:     public static string ExceptionPolicyName { get; set; }
   7:     static PageBase()
   8:     {
   9:         ExceptionPolicyName = "default";
  10:         eventsProperty = typeof(Control).GetProperty("Events", BindingFlags.Instance | BindingFlags.NonPublic);
  11:         headField = typeof(EventHandlerList).GetField("head", BindingFlags.Instance | BindingFlags.NonPublic);
  12:     }
  13:  
  14:     protected override void OnInit(EventArgs e)
  15:     {
  16:         base.OnInit(e);
  17:         Trace.Write("Begin to wrap events!");
  18:         this.WrapEventHandlers(this);
  19:         Trace.Write("Wrapping events ends!");
  20:     }
  21:  
  22:     protected virtual void WrapEventHandlers(Control control)
  23:     {
  24:         string exceptionPolicyName = this.GetExceptionPolicyName();
  25:         EventHandlerList events = eventsProperty.GetValue(control, null) as EventHandlerList;
  26:         if (null != events)
  27:         {
  28:             object head = headField.GetValue(events);
  29:             if (null != head)
  30:             {
  31:                 EventHandlerWrapperUtil.Wrap(head, exceptionPolicyName);
  32:             }
  33:         }
  34:         foreach (Control subControl in control.Controls)
  35:         {
  36:             WrapEventHandlers(subControl);
  37:         }
  38:     }
  39:  
  40:     protected virtual string GetExceptionPolicyName()
  41:     {
  42:         ExceptionPolicyAttribute attribute = this.GetType().GetCustomAttributes(true)
  43:             .OfType<ExceptionPolicyAttribute>().FirstOrDefault();
  44:         if (null != attribute)
  45:         {
  46:             return attribute.ExceptionPolicyName;
  47:         }
  48:         else
  49:         {
  50:             return ExceptionPolicyName;
  51:         }
  52:     }
  53: }

七、AlertHandler

我想有人对用于显示错误消息对话框的AltertHandler的实现很感兴趣,下面给出了它和对应的AlertHandlerData的定义。从如下的代码可以看出,AltertHandler仅仅是调用Page的RaisePostBackEvent方法注册了一段显示错误消息的JavaScript脚本而已。

   1: [ConfigurationElementType(typeof(AlertHandlerData))]
   2: public class AlertHandler: IExceptionHandler
   3: {
   4:     public string MessageTemplate { get; private set; }
   5:     public AlertHandler(string messageTemplate)
   6:     {
   7:         this.MessageTemplate = messageTemplate;
   8:     }
   9:  
  10:     protected string FormatMessage(Exception exception)
  11:     {
  12:         Guard.ArgumentNotNull(exception, "exception");
  13:         string messageTemplate = string.IsNullOrEmpty(this.MessageTemplate) ? exception.Message : this.MessageTemplate;
  14:         return messageTemplate.Replace("{ExceptionType}", exception.GetType().Name)
  15:                                 .Replace("{HelpLink}", exception.HelpLink)
  16:                                 .Replace("{Message}", exception.Message)
  17:                                 .Replace("{Source}", exception.Source)
  18:                                 .Replace("{StackTrace}", exception.StackTrace);
  19:     }
  20:  
  21:     public Exception HandleException(Exception exception, Guid handlingInstanceId)
  22:     {
  23:         Page page = HttpContext.Current.Handler as Page;
  24:         if (null != page)
  25:         {
  26:  
  27:             string message = this.FormatMessage(exception);
  28:             string hiddenControl = "hiddenCurrentPageException";
  29:             page.ClientScript.RegisterHiddenField(hiddenControl, message);
  30:             string script = string.Format("<Script language=\"javascript\">var obj=document.forms[0].{0};alert(unescape(obj.value));</Script>", 
  31:                 new object[] { hiddenControl });
  32:             page.ClientScript.RegisterStartupScript(base.GetType(), "ExceptionHandling.AlertHandler", script);
  33:         }
  34:         return exception;
  35:     }
  36: }
  37:  
  38: public class AlertHandlerData : ExceptionHandlerData
  39: {
  40:     [ConfigurationProperty("messageTemplate", IsRequired = false, DefaultValue="")]
  41:     public string MessageTemplate
  42:     {
  43:         get { return (string)this["messageTemplate"]; }
  44:         set { this["messageTemplate"] = value; }
  45:     }
  46:  
  47:     public override IEnumerable<TypeRegistration> GetRegistrations(string namePrefix)
  48:     {
  49:         yield return new TypeRegistration<IExceptionHandler>(() => new AlertHandler(this.MessageTemplate))
  50:         {
  51:             Name = this.BuildName(namePrefix),
  52:             Lifetime = TypeRegistrationLifetime.Transient
  53:         };
  54:     }
  55: }
转载自:http://www.cnblogs.com/artech/archive/2012/10/28/automatic-exception-handling-aspnet.html
程序员的基础教程:菜鸟程序员

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/460506.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

vs2010插件

转载于:https://www.cnblogs.com/tinytiny/p/3608030.html

leetcode1——两数之和

一、提出问题 给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出 和为目标值 target 的那 两个 整数&#xff0c;并返回它们的数组下标。 你可以假设每种输入只会对应一个答案。但是&#xff0c;数组中同一个元素在答案里不能重复出现。 你可以按…

如何脱离SDK,使用DW5.5和phonegap以及JQMobile搭建开发环境

也许有些人是学C出身&#xff0c;对于Java几乎不了解。一时心血来潮想学学android开发&#xff0c;于是下载了Eclipse&#xff0c;安装了SDK&#xff0c;有模有样的学习起来。也许是懒惰了&#xff0c;对于java一直总是提不起精神。于是确定使用DreamweaverJquery mobilePhoneg…

leetcode2——两数相加

一、提出问题 给你两个非空的链表&#xff0c;表示两个非负的整数。它们每位数字都是按照逆序的方式存储的&#xff0c;并且每个节点只能存储一位数字。请你将两个数相加&#xff0c;并以相同形式返回一个表示和的链表。 你可以假设除了数字 0 之外&#xff0c;这两个数都不会以…

32为Linux安卓AVD启动报错

2019独角兽企业重金招聘Python工程师标准>>> 当使用android的AVD时提示以下错误&#xff1a;Starting emulator for AVD NexusOne ERROR: 32-bit Linux Android emulator binaries are DEPRECATED, to use them you will have to do at least one of the following:…

填问卷,得《2015中国呼叫中心知识库现状与问题报告》

为了解中国呼叫中心知识库运营现状和存在的主要问题&#xff0c;掌握呼叫中心知识库的总体发展水平&#xff0c;中国知识管理中心&#xff08;KMCenter&#xff09;面向全国呼叫中心发起“2015中国呼叫中心知识库现状与问题调研”活动&#xff0c;主要通过问卷调研和典型用户访…

ARM汇编的特点

以下内容源于网络资源的学习与整理&#xff0c;如有侵权请告知删除。 一、LDR/STR架构 ARM采用RISC架构&#xff0c;CPU本身不能直接读取内存&#xff0c;需要先将内存中的内容加载到CPU的通用寄存器中才能被CPU处理。换言之&#xff0c;寄存器是CPU和内存进行数据交换的中介。…

持久代是方法区还是堆中的?

2019独角兽企业重金招聘Python工程师标准>>> 昨天跟一哥们讨论&#xff0c;持久代在方法区&#xff0c;属不属于堆中的? 它的意思是持久代不属于堆,属于方法区&#xff0c;而我则认为持久代属于方法区也属于堆。 结果今天上网一查,还真的是。下面是解释: 持久代”仅…

Web 前端攻防(2014版)-baidu ux前端研发部

http://fex.baidu.com/articles/page2/ Web 前端攻防&#xff08;2014版&#xff09; zjcqoo | 20 Jun 2014禁止一切外链资源 外链会产生站外请求&#xff0c;因此可以被利用实施 CSRF 攻击。 目前国内有大量路由器存在 CSRF 漏洞&#xff0c;其中相当部分用户使用默认的管理账…

Silverlight动态设置WCF服务Endpoint

2013-02-02 05:57 by jv9, 1763 阅读, 3 评论, 收藏, 编辑 去年12月收到一位朋友的邮件&#xff0c;咨询Silverlight使用WCF服务&#xff0c;应用部署后一直无法访问的问题&#xff0c;通过几次交流&#xff0c;才发现在他的项目中&#xff0c;全部使用静态URL作为WCF服务的End…

第4季3:Hi3518e的sensor接口引脚复用设置(load3518e文件)

以下内容源于朱有鹏嵌入式课程的学习与整理&#xff0c;如有侵权请告知删除。 在第2、3季的内容中&#xff0c;在板载系统的配置脚本即/etc/profile文件中&#xff0c;都有如下这句代码&#xff1a; ./load3518e -i -sensor ar0130 -osmem 32 -total 64 在第4季1&#xff1a…

iOS游戏开发 几个有利工具

2019独角兽企业重金招聘Python工程师标准>>> iOS游戏开发 几个有利工具 本文介绍的是iOS游戏开发 几个有利工具&#xff0c;为友们介绍几款开发工具&#xff0c;游戏爱好者记住了&#xff01;先来看内容。 AD&#xff1a; iOS游戏开发 几个有利工具是本文要介绍的内…

关于editor网页编辑器ueditor.config.js 配置图片上传

最近公司项目在做一个门户网站&#xff0c;其中新闻和简介等部分使用到了ueditor编辑器&#xff0c;但是上级明确指示需要图片上传这个功能&#xff0c;这时却发现图片上传功能不能正常使用&#xff0c;上传时一直报错&#xff0c;网上收了好几个处理办法&#xff0c;都说的不够…

[歪谈]拽一个贵人出来给你当炮架子

[歪谈]拽一个贵人出来给你"当炮架子" 我们在古装神话剧中经常会听到某个“先知”对前来算命的人说&#xff1a;你会在某某时刻遇到你的贵人。而这个贵人会在事业上助你一臂之力。 这里有个问题&#xff1a;贵人到底是什么&#xff1f;我们怎样去寻找我们的贵人。 前几…

第6季1:H264编码原理与基本概念

以下内容源于网络资源的学习与整理&#xff0c;如有侵权请告知删除。 参考博客 &#xff08;1&#xff09;H264 编码基本原理_ByteSaid的博客-CSDN博客_h264编码原理 &#xff08;2&#xff09;H264 编码简介_mydear_11000的博客-CSDN博客 &#xff08;3&#xff09;什么是I帧…

pureMVC简单示例及其原理讲解四(Controller层)

本节将讲述pureMVC示例中的Controller层。 Controller层有以下文件组成&#xff1a; AddUserCommand.asDeleteUserCommand.asModelPrepCommand.asViewPrepCommand.asStartupCommand.asAddUserCommand 。顾名思义&#xff0c;它是添加用户命令。让我们首先看看代码。 Addusercom…

ActiveMQ学习笔记(2)——JMS消息模型

2019独角兽企业重金招聘Python工程师标准>>> 1.1 JMS模型简介 JMS支持两种消息通信模型&#xff1a; 点对点模型(Point to Point&#xff0c;P2P)发布者/订阅者模型&#xff08;publish/subscribe&#xff0c; pub/sub&#xff09;P2P模型中&#xff0c;Sender把一…

C# 图片盖章功能实现,支持拖拽-旋转-放缩-保存

实现图片盖章功能&#xff0c;在图片上点击&#xff0c;增加“图章”小图片&#xff0c;可以拖拽“图章”到任意位置&#xff0c;也可以点击图章右下角园框&#xff0c;令图片跟着鼠标旋转和放缩。 操作方法&#xff1a;1.点击增加“图章”2.选中移动图标3.点中右下角放缩旋转图…

【一周一算法】算法2:邻居好说话——冒泡排序

【啊哈&#xff01;算法】    简化版的桶排序不仅仅有上一节所遗留的问题&#xff0c;更要命的是&#xff1a;它非常浪费空间&#xff01;例如需要排序数的范围是0~2100000000之间&#xff0c;那你则需要申请2100000001个变量&#xff0c;也就是说要写成int a[2100000001]。…

用TextPaint来绘制文字

TextPaint是paint的子类&#xff0c;用它可以很方便的进行文字的绘制&#xff0c;一般情况下遇到绘制文字的需求时&#xff0c;我们一般用TextPaint所提供的方法。开始学习如何绘制文字之前&#xff0c;我们必须要先了解下android中文字是怎么绘制到屏幕上的&#xff0c;文字的…