可能一些技术细节涉及到架构实现方案,不过,不影响本意的表达。
跨页面传值(按查询的导出方案)
场景:页面类型:查询条件,查询,导出。
查询条件比较多,且查询内容也可能比较多,如果使用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
View Code
public class LifeSession : IDisposable
{
List keys = new List();
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(string Key, T NewModel)
where T : class
{
if (Key.HasValue() == false)
{
throw new GodError("接收Session值的 Key 值不能为空");
}
XmlDictionary dict = null;
//10 sec
for (int i = 0; i < 100; i++)
{
if (HttpContext.Current.Session[Key] != null)
{
dict = HttpContext.Current.Session[Key] as XmlDictionary;
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:
View Code
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 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 App { get; set; }
static LifeSession()
{
App = new Dictionary();
}
List keys = new List();
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(string Key, T NewModel)
where T : class
{
if (Key.HasValue() == false)
{
throw new GodError("接收Session值的 Key 值不能为空");
}
XmlDictionary 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;
}
}
完毕。