GET请求和POST请求均是HTTP中最常用的请求协议,而Ajax提供了便捷的方法,getJSON和post就是其中的两个异步请求方法。从名称上就可以看出getJSON方法是专门用来获取JSON数据的异步请求,而post方法也可以通过POST协议向服务器发送各种数据对象。我在最近开发的一个小项目中需要异步请求获取服务的JSON数据对象时也就很自然地想到了这个方法。由于我是刚开始学习使用ASP.NET MVC技术,对于Ajax更是不够了解,所以,难免不能合理的根据使用场景使用合适的方法,下面就来说说我遇到的问题以及我的处理方式。
我的使用场景:
- 项目使用技术框架:ASP.NET MVC4
- 开发工具:VS2013
- 布局页将页面布局分为左侧导航页和右侧内容页,导航页和内容页均为分部视图
- 内容分部视图script脚本中在jQuery的ready方法中直接使用getJSON方法在页面完全加载后获取后台服务器上的JSON数据,由于是很简单的小项目,就直接在视图中编写了脚本,并直接请求控制器中的动作方法
- 内容页有复选框列表和提交按钮以及其他功能所需元素
- 运行浏览器为IE11
遇到的问题(现象):
声明:
为了方便描述,这里简化场景为左侧一个页面导航链接link_A,右侧内容页面中有一个复选框列表checkBoxList,一个提交按钮submit。
功能简单的为:
- 点击link_A后页面导航至当前内容页,此时将会根据配置结果显示checkBoxList的内容,如果之前被设置为选中的项将会被勾选。
- 点击submit按钮将提交checkBoxList的设置结果,并在成功提交后刷新当前页(其中实现方式这里不作描述,以简化描述的问题)。
- 提交的数据对象与获取的数据对象为同一个JSON数据对象,方便起见为其命名为jsonModel
起初使用getJSON方法获取服务端提供JSON数据的动作方法时可以实现基本功能,在成功提交后也能正确显示最后的设置结果(如之前只勾选了选项A,现在勾选的是A和B,提交到后台的也是A和B)。
代码示例:
// GetJSONModel 控制器中用于获取 JSON 数据的动作方法
$.getJSON("@Url.Action("GetJSONModel")", null, OnSuccess);
此时的现象是:
- 点击link_A链接时页面加载完成后得到的jsonModel对象的内容为我最后一次设置的前一个状态(即之前只勾选的选项A);
- 清除浏览器的缓存,然后再次点击link_A链接将会得到正确的结果(即最后一次勾选的A和B均能正确被勾选);
- 直接F5刷新浏览器,重新发送请求也会得到正确的结果(即最后一次勾选的A和B均能正确被勾选)。
原因:如果不清除缓存而直接点击link_A链接时将不调用控制器中的GetJSONModel方法,所以我认为这和缓存有关。可当直接F5刷新时又能够正确显示结果,也会调用GetJSONModel方法(此时未清除缓存),这又说明并不完全是缓存的问题。
解决办法:使用post方法可以满足我这里需要的每一次请求都会调用后台动作方法。示例代码如下:
$.post("@Url.Action("GetJSONModel")", null, OnSuccess);
总结:
这里提供了这种场景下的解决办法,也能清楚的看到这两种请求的在实际使用中的一些效果区别:
- 对于使用getJSON方法发送的GET请求只有在清除缓存后导航或整体刷新页面(如使用F5刷新)时会调用后台动作方法获取最新的数据
- 对于post方法发送的POST请求可以在每一次的请求都会调用后台动作方法获取所需的数据
但是,以我现在对这两个协议的了解还不能解释清楚GET请求所带来的前面现象中描述的那个问题。不过有一点很清楚,就是在项目中使用时一定要注意是否应该使用GET请求,如果不能很好的应用这种请求将会带来很痛苦的教训,甚至灾难性的破坏——因为它是安全的的请求,HTTP中又会肯定其合法性,如果请求的数据会改变服务器的状态,那后果就可想而知了。
这次遗留的关于使用GET请求时并不是什么情况下都可以访问请求的对象的问题希望有一天我能够真正的搞明白,那时候我会进一步阐明其中缘由。当然,如果哪位大神能够指点一二甚好,我也很期待学习。
好了,就到这吧,希望我的问题描述的足够简单、清除,易于理解。
-----------------------------分割线下方将解释之前遗留的问题----------------------------------------
遗留问题释疑:
本文前面描述的现象中存在一个遗留问题,就是关于GET请求不会每一次都会调用后台请求的控制器方法。现在经过查找资料及多次试验,最终弄明白是怎么回事了。其实还是和缓存有关,即对于多数浏览器,都会将请求所获取的数据进行缓存,如果在浏览器会话期间将不再进行请求(所以,这也就是为什么我在重新导航至该请求地址时一直不会访问目标方法,并更像获取的数据——刷新浏览器将会是一次新的会话——这不同于点击导航连接重新访问,所以这样可以)。
解决办法有多种,之前我给出的改用post()方法是其中之一。既然这和浏览器的缓存有关系,所以可以使用Ajax禁用内容缓存来实现;另外,在浏览器会话期间重新访问同一个URL将不再进行请求,那么可以为请求的URL设置一个随机的参数值。详见如下示例:
- 禁用内容缓存:
$.ajax({
type: "GET",
url: "@Url.Action("GetJSONModel")",
cache: false,
success: OnSuccess
});
- 为请求URL提供随机参数值:
$.getJSON("@Url.Action("GetJSONModel")", { random: Math.random() }, OnSuccess);
补充:
好了,现在我想看到这里就能明白是怎么回事并知道该如何处理这个问题了。但是,还是要提醒一下,这也和很多资料和书籍里提到的一样,关于该如何选择GET和POST,在什么时候用哪一种请求更好。这是我们在项目中应该注意的一点。关于这一点的一般说法是:
GET请求用于安全交互,同一个请求可以发起任意多次而不产生额外作用(即副作用)。POST请求则用于不安全交互,提交数据的行为会导致一些状态的改变。我想着样说的话有些人应该还是不太好理解,尤其是像我一样刚学习这一技术的菜鸟——我就很长一段时间被这个“安全”和“非安全”给搞得晕头转向。所以,简单来说就是如果我们需要获取一些只读的数据,尤其是在当前浏览器会话期间内的(不在同一会话期间内、给予同一URL随机参数或禁用缓存的除外)则应该使用GET请求;如果请求所获取的数据是已经发生变化了的,或将会改变程序状态的操作,这应该使用POST请求。如果实在搞不清楚的话,很多大神也会建议我们直接使用POST请求。
之所以将GET应用于获取只读数据的情况下,是因为其为安全交互,将被HTTP服务认为是合法请求,如果这个请求将改变服务器状态,那很有可能会带来灾难性的后果,所以,对于GET请求一定要慎重使用。建议我们如果请求将会带来程序状态的改变最好使用POST请求。