博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
遭遇浏览器兼容性问题,这次是某些浏览器回退功能不正常
阅读量:4521 次
发布时间:2019-06-08

本文共 5356 字,大约阅读时间需要 17 分钟。

实现一个很简单的功能,两个动态页面A和B,从A页面导航至B页面,导航通过JS函数控制,具体写法就是window.location.href=xxx,然后点击浏览器上的回退箭头,可以从B页面回退到A页面。

各主流浏览器工作得非常好,IE6也很配合,好,很好,非常好。

但是,可是,可但是,在某些点击顺序变化的情况下,点击回退箭头,安全站点下的Safari总是强制弹出对话框。当然鉴于情况较为罕见,测试也没有计较,至少不能算不兼容。

到这里这个功能就算完成了?可能,也许,maybe…先上生产经受一下用户考验再说。

果然,在天朝总有些例外的情况会发生,别忘了我们有一堆中国特色的国产奇葩浏览器,而这次例外的是360安全浏览器和360极速浏览器。

用户反馈360浏览器回退偶尔有点问题,具体现象就是偶尔(我再次说了是偶尔)会发生点击360浏览器回退箭头直接有一个刷新前一页面(A页面)的动作,因为刷新导致A页面请求参数可能不合法而报错。

但是实际上我们所理解的回退功能是浏览器的独特的页面“记忆”功能,根本不需要页面再次刷新,所以我就顺理成章地想到要解决回退刷新页面的问题。

一开始怀疑是A页面的某个ajax异步调用造成的问题,后来抓包跟踪发现不是。

然后分析分析再分析排查排查再排查…

又过了很久终于想到可能和A页面的JS导航函数有关,就查了一下location.href,发现还有个document.location.href。然后我就学到window.location.href和document.location.href的主要区别:window.location.href对单一窗口单一文档(document)导航通常没有问题,但如果一个窗口中包含多个文档(比如页面当中嵌入iframe)在某些浏览器下就会导致奇怪的现象,例如导航失败,还有就是本文所写的浏览器回退功能不太正常。而通常情况下document.location.href和window.location.href功能一样,但在多文档窗口下它更适用一些。

看上面分析的区别,简直让我如梦方醒大彻大悟忍不住一阵激动。就尝试着把导航方法换成document.location.href,本地测试问题竟然没有重现,清理浏览器缓存重试果然确实没有重现。

再去排查A页面,果然有一个隐藏的曾经起过作用但是现在已被弃用的iframe,为了保险起见,又把A页面的iframe也去掉。

接着再发布再跑测试自己的电脑上果然正常了,非常好,很好,好。

此时我发自内心由衷钦佩自己的勤勉和严谨哈哈哈哈哈哈。

到这里你以为问题彻底解决了?图样图森破。

测试人员报告说问题依旧,清理浏览器缓存也不行,后来发布生产再验证果然问题依旧,而且测试主管顺带又提了个问题,iphone上chrome浏览器回退报一样的错误,好的,报个BUG先。

个人历史悠久的实践经验表明,分析解决浏览器兼容性这样的BUG简直就是开发人员的噩梦。除了技术方面,你还需要考虑外部环境如客户端设置、网络状况等等。针对本文所描述的浏览器回退功能的缺陷,至少我查了一堆资料就没有几个说法是可行的。有人提议重写浏览器的回退事件,我觉得也不合理,至少有点简单问题复杂化处理了,还是觉得document.loaction.href那个是至今查到的看上去理论上是最靠谱的,但是,你也许已经知道,“在理论上,理论和实践之间没有什么差别;在实践中,二者果然截然不同”。

在一番痛苦挣扎之后,想起我的直接领导以前对我说过的一句话,别一味埋头做事,有资源要学会充分利用,我就做了一个重大而明智的决定。听说我司UED好多强人,他们对浏览器的理解程度肯定比我这个野路子出身的高深的多,就发了个邮件转给他们请他们协助解决,前端就交给专业人士处理去吧。

 

附:动态调用webservice

开发过程中使用外部web服务的时候,通常我们可以通过工具如VS直接“添加web引用”,还有一种比较常用的方式是通过VS提供的命令行工具直接生成web服务代理类。

通过命令行工具的方式被很多开发人员采用,笔者也不例外。通常生成代理类的命令行格式如下:

wsdl /language:cs /out:MyService.cs /namespace:Myspace url或本地地址

但是实际开发的时候发现依赖的外部服务比较多,按照上面的方法你不得不按照各个web服务URL一个一个生成本地代理类,很明显,你会觉得这样做比较费事。好在我们知道了这个命令行的几个参数的含义,可以使用技术动态下载编译生成代理类,然后通过反射调用服务方法。主要实现封装成如下示例代码:

