HTTP跳转状态码的研究

一直以来都是直接用PHP的header函数输出一个Location标头来实现跳转。很少深究这个跳转具体是怎么实现的,只知道是PHP看到有Location就生成一个302服务器返回状态码。

今天看了HTTP/1.1的协议标准文档,发现除了302 Found外,可以用于跳转的还有301 Moved Permanently、303 See Other以及307 Redirect Temporary。那么这几个状态码有什么不同呢?

顾名思义,301是实现永久跳转的。服务器返回这个状态码,相当于告诉来访者目标地址已经永久转移了,转移后的目标地址用Location标头来描述。由于是永久转移,中间的代理服务器和浏览器都可以缓存这个转移后的目标地址,以后用户再访问的时候,可以不把Request发到服务器上,直接导向目标地址。

302 Found的本意是通知浏览器去请求另一个资源,在HTTP/1.0的定义中,浏览器收到302返回码后,应该用和原请求方法相同的方法来请求目标地址。也就是说,如果浏览器POST到login.php上,而login.php最终返回302,并在Location标头中指定index.php,浏览器就应该再发个POST请求到index.php上。

但是实际情况是,早期版本的浏览器都会用GET方法来发起第二次请求。而早期的Web开发人员也在无数的地方写了大量的302返回码,目的是为了让浏览器跳转并用GET用GET访问。所以最终HTTP/1.1将错就错,加入了303 See Other和307 Redirect Temporary两个状态码。

303 See Other要求用GET方法访问Location中指定的地址。代理服务器或者浏览器可以根据返回标头中的缓存策略对结果加以缓存。现今的大多数浏览器使用处理303的方法来处理302,以保证向前兼容。而反过来,为了兼容早期的浏览器,Web开发人员应该尽量使用302来实现临时重定向。

307 Redirect Temporary就是当年真正的302了,它要求浏览器以完全相同的方法请求Location中指定的地址,而且绝对不可以缓存请求的结果。

现在应该很少见只支持HTTP/1.0的浏览器了,不过网上大量的爬虫程序未必能完整的实现HTTP/1.1,所以平时开发的时候还是多用301和302吧。如果遇到真的需要浏览器重新POST到新地址的情况,用一下307应该也不是问题。

Continue Reading

Webkit的OfflineApplicationCache通配符支持的陷阱

最近在写Web Offline Application的网页。据MDN的说法,manifest文件的NETWORK部分是可以使用通配符的。我希望页面的Ajax请求都是连网发送的,所以写了一个这样的manifest文件

CACHE MANIFEST
CACHE:
css/common.css
css/index.css
js/jquery-min.js
js/index.js

NETWORK:
*.php

#2013-04-15T19:38:24+08:00

实际使用的时候,Mozilla Firefox下工作良好,但Google Chrome下Ajax请求全都会出错。后来找了很多资料,才发现Webkit对NETWORK部分的处理根本就没做通配符处理。当一个请求发起时,Webkit的浏览器会先看看NETWORK部分有没有完全相同的,如果有就直接连网发送;如果没有则会看NETWROK部分有没有单独的*,没有就直接返回加载错误。最后只好把manifest改成

CACHE MANIFEST
CACHE:
css/common.css
css/index.css
js/jquery-min.js
js/index.js

NETWORK:
*

#2013-04-15T21:14:51+08:00

这样一来所有manifest中不存在的资源都会联网加载。

Continue Reading

解决在git的post-receive钩子中无法checkout的问题


Warning: wp_codebox_substitute(): Argument #1 ($match) must be passed by reference, value given in /web/rek.me/rek/webroot/wp-content/plugins/wp-codebox/main.php on line 125

Fatal error: Uncaught TypeError: count(): Argument #1 ($value) must be of type Countable|array, null given in /web/rek.me/rek/webroot/wp-content/plugins/wp-codebox/main.php:31 Stack trace: #0 [internal function]: wp_codebox_substitute(Array) #1 /web/rek.me/rek/webroot/wp-content/plugins/wp-codebox/main.php(125): preg_replace_callback('/\\s*<pre(?:lang...', 'wp_codebox_subs...', '<p style="text-...') #2 /web/rek.me/rek/webroot/wp-includes/class-wp-hook.php(324): wp_codebox_before_filter('<p style="text-...') #3 /web/rek.me/rek/webroot/wp-includes/plugin.php(205): WP_Hook->apply_filters('<p style="text-...', Array) #4 /web/rek.me/rek/webroot/wp-includes/post-template.php(256): apply_filters('the_content', '<p style="text-...') #5 /web/rek.me/rek/webroot/wp-content/themes/olsen-light/content-entry.php(59): the_content('') #6 /web/rek.me/rek/webroot/wp-includes/template.php(812): require('/web/rek.me/rek...') #7 /web/rek.me/rek/webroot/wp-includes/template.php(745): load_template('/web/rek.me/rek...', false, Array) #8 /web/rek.me/rek/webroot/wp-includes/general-template.php(206): locate_template(Array, true, false, Array) #9 /web/rek.me/rek/webroot/wp-content/themes/olsen-light/index.php(55): get_template_part('content', 'entry') #10 /web/rek.me/rek/webroot/wp-includes/template-loader.php(106): include('/web/rek.me/rek...') #11 /web/rek.me/rek/webroot/wp-blog-header.php(19): require_once('/web/rek.me/rek...') #12 /web/rek.me/rek/webroot/index.php(17): require('/web/rek.me/rek...') #13 {main} thrown in /web/rek.me/rek/webroot/wp-content/plugins/wp-codebox/main.php on line 31