可能一些技术细节涉及到架构实现方案,不过,不影响本意的表达。
跨页面传值(按查询的导出方案)
场景:页面类型:查询条件,查询,导出。
查询条件比较多,且查询内容也可能比较多,如果使用URL传值的话,可能存在URL超长截断的可能。
原理:
1. 问题:在回调(Ajax或timer)里执行 document.location 或 window.open 方法,浏览器会阻止下载(有提示)。要解决,如何把条件 Post 到服务器端,再执行 window.location 方法而不报阻止 。
2. 导出时,先使用 Ajax 把查询Model 传递到服务器,保存在Session 里。
3. 执行 window.location 跳转。
4. 跳转页面检查Session 里的查询Model,如果不存在,则进行阻塞等待,等待最大时间 3秒。 如果超时,则抛出错误,如果3秒内得到 Ajax 传递回来的查询Model,则继续执行。
实现方案:
1. 客户端传值,先调用 jv.saveSession , 再弹出页面传递参数 : QueryKey。
jv.saveSession("ParkingInfo", $(".divQuery:last", jv.boxdy()).GetDivJson("List_")); jv.PopList({ url: "~/ReportWeb/Parking/ParkingInfo.aspx?QueryKey=ParkingInfo", entity: "Notices" }, null); |
2. 服务器端接收(只能接收一次)
var qModel = LifeSession.OnceGetQuery(Request.QueryString["QueryKey"], new ParkingBiz.ParkingQuery()); if (qModel == null) { Response.Write("接收参数时失败,请重试,或联系系统管理员。"); Response.End(); } |
当然 LifeSession
public class LifeSession : IDisposable { List<string> keys = new List<string>(); public object this[string SessionKey] { get { return HttpContext.Current.Session[SessionKey]; } set { GodError.Check(value == null, () => "数据接收异常"); keys.Add(SessionKey); HttpContext.Current.Session[SessionKey] = value; } } public void Dispose() { keys.All(o => { HttpContext.Current.Session.Remove(o); return true; }); } public static T OnceGetQuery<T>(string Key, T NewModel) where T : class { if (Key.HasValue() == false) { throw new GodError("接收Session值的 Key 值不能为空"); } XmlDictionary<string, string> dict = null; //10 sec for (int i = 0; i < 100; i++) { if (HttpContext.Current.Session[Key] != null) { dict = HttpContext.Current.Session[Key] as XmlDictionary<string, string>; if (dict == null) { var err = "数据接收异常 :" + HttpContext.Current.Session[Key].GetType().FullName; LogInfo.To(InfoEnum.Error, MySessionKey.UserName.Get(), err); throw new GodError(err); } HttpContext.Current.Session.Remove(Key); break; } else { Thread.Sleep(100); } } if (dict == null) return null; var retVal = dict.DictionaryToModel(NewModel); GodError.Check(retVal == null, () => "Session项数据 : " + Key + "转换失败, 数据:" + dict.ToJson() + ",转换目标类型:" + typeof(T).FullName); return retVal; } }
js:
jv.saveSession = function (sessionKey, jsonData) { if (!sessionKey) { alert("请输入 SessionKey "); return false; } if (!jsonData) { alert("请输入 JsonData "); return false; } $.post(jv.Root + "Master/Home/SaveSession/" + sessionKey + ".aspx", jsonData, function (res) { if (res.msg) { alert(res.msg) }; }); return true; };
执行流程
点击导出后,其流程图是这样的:
1. Client ----------------------------------------------IIS加工并赋Session----> 保存Session
2. Client ----------------------IIS加工并赋Session-----> 取Session
其中: 1和2 到达服务器时间不确定,上图只说明其中一种特例出错的情况.
出现的问题
当Session超时后,再执行导出,取不到 Session , 调试发现: 当Session 超时后, 保存 Session 时, Session 的 IsNewSession 是 true .但这不重要. 重要的是,它们是两个线程,当第2 个线程早到的时候,先执行了Session 的赋值,也就是说已过了IIS赋Session的时间,再也取不到 1号线程赋的Session, 这两个线程之间不能共享值.
解决方法
因为要清空数据,所以可以换用 全局静态变量 public static Dictionary<string,StringDict> App {get;set;} 来保存 Session 值。 其Key 是 SessionId 和 原SessionKey 在 OnceGetQuery 时,清除该Key值,即可。
改造后的代码:
[HttpPost] [ValidateInput(false)] public ActionResult SaveSession(string uid, FormCollection query) { if (uid.HasValue()) { LifeSession.App[Session.SessionID + "_" + uid.Trim()] = query.ToStringDict(); } return new JsonMsg(); }
public class LifeSession : IDisposable { public static Dictionary<string, StringDict> App { get; set; } static LifeSession() { App = new Dictionary<string, StringDict>(); } List<string> keys = new List<string>(); public object this[string SessionKey] { get { return HttpContext.Current.Session[SessionKey]; } set { GodError.Check(value == null, () => "数据接收异常"); keys.Add(SessionKey); HttpContext.Current.Session[SessionKey] = value; } } public void Dispose() { keys.All(o => { HttpContext.Current.Session.Remove(o); return true; }); } public static T OnceGetQuery<T>(string Key, T NewModel) where T : class { if (Key.HasValue() == false) { throw new GodError("接收Session值的 Key 值不能为空"); } XmlDictionary<string, string> dict = null; Key = HttpContext.Current.Session.SessionID + "_" + Key; //10 sec for (int i = 0; i < 100; i++) { if (App.ContainsKey(Key)) { dict = App[Key] as StringDict; if (dict == null) { var err = "数据接收异常 :" + App[Key].GetType().FullName; LogInfo.To(InfoEnum.Error, MySessionKey.UserName.Get(), err); throw new GodError(err); } App.Remove(Key); break; } else { Thread.Sleep(100); } } if (dict == null) return null; var retVal = dict.DictionaryToModel(NewModel); GodError.Check(retVal == null, () => "Session项数据 : " + Key + "转换失败, 数据:" + dict.ToJson() + ",转换目标类型:" + typeof(T).FullName); return retVal; } }
完毕。