标点符(钱魏 Way)

PHP最佳实践:PRG模式

网上有非常多的PHP最佳实践的技巧,有些已经过期,但是有些还是非常的实用,期望能够整理一篇较为完整的PHP最佳实践,供自己学习和复习。

Post/Redirect/Get 简称PRG,是一种用来防止表单重复提交数据的一种Web设计模式,像用户刷新提交响应页面这样典型的重复提交form内容的情况可通过PRG模式来得到避免。

post_redirect_get

用户在浏览器中的A页面完成输入,点击了提交按钮进入到了B页面,提示”提交成功”。若分别在B页面出现下列二种情况:

  1. 用户刷新或者重新加载B页面;
  2. 用户点击浏览器的后退按钮后继续店家前进按钮;

三种情况都会造成表单重复提交问题。解决问题的一种思路就是将用户点击提交按钮从而发出POST请求, 页面提交跳转和显示结果页面这三个行为分离开。Client用POST方法请求Server响应数据变更,Server用Redirect方法将response指定到另一个URL上的结果页面,Client所有对页面显示的请求都用GET方法告知Server。这样,”后退再前进”或刷新页面的行为都发出的是GET请求, 从而不会对server产生任何数据更改的影响。

PRG模式通过响应页面Header返回HTTP状态码进行页面跳转替代响应页面跳转过程。HTTP 1.1 规范介绍HTTP 303U状态页进行跳转,303状态能确保会员在浏览器端安全地刷新服务器端响应,而不会引起HTTP POST请求重复提交。目前很多网站依然使用HTTP 302来响应跳转,主要考虑到一些版本的浏览器不能很好地兼容HTTP1.1规范中的303状态码。

具体实现方式如下:

HTTP规范摘录:

302 Found

请求的资源现在临时从不同的URI响应请求。由于这样的重定向是临时的,客户端应当继续向原有地址发送以后的请求。只有在Cache-Control或Expires中进行了指定的情况下,这个响应才是可缓存的。

新的临时性的URI应当在响应的Location域中返回。除非这是一个HEAD请求,否则响应的实体中应当包含指向新的URI的超链接及简短说明。

如果这不是一个GET或者HEAD请求,那么浏览器禁止自动进行重定向,除非得到用户的确认,因为请求的条件可能因此发生变化。

注意:虽然RFC 1945和RFC 2068规范不允许客户端在重定向时改变请求的方法,但是很多现存的浏览器将302响应视作为303响应,并且使用GET方式访问在Location中规定的URI,而无视原先请求的方法。状态码303和307被添加了进来,用以明确服务器期待客户端进行何种反应。

303 See Other

对应当前请求的响应可以在另一个URI上被找到,而且客户端应当采用GET的方式访问那个资源。这个方法的存在主要是为了允许由脚本激活的POST请求输出重定向到一个新的资源。这个新的URI不是原始资源的替代引用。同时,303响应禁止被缓存。当然,第二个请求(重定向)可能被缓存。

新的URI应当在响应的Location域中返回。除非这是一个HEAD请求,否则响应的实体中应当包含指向新的URI的超链接及简短说明。

注意:许多HTTP/1.1版以前的浏览器不能正确理解303状态。如果需要考虑与这些浏览器之间的互动,302状态码应该可以胜任,因为大多数的浏览器处理302响应时的方式恰恰就是上述规范要求客户端处理303响应时应当做的。

需要得注意的是,PRG设计模式并不能适用所有的表单重复提交情况,如以下几种情况:

  1. 如果用户返回表单页面,重新提交表单的情况
  2. 用户在服务器端响应到达之前,多次点击提交按钮的时候。(可通过JavaScript控制提交按钮点击次数)
  3. 由于服务器响应缓慢,用户刷新提交POST请求造成的多次POST请求
  4. 恶意用户避开客户端预防多次提交手段,进行重复提交请求

除了PRG设计模式外,另外还有一些其他技术被用在防止表单重复提交的情况下,如客户端我们可以采取JavaScript防止用户多次点击提交按钮,还可以采用Session记录用户当前提交行为等。不管采用何种语言(PHP、ASP.NET、Python等)开发网站,都建议使用PRG来避免用户在刷新浏览器的时候重复的提交数据。

参考链接:http://en.wikipedia.org/wiki/Post/Redirect/Get

码字很辛苦,转载请注明来自标点符《PHP最佳实践:PRG模式》

评论