解决ie8的跨站请求问题

由于工作的需要,最近开始学前端。果然实践出真知,感觉这几天学到了不少东西。

需求是这样的,需要在主站上挂载一个BBS,而BBS服务是在另一个域名下的,数据的提交和请求都涉及到跨站。

jsonp是解决这类问题的一种常用方法,但是这种方法有个缺点,由于jsonp实际是是用的GET请求,参数是编码在URL中的,而浏览器对URL支持的长度是有限的,最坑爹的IE支持的长度仅为2083字符,过长的URL也会被nginx拒绝的。内容稍长一些,这个限制就会达到,于是只能使用POST了。

最后采用的办法是,通过给服务器返回值中加上“Access-Control-Allow-Origin” header 来允许跨站请求。这个很快实现了,在Chrome(我最喜欢的浏览器)和Firefox下测试通过,但是IE8就不行了,原因是ie8不支持XMLHttpRequest,导致ajax请求无效,好在它支持另一种协议,XDomainRequest,通过这个扩展 https://github.com/MoonScript/jQuery-ajaxTransport-XDomainRequest ,代码不用修改一行,就可以正常在IE8下运行了。这时,GET请求可以用了,但是在测试发帖时却发现,服务器获取不到POST的内容,通过IE的开发者工具模拟IE8,IE9均无法正常POST,IE10却又可以了,我们将IE8和IE10的请求头作对比,发现IE8发出的请求没有定义Content-Type,我们的服务器是用的是Tornado框架,经过翻阅源码,找到了这里

def parse_body_arguments(content_type, body, arguments, files):
    """Parses a form request body.

    Supports ``application/x-www-form-urlencoded`` and
    ``multipart/form-data``.  The ``content_type`` parameter should be
    a string and ``body`` should be a byte string.  The ``arguments``
    and ``files`` parameters are dictionaries that will be updated
    with the parsed contents.
    """
    if content_type.startswith("application/x-www-form-urlencoded"):
        uri_arguments = parse_qs_bytes(native_str(body), keep_blank_values=True)
        for name, values in uri_arguments.items():
            if values:
                arguments.setdefault(name, []).extend(values)
    elif content_type.startswith("multipart/form-data"):
        fields = content_type.split(";")
        for field in fields:
            k, sep, v = field.strip().partition("=")
            if k == "boundary" and v:
                parse_multipart_form_data(utf8(v), body, arguments, files)
                break
        else:
            gen_log.warning("Invalid multipart/form-data")

原来,tornado对不同Content-Type的POST内容解析方法是不一样的,到这里,问题就解决了,可以在ajax请求时自己附带上Content-Type值(未测试),或者hack tornado,设置默认的Content-Type。最后说一句,珍爱生命,远离IE。

2013-06-27 22:36