通过QueryString传递对象
我有对象A,该对象又具有对象B的类型的属性
Class A property x as Object B End Class
在我的ASP.NET页面上,当我选择一个映射到A型对象的gridview项时,将该对象序列化到QueryString上,并将其传递给下一页。
但是,如果属性x实际上有一些值,因为看起来超过了4k的QueryString容量长度,我就遇到了问题(尽管我认为对象没有那么大)
我已经考虑过以下方法来做到这一点
- 会话变量
未使用的方法,因为我已经读过,这是不好的做法。
- 在对象上使用唯一键,然后在下一页上对其进行检索。
由于对象没有映射到表中的单个实例而未使用的方法,它们是由来自不同数据库的数据组成的。
所以我想我的问题有两个
- 是否值得使用GKZip进一步压缩查询字符串(这可能吗?)
- 人们会建议采用什么其他方法呢?
解决方案
我不明白我们为什么不使用会话状态,但是...
选项1:检视状态
选项2:表单参数而不是querystring
但也请注意,序列化/反序列化时不会返回相同的对象。我们将获得一个新对象,该对象使用已序列化的原始对象的值进行初始化。我们将最终得到两个对象。
编辑:我们可以使用与会话状态相同的语法在viewstate中存储值
ViewState [" key"] = val;
该值必须是可序列化的。
缓存可能也不是这里的答案。正如Telos所述,我不确定我们为什么不考虑会议。
如果我们有一个页面依赖于此数据的可用性,那么我们只需在页面加载中抛出一个保护子句即可。
public void Page_Load() { if(!IsPostBack) { const string key = "FunkyObject"; if(Session[key] == null) Response.Redirect("firstStep.aspx"); var obj = (FunkyObject)Session[key]; DoSomething(obj); } }
如果会话绝对不可行,那么我们必须在另一页上重新实现该对象。只需在查询字符串中发送唯一标识符,即可再次将其拉回。
虽然将对象存储在会话中可能被认为是不好的做法,但比通过序列化查询字符串传递对象好几年。
回到经典的ASP中,在会话中存储对象被认为是不好的做法,因为我们创建了线程亲和性,并且还通过添加其他Web服务器来限制站点扩展的能力。这不再是asp.net的问题(只要我们使用外部状态服务器)。
还有其他避免使用会话变量的原因,但是就我们而言,我认为这是要走的路。
另一个选择是将需要访问该对象的2个页面合并为一个页面,使用面板隐藏和显示所需的"子页面",并使用viewstate存储对象。
我认为在查询字符串中传递它或者将其存储在会话中并不是一个好主意。
我们需要以下之一:
a)缓存层。像Microsoft Velocity这样的工具可以工作,但是我怀疑我们是否需要这种规模的工具。
b)将关键字放在查询字符串中我们需要的数据库中的每个对象上,并在下一次检索它们。 (例如,myurl.com / mypage.aspx?db1objectkey = 123&db2objectkey = 345&db3objectkey = 456)
如果在浏览器中显示下一页的URL没关系,则可以使用context.items集合。
context.items.add("keyA", objectA) server.transfer("nextPage.aspx")
然后在下一页上:
public sub page_load(...) dim objectA as A = ctype(context.items("keyA"), objectA) dim objectB as B = objectA.B end sub
使用此功能的一个原因是,如果我们希望用户相信下一页确实是第一页的一部分。对于他们来说,它仅显示为发生了回发。
同样,如果使用"下一页"的唯一方法是我们首先来自"第一页",那么使用此方法就不需要真正的唯一键。上下文项集合的范围仅特定于此特定请求。
我同意其他海报者的观点,他们提到查询字符串上的序列化对象比使用会话状态要糟糕得多。如果我们确实使用了会话状态,只需记住在使用它后立即清除使用的密钥。
使用会话状态似乎是执行此操作的最实用方法,这正是其设计目的。
这是我的工作:
Page1.aspx添加我的对象的实例的公共属性。添加一个按钮(Button1),其PostBackURL属性设置为〜/ Page2.aspx
Private _RP as ReportParameters Public ReadOnly Property ReportParams() as ReportParameters Get Return _RP End Get End Property Protected Sub Button1_Click(ByVal sender As Object, ByVal e As EventArgs) Handles Button1.Click _RP = New ReportParameters _RP.Name = "Report 1" _RP.Param = "42" End Sub
现在,在第二页上,Page2.aspx在第一条指令下,将以下内容添加到页面顶部的标记中:
<%@ PreviousPageType VirtualPath="~/Default.aspx" %>
然后为Page2.aspx后面的代码中的Page_Load添加以下内容
If Not Page.PreviousPage is Nothing Then Response.write (PreviousPage.ReportParams.Name & " " & PreviousPage.ReportParams.Param) End If
会话并非总是可用。例如,当IE上的XSS(跨站点脚本)安全设置阻止第三方Cookie的存储时。如果在非DNS域的网站的IFrame中调用网站,则默认情况下将阻止cookie。没有Cookie =没有会话。
另一个示例是我们必须将控制权传递给另一个网站,该网站将以纯URL而非帖子的形式回调到网站。在这种情况下,我们必须将会话参数存储在querystring参数中,考虑到4k大小约束和URL编码,更不用说加密等等,这很难做到。
问题在于,大多数内置的序列化方法非常冗长,因此必须诉诸于自己的方法,可能使用反射。
不使用会话的另一个原因仅仅是为了提供更好的用户体验。 N分钟后以及服务器重新启动时,会话将被清除。好的,在这种情况下,最好使用viewstate,但有时无法使用表单。好的,可以依靠JavaScript进行回发,但是,这并非总是可能的。
这些是我目前正在解决的问题。