这篇文章主要介绍Asp.Net中WebForm生命周期的概念分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!一.Asp.Net页面生命周期的概念当我们在浏览器地址栏中输入网址,回车查看页面时,这时会向服务器端IIS)发送一个request请求,服务器就会判断发送过来的请求页面,当完全识别 TTP页面处理程序类后,ASP.NET运行时将调用处理程序的 ProcessRequest 方法来处理请求、创建页面对象。通常情况下,无需更改此方法的实现,因为它是由 Page 类提供的。接下来被创建页面对象的ProcessRequest方法使页面经历了各个阶段:初始化、加载视图状态信息和回发数据、加载页面的用户代码以及执行回发服务器端事件。之后,页面进入显示模式:收集更新的视图状态,生成 HTML 代码并随后将代码发送到输出控制台。最后,卸载页面,并认为请求处理完毕。其中页面对象ProcessRequest方法 完成的这一系列事件的处理过程就是Asp.Net页面生命周期。二、为什么需要了解Asp.Net页面生命周期因为了解Asp.Net页面生命周期,可以帮助开发者在生命周期的合适阶段编写程序,以达到预期的效果,另外如果你想自己开发自定义控件,就必须熟悉页面的生命周期,以便正确进行控件的初始化,使用视图状态数据填充控件的属性,以及运行任何控件行为代码。也就是说只有熟悉了从创建到最后卸载等一系列的事件,开发起来才会顺心顺手,不会出现云里雾里的感觉。三、生命周期阶段1、请求页面:页请求发生在页生命周期开始之前。2、开始:在开始阶段,将设置页属性,如Request和Response。在此阶段,页还将确定请求是回发请求还是新请求,并设置IsPostBack属性。3、初始化页面:页面初始化期间,可以使用页中的控件,并将设置每个控件的UniqueID属性。如果当前请求是回发请求,则回发数据尚未加载,并且控件属性值尚未还原为视图状态中的值。4、加载页面:加载期间,如果当前请求是回发请求,则将使用从视图状态和控件状态恢复的信息加载控件属性。5、验证:在验证期间,将调用所有验证程序控件的Validate方法,此方法将设置各个验证程序控件和页的IsValid属性。6、回发事件处理:如果请求是回发请求,则将调用所有事件处理程序。7、呈现页面:在页面呈现期间,视图状态将被保存到页面,然后页面将调用每个控件,以将其呈现的输出提供给页的Response属性的OutputStream。8、卸载页面:完全呈现页、将页发送至客户端并准备丢弃时,将调用卸载。此时,将卸载页属性(如Response和Request)并执行清理。四、生命周期的事件1、PreInit使用该事件来执行下列操作:检查 IsPostBack 属性来确定是不是第一次处理该页。创建或重新创建动态控件。动态设置主控页。动态设置 Theme 属性。读取或设置配置文件属性值。注意:如果请求是回发请求,则控件的值尚未从视图状态还原。如果在此阶段设置控件属性,则其值可能会在下一事件中被重写。2、Init在所有控件都已初始化且已应用所有外观设置后引发。使用该事件来读取或初始化控件属性。3、InitComplete由 Page 对象引发。使用该事件来处理要求先完成所有初始化工作的任务。4、PreLoad如果需要在 Load 事件之前对页或控件执行处理,请使用该事件。在 Page 引发该事件后,它会为自身和所有控件加载视图状态,然后会处理 Request 实例包括的任何回发数据。5、LoadPage 在 Page 上调用 OnLoad 事件方法,然后以递归方式对每个子控件执行相同操作,如此循环往复,直到加载完本页和所有控件为止。使用 OnLoad 事件方法来设置控件中的属性并建立数据库连接6、控件事件使用这些事件来处理特定控件事件,如 Button 控件的 Click 事件或 TextBox 控件的 TextChanged 事件。注意:在回发请求中,如果页包含验证程序控件,请在执行任何处理之前检查 Page 和各个验证控件的 IsValid 属性。7、LoadComplete对需要加载页上的所有其他控件的任务使用该事件。8、PreRender在该事件发生前:Page 对象会针对每个控件和页EnsureChildControls。设置了 DataSourceID 属性的每个数据绑定控件会调用 DataBind 方法。有关更多信息,请参见下面的数据绑定控件的数据绑定事件。页上的每个控件都会发生 PreRender 事件。使用该事件对页或其控件的内容进行最后更改。9、SaveStateComplete在该事件发生前,已针对页和所有控件保存了 ViewState。将忽略此时对页或控件进行的任何更改。使用该事件执行满足以下条件的任务:要求已经保存了视图状态,但未对控件进行任何更改。10、Render这不是事件;在处理的这个阶段,Page 对象会在每个控件上调用此方法。所有 ASP.NET Web 服务器控件都有一个用于写出发送给浏览器的控件标记的 Render 方法。如果创建自定义控件,通常要重写此方法以输出控件的标记。不过,如果自定义控件只合并标准的 ASP.NET Web 服务器控件,不合并自定义标记,则不需要重写 Render 方法。有关更多信息,请参见开发自定义 ASP.NET 服务器控件。用户控件(.ascx 文件)自动合并呈现,因此不需要在代码中显式呈现该控件。11、Unload该事件首先针对每个控件发生,继而针对该页发生。在控件中,使用该事件对特定控件执行最后清理,如关闭控件特定数据库连接。对于页自身,使用该事件来执行最后清理工作,如:关闭打开的文件和数据库连接,或完成日志记录或其他请求特定任务。注意在卸载阶段,页及其控件已被呈现,因此无法对响应流做进一步更改。如果尝试调用方法(如 Response.Write 方法),则该页将引发异常。以上是“Asp.Net中WebForm生命周期的概念分析”这篇文章的所有内容,感谢各位的阅读!希望分享的内容对大家有帮助,更多相关知识,欢迎关注辰迅云资讯频道!...
这篇文章将为大家详细讲解有关.net中什么是反射,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。反射是.net中非常有用的特性。什么是反射可以用一句话来概括就是:基于程序集和元数据,可以动态创建某个类型的实例,调用方法,和访问对象成员的一种能力。为什么要使用反射在编译时无法确定要调用的对象的时候,就不得不使用反射。反射的应用场景最常见的应用场景有:1,基于工厂模式实现的数据库切换。2,ORM框架,因为它要面对的是通用的模型,所有的属性或方法都是动态生成的。3,基于插件的系统,在完全不知道外部插件究竟是什么东西的情况下,是一定无法在编译期确定的,因此会使用反射进行加载。程序集由于反射是基于程序集和元数据的,所以有必要解释一下什么是程序集,什么时元数据。程序集:程序集是所有类型的集合,编译后生成PE文件(例如可执行文件.exe和类库文件.dll)。元数据:元数据是程序集中的一部分,主要包含了名称,版本,语言文化和公钥标记等信息。关于“.net中什么是反射”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,使各位可以学到更多知识,如果觉得文章不错,请把它分享出去让更多的人看到。...
这篇文章主要介绍了衡量高质量代理IP的因素有哪些,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。1、可用ip数量。代理人IP池中IP数量较多,意味着IP重复率较低,IP代理人的工作效率较高。因此在选择代理ip时,必须考虑ip的数量是否大。2、代理ip线路的节点范围。代理IP线路节点覆盖范围越广越好,因为线路越广可以减少网络节点远近问题对通信的影响。3、访问速度。使用快速访问的代理ip,工作效率更高。所以在选择代理ip的时候也要考虑速度,速度越快越好。4、安全性。在选择代理ip时,须选择正规安全的代理服务提供商。最好不要使用免费代理ip。使用免费代理ip可能会导致信息泄露。5、代理ip的纯度。IP的纯度是指用户数量,同一个ip用户越少越好。如果同一个ip用户数量过多,就不能保证IP是否会因为别人使用不规范而无法使用。感谢你能够认真阅读完这篇文章,希望小编分享的“衡量高质量代理IP的因素有哪些”这篇文章对大家有帮助,同时也希望大家多多支持辰迅云,关注辰迅云资讯频道,更多相关知识等着你来学习!...
这篇文章给大家分享的是有关如何处理Spring Cloud Finchley版中Consul多实例注册的问题的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。consul 简介consul 具有以下性质:服务发现:consul通过http 方式注册服务,并且服务与服务之间相互感应。服务健康监测key/value 存储多数据中心consul可运行在mac windows linux 等机器上。由于Spring Cloud对Etcd的支持一直没能从孵化器中出来,所以目前来说大多用户还在使用Eureka和Consul,之前又因为Eureka 2.0不在开源的消息,外加一些博眼球的标题党媒体使得Eureka的用户有所减少,所以,相信在选择Spring Cloud的用户群体中,应该有不少用户会选择Consul来做服务注册与发现。本文就来说一下,当我们使用Spring Cloud最新的Finchley版 + Consul 1.2.x时候最严重的一个坑:多实例注册的问题。问题解读问题:该问题可能在开发阶段不一定会发现,但是在线上部署多实例的时候,将会发现Consul中只有一个实例。原因:造成该问题的主要原因是Spring Cloud Consul在注册的时候实例名(InstanceId)采用了:“服务名-端口号”(即: {spring.application.name}-{server.port} )的值,可以看到这个实例名如果不改变端口号的情况下,实例名都是相同的。如果熟悉Spring Cloud Consul的读者,可能会问老版本也是这个规则,怎么没有这个问题呢?。主要是由于Consul对实例唯一性的判断标准也有改变,在老版本的Consul中,对于实例名相同,但是服务地址不同,依然会认为是不同的实例。在Consul 1.2.x中,服务实例名成为了集群中的唯一标识,所以,也就导致了上述问题。解决方法既然知道了原因,那么我们要解决它就可以有的放矢了。下面就来介绍两个具体的解决方式:方法一:通过配置属性指定新的规则下面举个例子,通过 spring.cloud.consul.discovery.instance-id 参数直接来配置实例命名规则。这里比较粗暴的通过随机数来一起组织实例名。当然这样的组织方式并不好,因为随机数依然有冲突的可能,所以您还可以用更负责的规则来进行组织实例名。spring.cloud.consul.discovery.instance-id=${spring.application.name}-${random.int[10000,99999]}方法二:通过扩展 ConsulServiceRegistry 来重设实例名由于通过配置属性的方式对于定义实例名的能力有限,所以我们希望可以用更灵活的方式来定义。这时候我们就可以通过重写 ConsulServiceRegistry 的 register 方法来修改。比如下面的实现:public class MyConsulServiceRegistry extends ConsulServiceRegistry { public MyConsulServiceRegistry(ConsulClient client, ConsulDiscoveryProperties properties, TtlScheduler ttlScheduler, HeartbeatProperties heartbeatProperties) { super(client, properties, ttlScheduler, heartbeatProperties); } @Override public void register(ConsulRegistration reg) { reg.getService().setId(reg.getService().getName() + “-” + reg.getService().getAddress() + “-” + reg.getService().getPort()); super.register(reg); } }上面通过拼接“服务名”-“ip地址”-“端口号”的方式,构造了一个绝对唯一的实例名,这样就可以让每个服务实例都能正确的注册到Consul上了。感谢各位的阅读!关于“如何处理Spring Cloud Finchley版中Consul多实例注册的问题”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,让大家可以学到更多知识,如果觉得文章不错,可以把它分享出去让更多的人看到吧!...
本篇内容介绍了“xp系统开机提示页面文件太小的故障原因和解决方法”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!故障原因分析:该问题是XP系统中分配的虚拟内存太小导致的,我们只要设置一个合理的虚拟内存值就能解决。解决方法:1、右键“我的电脑”选择“属性”;2、在打开的系统属性窗口中,切换到“高级”标签页,点击性能区域的“设置”按钮;3、在打开的性能选项窗口中,切换到“高级”标签页,在其中的“虚拟内存”栏中单击“更改”按钮;4、在打开的虚拟内存设置页面中,初始大小填物理内存大小就可以了,最大值可填写到物理内存的2倍,比如你的内存是1GB,那么可填写2048M的虚拟内存值,最小虚拟内存和最大虚拟内存也可设置成一样大小,这样效果更好;5、设置完成后,再点击一下“设置”按钮,让你的设置生效,这样XP系统的页面文件太小问题就解决了。 “xp系统开机提示页面文件太小的故障原因和解决方法”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注辰讯云网站,小编将为大家输出更多高质量的实用文章!...
这篇文章给大家分享的是有关微信开发中如何实现接收事件推送和消息排重的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。微信服务器在5秒内收不到响应会断掉连接,并且重新发起请求,总共重试三次。这样的话,问题就来了。有这样一个场景:当用户关注微信账号时,获取当前用户信息,然后将信息写到数据库中。类似于pc端网站的注册。可能由于这个关注事件中,我们需要处理的业务逻辑比较复杂。如送积分啊,写用户日志啊,分配用户组啊。等等……一系列的逻辑需要执行,或者网络环境比较复杂,无法保证5秒内响应当前用户的操作,那如果当操作尚未完成,微信服务器又给我们的服务器推送了一条相同的关注事件,我们将再次执行我们的那些逻辑,这样就有可能导致数据库中出现重复的数据(有的童鞋就会说了,我在插入数据之前先判断当前是否已经存在了,如果存在了就不执行插入的操作。我想说的是,我当初也是这样想的,但真实的运行环境和我们的调试环境还是有差距的,直到发现数据库中有不少重复的用户信息时,我才发现消息去重的重要性。)。消息的去重普通消息和事件消息是有区别的。普通消息使用msgid,而事件消息使用FromUserName + CreateTime。我的思路是:新建类BaseMsg,有三个属性分别是FromUser,MsgFlag,CreateTime。代码如下:public class BaseMsg { /// <summary> /// 发送者标识 /// </summary> public string FromUser { get; set; } /// <summary> /// 消息表示。普通消息时,为msgid,事件消息时,为事件的创建时间 /// </summary> public string MsgFlag { get; set; } /// <summary> /// 添加到队列的时间 /// </summary> public DateTime CreateTime { get; set; } }创建个静态列表_queue,用来存储消息列表,列表的类型是List<BaseMsg>.在处理微信消息体前,首先判断列表是否实例化,如果没有实例化则实例化,否则判断列表的长度是否大于或等于50(这个可以自定义,用处就是微信并发的消息量),如果大于或等于50,则保留20秒内未响应的消息(5秒重试一次,总共重试3次,就是15秒,保险起见这里写20秒)。获取当前消息体的消息类型,并根据_queue判断当前消息是否已经请求了。如果是事件则保存FromUser和创建时间。如果是普通消息则保存MsgFlag。下面是代码:if (_queue == null) { _queue = new List<BaseMsg>(); } else if(_queue.Count>=50) { _queue = _queue.Where(q => { return q.CreateTime.AddSeconds(20) > DateTime.Now; }).ToList();//保留20秒内未响应的消息 } XElement xdoc = XElement.Parse(xml); var msgtype = xdoc.Element("MsgType").Value.ToUpper(); var FromUserName = xdoc.Element("FromUserName").Value; var MsgId = xdoc.Element("MsgId").Value; var CreateTime = xdoc.Element("CreateTime").Value; MsgType type = (MsgType)Enum.Parse(typeof(MsgType), msgtype); if (type!=MsgType.EVENT) { if (_queue.FirstOrDefault(m => { return m.MsgFlag == MsgId; }) == null) { _queue.Add(new BaseMsg { CreateTime = DateTime.Now, FromUser = FromUserName, MsgFlag = MsgId }); } else { return null; } } else { if (_queue.FirstOrDefault(m => { return m.MsgFlag == CreateTime; }) == null) { _queue.Add(new BaseMsg { CreateTime = DateTime.Now, FromUser = FromUserName, MsgFlag = CreateTime }); } else { return null; } }当消息已经存在队列中时,则不转换当前的消息为实体了,直接返回null,调用的时候,当返回null时就不做任何处理。下面开始讲解事件消息。接上篇讲。所有的消息都继承BaseMessage,而所有的事件类型都包含一个Event的属性。这里为了方便调用,将消息类型定义为枚举,代码如下:/// <summary> /// 事件类型枚举 /// </summary> public enum Event { /// <summary> /// 非事件类型 /// </summary> NOEVENT, /// <summary> /// 订阅 /// </summary> SUBSCRIBE, /// <summary> /// 取消订阅 /// </summary> UNSUBSCRIBE, /// <summary> /// 扫描带参数的二维码 /// </summary> SCAN, /// <summary> /// 地理位置 /// </summary> LOCATION, /// <summary> /// 单击按钮 /// </summary> CLICK, /// <summary> /// 链接按钮 /// </summary> VIEW, /// <summary> /// 扫码推事件 /// </summary> SCANCODE_PUSH, /// <summary> /// 扫码推事件且弹出“消息接收中”提示框 /// </summary> SCANCODE_WAITMSG, /// <summary> /// 弹出系统拍照发图 /// </summary> PIC_SYSPHOTO, /// <summary> /// 弹出拍照或者相册发图 /// </summary> PIC_PHOTO_OR_ALBUM, /// <summary> /// 弹出微信相册发图器 /// </summary> PIC_WEIXIN, /// <summary> /// 弹出地理位置选择器 /// </summary> LOCATION_SELECT, /// <summary> /// 模板消息推送 /// </summary> TEMPLATESENDJOBFINISH }定义好枚举后,就是定义消息实体了。关注/取消关注事件xml数据包如下:<xml><ToUserName><![CDATA[toUser]]></ToUserName><FromUserName><![CDATA[FromUser]]></FromUserName><CreateTime>123456789</CreateTime><MsgType><![CDATA[event]]></MsgType><Event><![CDATA[subscribe]]></Event></xml>对应的实体:/// <summary> /// 订阅/取消订阅事件 /// </summary> public class SubEventMessage : EventMessage { private string _eventkey; /// <summary> /// 事件KEY值,qrscene_为前缀,后面为二维码的参数值(已去掉前缀,可以直接使用) /// </summary> public string EventKey { get { return _eventkey; } set { _eventkey = value.Replace("qrscene_", ""); } } /// <summary> /// 二维码的ticket,可用来换取二维码图片 /// </summary> public string Ticket { get; set; } }这里需要注意的是,当用户扫描带参数的二维码时,如果用户没有关注当前公众号,用户关注时,会在消息体中带上qrscene_参数,和Ticket,所以这里定义了两个属性:EventKey,Ticket。当给EventKey赋值时,替换掉qrscene_,因为我们真正需要的就是后面的参数。扫描带参数二维码事件用户扫描带场景值二维码时,可能推送一下两种事件:如果用户还未关注公众号,则用户可以关注公众号,关注后微信会将带场景值关注事件推送给开发者。如果用户已经关注公众号,则微信会将带场景值扫描事件推送给开发者。、第一种上面已经讲了,这里就只说明下第二种。用户已关注时的事件推送xml包如下:<xml><ToUserName><![CDATA[toUser]]></ToUserName><FromUserName><![CDATA[FromUser]]></FromUserName><CreateTime>123456789</CreateTime><MsgType><![CDATA[event]]></MsgType><Event><![CDATA[SCAN]]></Event><EventKey><![CDATA[SCENE_VALUE]]></EventKey><Ticket><![CDATA[TICKET]]></Ticket></xml>对应的实体如下:/// <summary> /// 扫描带参数的二维码实体 /// </summary> public class ScanEventMessage : EventMessage { /// <summary> /// 事件KEY值,是一个32位无符号整数,即创建二维码时的二维码scene_id /// </summary> public string EventKey { get; set; } /// <summary> /// 二维码的ticket,可用来换取二维码图片 /// </summary> public string Ticket { get; set; } }上报地理位置事件当公众号开启上报地理位置功能后,每次进入公众号会话时,用户同意上报地理位置后,都会在进入时上报地理位置,或在进入回话后每5秒上报一次地理位置,公众号可以再公众平台的后台中修改设置。上报地理位置时,微信会将上报地理位置事件推送到开发者填写的url。xml数据包如下:<xml><ToUserName><![CDATA[toUser]]></ToUserName><FromUserName><![CDATA[fromUser]]></FromUserName><CreateTime>123456789</CreateTime><MsgType><![CDATA[event]]></MsgType><Event><![CDATA[LOCATION]]></Event><Latitude>23.137466</Latitude><Longitude>113.352425</Longitude><Precision>119.385040</Precision></xml>对应的实体如下:/// <summary> /// 上报地理位置实体 /// </summary> public class LocationEventMessage : EventMessage { /// <summary> /// 地理位置纬度 /// </summary> public string Latitude { get; set; } /// <summary> /// 地理位置经度 /// </summary> public string Longitude { get; set; } /// <summary> /// 地理位置精度 /// </summary> public string Precision { get; set; } }自定义菜单事件常用的事件有:click,view,scancode_puth,scancode_waitmsg,location_select。另外还有三种发图的事件,由于并不常用,笔者也没想到使用场景,再次就不一一讲述了,有兴趣的可以自己研究下,或者和我进行交流。click事件推送的xml数据包:<xml><ToUserName><![CDATA[toUser]]></ToUserName><FromUserName><![CDATA[FromUser]]></FromUserName><CreateTime>123456789</CreateTime><MsgType><![CDATA[event]]></MsgType><Event><![CDATA[CLICK]]></Event><EventKey><![CDATA[EVENTKEY]]></EventKey></xml>view事件推送的xml数据包和click的格式是一样的,所以定义一个类就可以了,如下:/// <summary> /// 普通菜单事件,包括click和view /// </summary> public class NormalMenuEventMessage : EventMessage { /// <summary> /// 事件KEY值,设置的跳转URL /// </summary> public string EventKey { get; set; } }scancode事件的xml数据包如下:<xml><ToUserName><![CDATA[ToUserName]]></ToUserName><FromUserName><![CDATA[FromUserName]]></FromUserName><CreateTime>1419265698</CreateTime><MsgType><![CDATA[event]]></MsgType><Event><![CDATA[scancode_push]]></Event><EventKey><![CDATA[EventKey]]></EventKey><ScanCodeInfo><ScanType><![CDATA[qrcode]]></ScanType><ScanResult><![CDATA[http://weixin.qq.com/r/JEy5oRLE0U_urVbC9xk2]]></ScanResult></ScanCodeInfo></xml>对应的实体如下:/// <summary> /// 菜单扫描事件 /// </summary> public class ScanMenuEventMessage : EventMessage { /// <summary> /// 事件KEY值 /// </summary> public string EventKey { get; set; } /// <summary> /// 扫码类型。qrcode是二维码,其他的是条码 /// </summary> public string ScanType { get; set; } /// <summary> /// 扫描结果 /// </summary> public string ScanResult { get; set; } }至此,当前常用的事件类型消息都已定义完毕,结合上一篇所讲的,将xml数据包转换成对象的完整代码如下:public class MessageFactory { private static List<BaseMsg> _queue; public static BaseMessage CreateMessage(string xml) { if (_queue == null) { _queue = new List<BaseMsg>(); } else if(_queue.Count>=50) { _queue = _queue.Where(q => { return q.CreateTime.AddSeconds(20) > DateTime.Now; }).ToList();//保留20秒内未响应的消息 } XElement xdoc = XElement.Parse(xml); var msgtype = xdoc.Element("MsgType").Value.ToUpper(); var FromUserName = xdoc.Element("FromUserName").Value; var MsgId = xdoc.Element("MsgId").Value; var CreateTime = xdoc.Element("CreateTime").Value; MsgType type = (MsgType)Enum.Parse(typeof(MsgType), msgtype); if (type!=MsgType.EVENT) { if (_queue.FirstOrDefault(m => { return m.MsgFlag == MsgId; }) == null) { _queue.Add(new BaseMsg { CreateTime = DateTime.Now, FromUser = FromUserName, MsgFlag = MsgId }); } else { return null; } } else { if (_queue.FirstOrDefault(m => { return m.MsgFlag == CreateTime; }) == null) { _queue.Add(new BaseMsg { CreateTime = DateTime.Now, FromUser = FromUserName, MsgFlag = CreateTime }); } else { return null; } } switch (type) { case MsgType.TEXT: return Utils.ConvertObj<TextMessage>(xml); case MsgType.IMAGE: return Utils.ConvertObj<ImgMessage>(xml); case MsgType.VIDEO: return Utils.ConvertObj<VideoMessage>(xml); case MsgType.VOICE: return Utils.ConvertObj<VoiceMessage>(xml); case MsgType.LINK: return Utils.ConvertObj<LinkMessage>(xml); case MsgType.LOCATION: return Utils.ConvertObj<LocationMessage>(xml); case MsgType.EVENT://事件类型 { var eventtype = (Event)Enum.Parse(typeof(Event), xdoc.Element("Event").Value.ToUpper()); switch (eventtype) { case Event.CLICK: return Utils.ConvertObj<NormalMenuEventMessage>(xml); case Event.VIEW: return Utils.ConvertObj<NormalMenuEventMessage>(xml); case Event.LOCATION: return Utils.ConvertObj<LocationEventMessage>(xml); case Event.LOCATION_SELECT: return Utils.ConvertObj<LocationMenuEventMessage>(xml); case Event.SCAN: return Utils.ConvertObj<ScanEventMessage>(xml); case Event.SUBSCRIBE: return Utils.ConvertObj<SubEventMessage>(xml); case Event.UNSUBSCRIBE: return Utils.ConvertObj<SubEventMessage>(xml); case Event.SCANCODE_WAITMSG: return Utils.ConvertObj<ScanMenuEventMessage>(xml); default: return Utils.ConvertObj<EventMessage>(xml); } } break; default: return Utils.ConvertObj<BaseMessage>(xml); } } }感谢各位的阅读!关于“微信开发中如何实现接收事件推送和消息排重”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,让大家可以学到更多知识,如果觉得文章不错,可以把它分享出去让更多的人看到吧!...
小编给大家分享一下微信开发中如何实现后台登录,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!效果图找到app.js在里面写如下代码App({ onLaunch: function () { //调用API从本地缓存中获取数据 var logs = wx.getStorageSync('logs') || [] logs.unshift(Date.now()) wx.setStorageSync('logs', logs) }, globalData: { adminUserViewId: "", token: "", userInfo: null, BaseURL:"http://airb.cakeboss.com.cn" // BaseURL:"http://192.168.0.107:8080" },敲黑板划重点:上图中的代码片段重要的地方就是:“globalData中的 adminUserViewId: "",token: "" ”这两个参数是前端需要存储的后台参数,用来标记用户的登录状态的。然后建一个login文件夹,在login.wxml中写如下代码<import src="../../components/toast.wxml" /><!-- is="toast" 匹配组件中的toast提示 如果用dialog的话这就是dialog --><template is="toast" data="{{ ...$wux.toast }}" /><view class="login_container"> <view class="login_view"> <text class="login_lable">账号:</text> <input class="login_text" placeholder="请输入登录账号" bindinput="listenerUsernameInput"/> </view> <view class="login_view"> <text class="login_lable">密码:</text> <input class="login_text" placeholder="请输入密码" password="true" bindinput="listenerPasswordInput"/> </view> <view> <button class="login_button" bindtap="loginAction">登录</button> </view></view>然后建一个login文件夹,在login.wxss中写如下代码.login_container { margin-top: 30px; }.login_view { width: calc(100% - 40px); padding: 0 20px; line-height: 45px; height: 45px; margin-bottom: 20px; }.login_text { float: left; height: 45px; line-height: 45px; font-size: 12px; border: 1px solid rgb(241, 242, 243); padding: 0 12px; width: calc(100% - 70px); border-radius: 4px; }.login_lable { float: left; font-size: 12px; width: 40px; }.login_button { width: 150px; background: green; color: #fff; }在login.js中写如下代码//login.js//获取应用实例var app = getApp()var util = require('../../utils/util.js'); Page({ data: { motto: 'Hello World', username: "", password: "" }, onLoad(options) { // 初始化提示框 this.$wuxToast = app.wux(this).$wuxToast }, /** 监听帐号输入 */ listenerUsernameInput: function (e) { this.data.username = e.detail.value; }, /** 监听密码输入 */ listenerPasswordInput: function (e) { this.data.password = e.detail.value; }, // 登录按钮点击事件 loginAction: function () { var userName = this.data.username; var passwords = this.data.password; var that = this; if (userName === "") { that.$wuxToast.show({ type: 'text', timer: 1000, color: '#fff', text: "用户名不能为空!", success: () => console.log('用户名不能为空!') }) return; } if (passwords === "") { that.$wuxToast.show({ type: 'text', timer: 1000, color: '#fff', text: "密码不能为空!", success: () => console.log('密码不能为空!') }) return; } //加载提示框 util.showLoading("登录中..."); var urlStr = app.globalData.BaseURL + '/api/adminUser/login'; wx.request({ method: "POST", url: urlStr, //仅为示例,并非真实的接口地址 data: util.json2Form({ username: userName, password: passwords }), header: { "Content-Type": "application/x-www-form-urlencoded" }, success: function (res) { util.hideToast(); console.log(res.data); var code = res.data.code; if (code === 200) { // 后台传递过来的值 var adminUserViewId = res.data.data.adminUserViewId; var token = res.data.data.token; // 设置全局变量的值 app.globalData.adminUserViewId = res.data.data.adminUserViewId; app.globalData.token = res.data.data.token; // 将token存储到本地 wx.setStorageSync('adminUserViewId', adminUserViewId); wx.setStorageSync('token', token); console.log("登录成功的adminUserViewId:" + adminUserViewId); console.log("登录成功的token:" + token); // 切换到首页 wx.switchTab({ url: '/pages/index/index' }) } else { that.$wuxToast.show({ type: 'text', timer: 1000, color: '#fff', text: res.data.msg, success: () => console.log('登录失败,请稍后重试。' + res.data.msg) }) } }, fail: function () { util.hideToast(); console.log("登录失败"); that.$wuxToast.show({ type: 'text', timer: 1000, color: '#fff', text: '服务器君好累以上是“微信开发中如何实现后台登录”这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注辰迅云资讯频道!...
今天就跟大家聊聊有关如何用Linux git命令设置免输密码,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。Linux下git 命令 免输密码设置1. 在~/目录下, touch创建文件 .Git-credentialstouch .git-credentialsvim .git-credentials点击 “i” 进入编辑文件模式https://{username}:{password}@github.com例如 https://zhang3:12345@github.com点击“ESC” 然后输入“:wq”退出编辑并保存2. 在终端下执行git config --global credential.helper store3. 可以看到~/.gitconfig文件,会多了一项:vi .gitconfig 进入可以看到[credential]helper = store4. 再次执行 git命令只需要输入1次 正确的账号和密码 以后可以直接跳过输入账号密码了看完上述内容,你们对如何用Linux git命令设置免输密码有进一步的了解吗?如果还想了解更多知识或者相关内容,请关注辰迅云资讯频道,感谢大家的支持。...
这篇文章将为大家详细讲解有关css中Padding与line-height的区别有哪些,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。你对Padding属性与line-height的区别是否了解,这里向大家描述一下,其实padding是布局设置,对容器进行内边距定义。而line-height是行距设置,设置对象(如文本)的行高。Padding属性与line-height的区别最近有朋友提出一个问题,padding与line-height的区别。这是CSS基础知识,对盒模型知识与布局以及相关属性不是很了解的时候,是很容易混淆与分不清楚的。其实padding是布局设置,对容器进行内边距定义。而line-height是行距设置,设置对象(如文本)的行高。Padding属性简介padding是内补丁也叫内边距,也称为填充,padding 简写属性在一个声明中设置所有内边距属性。检索或设置对象四边的补丁边距。语法:padding:padding-top|padding-right|padding-bottom|padding-left说明:检索或设置对象四边的内补丁。对于td和th对象而言默认值为1。其他对象的默认值为0。参数:length:长度值;%:百分比,由父元素宽度决定初始值:对于td和th对象而言默认值为1。其他对象的默认值为0。继承性:不可继承注释:不允许使用负值。如果提供全部四个参数值,将按上-右-下-左的顺序作用于四边。如果只提供一个,将用于全部的四条边。如果提供两个,***个用于上-下,第二个用于左-右。如果提供三个,***个用于上,第二个用于左-右,第三个用于下。内联对象要使用该属性,必须先设定对象的height或width属性,或者设定position属性为absolute,且不允许负值。line-height是行高检索或设置对象的行高。即字体***端与字体内部顶端之间的距离。如行内包含多个对象,则应用***行高。此时行高不可为负值。也就是文本的行距。文本通常是一行或多行组成的,表示各行之间的距离。例如:<p>网页教学网--中国网页设计,网页制作***站</p>或<div>网页教学网--中国网页设计,网页制作***站</div> p,div{ padding-top:40px; line-height:200%; }最终的效果如图所示:关于“css中Padding与line-height的区别有哪些”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,使各位可以学到更多知识,如果觉得文章不错,请把它分享出去让更多的人看到。...
这篇文章主要为大家展示了“非Spring管理Bean怎么添加AOP”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“非Spring管理Bean怎么添加AOP”这篇文章吧。问题如下图归其根本这是个历史项目,里面有很多的类并没有交给spring管理,但现在需要统一添加日志。面对这样的问题,其实只要了解AOP的原理,就会有多种方法。AOP都是基于动态代理来实现,而动态代理常见的就是cglib和java动态代理,不了解的可以看下之前干货君写的文章java动态代理为什么需要基于接口cglib动态代理对类没有任何限制吗?但此两种方法似乎在这样的场景不好实现,需要修改大量的代码,那么有没有什么好的方案呢?答案当然是有。首先要清楚的是AOP的底层实现原理就是字节码,我们只需要从字节码层面,就一定可以解决这样的问题。因此可以利用编译期增强和运行期增强,常见的方案有两种,一种Java Agent技术,另一种 AspectJ方案。Java AgentJava Agent中文名字叫做java 探针,可以在运行java时指定探针程序,对原程序无侵入,常见的一些APM工具都会这样,如skywalking,后续有机会给大家介绍下。如下图java agent的主要原理就是利用JVMTI(JVM Tool Interface),JVM用来暴露一些供用户扩展的接口集合,因此可以在此处做一些运行期字节码增强。Java Agent内容比较多,有很多大家熟悉的工具都是基于它去做的,例如阿里的arthas。本文就不介绍了,后期会给大家详细介绍下Java Agent。AspectJ方案可以利用aspectj + javac来编译织入代码,也可以利用maven插件aspectj-maven-plugin,下面利用AspectJ注解 + aspectj-maven-plugin来实战一下。aspectj-maven-plugin官网 http://www.mojohaus.org/aspectj-maven-plugin/usage.html引入依赖编译增强,依赖此jarimport org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; @Aspect public class Aop { @Pointcut("execution(* com.ganhuojun.gracefulshutdown.controller..*.*(..))") public void pointcut1(){ } @Before("pointcut1()") public void before(){ System.out.println("controller before"); } }定义注解注意:该注解不要交给spring管理<plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>aspectj-maven-plugin</artifactId> <version>1.11</version> <configuration> <complianceLevel>1.8</complianceLevel> <source>1.8</source> <!--<showWeaveInfo>true</showWeaveInfo>--> <!--<Xlint>ignore</Xlint>--> <encoding>UTF-8</encoding> <sources> <source> <basedir>src/main/java</basedir> <!--此处使用include一致会导致织入失败,暂时未找到好的解决办法,不写则引用所有的Aspect--> <!--<includes>--> <!--<include>**/Aop.java</include>--> <!--<include>**/ControllerAop.aj</include>--> <!--</includes>--> <excludes> <exclude>**/ServiceAop.java</exclude> </excludes> </source> </sources> </configuration> <executions> <execution> <goals> <goal>compile</goal> </goals> </execution> </executions> </plugin> 配置maven插件<plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>aspectj-maven-plugin</artifactId> <version>1.11</version> <configuration> <complianceLevel>1.8</complianceLevel> <source>1.8</source> <!--<showWeaveInfo>true</showWeaveInfo>--> <!--<Xlint>ignore</Xlint>--> <encoding>UTF-8</encoding> <sources> <source> <basedir>src/main/java</basedir> <!--此处使用include一致会导致织入失败,暂时未找到好的解决办法,不写则引用所有的Aspect--> <!--<includes>--> <!--<include>**/Aop.java</include>--> <!--<include>**/ControllerAop.aj</include>--> <!--</includes>--> <excludes> <exclude>**/ServiceAop.java</exclude> </excludes> </source> </sources> </configuration> <executions> <execution> <goals> <goal>compile</goal> </goals> </execution> </executions> </plugin> 排除spring的aop如果对spring aop比较熟悉的都知道,spring的aop也是基于AspectJ的,因此需要exclude的,已经配置到mavn的地方了。编译&运行&测试编译后class文件已经被织入了相关代码,如下图运行相关日志输出如下说明功能已经实现。以上是“非Spring管理Bean怎么添加AOP”这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注辰迅云资讯频道!...