DynamicInvokeWebServiceusing System;using System.CodeDom;using System.CodeDom.Compiler;using System.Net;using System.Text;using System.Web.Services.Description;using System.Xml.Serialization;using Microsoft.CSharp;namespace DotNet.Common.Util{    public class WebServiceUtil    {        private static readonly string protocal = "Soap";        public static object DynamicInvokeWebService(string url, string className, string methodName, params object[] args)        {            try            {                //1、获取WSDL                  var wc = new WebClient();                var stream = wc.OpenRead(url + "?WSDL");                var sd = ServiceDescription.Read(stream);                ServiceDescriptionImporter sdi = new ServiceDescriptionImporter();                sdi.ProtocolName = protocal; // 指定访问协议。                  sdi.Style = ServiceDescriptionImportStyle.Client; // 生成客户端代理。                  sdi.CodeGenerationOptions = CodeGenerationOptions.GenerateProperties | CodeGenerationOptions.GenerateNewAsync;                sdi.AddServiceDescription(sd, null, null);                var cn = new CodeNamespace("MySpace");                //2、生成客户端代理类代码                  CodeCompileUnit ccu = new CodeCompileUnit();                ccu.Namespaces.Add(cn);                sdi.Import(cn, ccu);                //3、设定编译参数                  var compilerParameters = new CompilerParameters();                compilerParameters.GenerateExecutable = false;                compilerParameters.GenerateInMemory = true;                compilerParameters.ReferencedAssemblies.Add("System.dll");                compilerParameters.ReferencedAssemblies.Add("System.Data.dll");                compilerParameters.ReferencedAssemblies.Add("System.XML.dll");                compilerParameters.ReferencedAssemblies.Add("System.Web.Services.dll");                var csc = new CSharpCodeProvider();                var result = csc.CompileAssemblyFromDom(compilerParameters, ccu); //编译代理类                  if (result.Errors.HasErrors)                {                    var sb = new StringBuilder();                    foreach (var item in result.Errors)                    {                        sb.Append(item.ToString());                        sb.Append(Environment.NewLine);                    }                    throw new Exception(sb.ToString());                }                //4、生成代理实例,并通过反射调用方法                  var assembly = result.CompiledAssembly;                var t = assembly.GetType("MySpace." + className, true, true);                var obj = Activator.CreateInstance(t);                var method = t.GetMethod(methodName);                return method.Invoke(obj, args);            }            catch (Exception ex)            {                //Logger.Error(ex);                throw;            }        }    }}

通过上面的代码,我们可以分析得出,动态生成webservice的过程主要包括几步:

1、从目标URL下载WSDL数据

2、生成客户端代理类代码

3、并编译代理类

4、生成代理实例,并利用反射调用方法。

调用的形式如下:

var affectNum = WebServiceUtil.DynamicInvokeWebService("webservice url", "OrderService", "SetFinishedByOrderId", 123456, true);            if (affectNum != null)            {                Console.WriteLine((bool)affectNum);//设置订单状态是否完成            }

 

可以想象,这样动态构造web服务并进行调用肯定会有一些性能损失,如果是后台应用系统,对性能要求不是特别高的情况下倒是不妨一试。

最后需要注意的一点,就是因为web服务是动态调用的,所以除了c#的基元类型如int、string、object等等,自定义类型是无法通过上述方法转换回来的,有心的你不妨动手一试。

 

参考:

转载于:https://www.cnblogs.com/jeffwongishandsome/p/some-browsers-GoBack-key-donot-work-well.html

你可能感兴趣的文章
元素定位的八大法则
查看>>
Sublime Text 3 使用小记
查看>>
总结Pycharm里面常用的快捷键
查看>>
util.promisify 的那些事儿
查看>>
配置phpstudy+phpstorm+xdebug环境
查看>>
BZOJ 1079 [SCOI2008]着色方案
查看>>
[Win8.1系统]双系统
查看>>
HDU 3899 树形DP
查看>>
获取当前页面url信息
查看>>
Java容器类源码分析前言之集合框架结构(基于JDK8)
查看>>
linux下C/C++程序的内存布局
查看>>
单词计数问题
查看>>
php 魔术方法 __autoload()
查看>>
js div拖动动画运行轨迹效果
查看>>
Recipe 1.9. Processing a String One Word at a Time
查看>>
Linux 下查看系统是32位 还是64 位的方法
查看>>
MySQL 引擎 和 InnoDB并发控制 简介
查看>>
Dave Python 练习二
查看>>
.net知识体系
查看>>
第二章 第五节 获取帮助
查看>>