iframe实现跨域post请求的技术细节

在最近的一个项目中,我打算在页面上实现这样一个功能:
在网页上画出某种图形,上传到服务器后,返回一个src地址。这个地址可以用来分享到各种社交媒体。

这个功能看似非常简单,但要实现它还需要注意各种小的细节。

首先说下思路和技术要点:

  • 用canvas来实现页面的画图功能,利用canvas的toDataURL方法可以很方便地将画布的数据保存为dataURIs
  • 用jQuery的ajax方法将图像数据异步提交到后台,在后台处理上传后,返回一个地址给前端调用。

按照这个思路马上就用jQuery.get()实现了一个测试用例。但结果非常让人失望!下面就是我遇到的各种问题:

  1. post OR get

首先是请求方式问题。

我们知道,http的get请求是利用url发送给服务器请求,受制于字节数限制,无法发送大的数据量。jquery中的get方法是其ajax方法的一个封装,基本原理仍是利用XMLHttpRequest对象发送了一个get请求,要发送图片这种较大的数据,用get方式显然力不从心,所以必须用post方式提交。

心想这么简单,于是把get改成post,over!
但结果失败了,因为这里遇到了另一个很严重的问题。

  1. 跨域提交(如果你是同域上传的话,这一段可以忽略了)

jQuery中的post方法在同域的情况下,可以顺利地以异步的方式提交form 数据,并用返回一个json结果。
所以,在同域的情况下,你可以这样
$.post("url",form1.serialize(),function(json){console.log(json)},'json');
服务器将处理后的结果用json形式返回给前端。

但在我的工作环境中,跨域是很常见的。而且我也希望能将这个功能组件化,让它在不同的子域中调用。
这样就必须要用到跨域的方式,于是很自然地想到jQuery中的jsonp。而且在jquery官方的ajax方法文档中,写明了dataType的值可以是"jsonp"。
于是又去改了一通,点了该死的按钮以后返回的是"HTTP 414 Request-URI too long"!God!
原来jQuery的post方法在跨域提交时会自动转换为GET方式,此时提交的数据已经超过了GET方式的请求字节限制!

这个时候,虽然有些气急败坏,但终于找到症结所在,就是下面要说的问题:

  1. 如何在跨域的情况下异步发送post请求?

这里要说明一个情况,就是为什么要用异步?
因为在我之前的许多项目中,大多都是在页面上发送一个异步请求,把服务器的结果实时地展示到页面上来,而不必刷新整个页面。当然会很自然地想到这样的实践方式。

我没有考虑到的是,之前的这些实践都是发送的get请求。当发送get请求时,如果是跨域,利用XMLHttpRequest对象发送get请求时可以用jsonp的方式跟服务器配合取回结果;如果是同域,那就更没有问题了。

但这次要上传图像数据,只能发送post请求,而且要异步实现。这可就犯难了。
好在守着互联网这个宝库,稍微搜索了一番以后,终于发现了一些有用的东西。

首先是这篇:POST跨域问题

看了这个后,知道了异步的情况下是不能跨域发送post请求的。想想也是,你在自己的网站上随便form发送了一大段数据到别人的网站,人家不搭理你就不错了,还要人家给你返回个“哥俩好”,可能么?

安全很重要,但我自己的两个子域虽然是跨域,但总归是一家子。一家人怎么才能不说两家话呢?

来看这个:跨域post请求实现方案小结
文档中列举出了目前实现跨域请求很多方法,如CORS、invisible iframe、server proxy、flash proxy。

还有一些文章列出了HTML5 WebSocket方法,但这种方式目前还不是标准,或者还没有流行起来,要想让功能以比较稳定的方式运行,最好还是用当下主流的方式。于是决定用iframe来实现。

  1. iframe实现跨域提交的原理

iframe进行post“跨域”无刷新提交原理

看了这个图以后,基本上思路就明晰了。

我们用异步的目的是实现无刷新,既然post不能跨域异步提交,并且不异步也可以无刷新,那我们就不异步。我把form提交到本页面的一个空iframe中,这样不会造成页面的刷新。让服务器处理完后跳转到同域下的upload_result网页里,并给这个网页的url附加处理的结果。这样原始请求页的空iframe就加载了服务器跳转的那个页面的内容。同时,虽然这个upload_result页虽然跟我的原始页面不在一个子域,但我可以人为地设置它们的document.domain为同一个父域,当这个upload_result页面在我的原始请求页加载时,就可以执行父页面的callback函数。

  1. 实现步骤

明白了基本的原理,剩下的就是体力活了,下面看看怎么构建这样一个机制。

  • 首先在服务器端写好一个upload_result.html,里面用js解析出页面URL附带的信息。其中包括两个最重要的信息,服务器上传图片后的地址和原始页面请求的回调函数名
  • 在服务器端写好upload脚本,处理前端post过来的数据,最重要的两个域对应地是文件信息(这里是二进制数据)和回调函数名称。当处理完成图片上传后,用header("Location: upload_result.html?paramString")跳转到upload_result页。其中paramString包含了url格式的图像地址和回调函数名称键值对。
    需要注意的是,传给服务器端的数据是base64编码的,需要先解码才能保存。
  • 前端页面需要构造的元素如下:
    • 一个回调函数,比如upload_callback,此函数须能让iframe的页面中访问到。
    • 一个空的iframe
    • 一个隐藏的form。给这个form设置action为服务器处理地址,target为空iframe的name/id。再在这个form 中设置两个个隐藏的input:一个用来接收canvas图像数据,另一个用来接收页面的回调函数名。

这样,当用户在canvas画完图,点击upload按钮时,把canvas的信息和回调函数名赋值给form,触发form提交。结果返回给空iframe,iframe 解析自身的返回结果,并触发父页面的callback函数。于是实现了页面的无刷新跨域post提交。Good!

参考:

  1. JSONP跨域的原理解析
  2. 使用 HTML5 WebSocket 构建实时 Web 应用
  3. JSONP与POST方式请求
  4. 利用iframe进行post“跨域”无刷新提交
  5. js利用form+iframe解决跨域post和get提交
  6. ajax跨域、iframe跨域和JS跨域通信的几种解决方案
  7. php读取和保存base64编码的图片内容

延伸

实现了canvas二进制图像的跨域上传和返回,那么在此基础上还可以做些更好玩的:比如图像编辑。
功能很简单:用户打开本地的图片文件并加载到canvas中,在画布上进行各种编辑,完了上传到服务器,并把返回的地址分享出去。
这里要用到的一个新东西就是HTML5的FileReader对象。利用它可以实现如本地预览等功能,我们用它来把选中的本地文件加载到canvas中,编辑后上传。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342

推荐阅读更多精彩内容

  • 由于浏览器的同源策略保护机制,浏览器不能执行来自其他来源的脚本。通过js在不同的域之间进行数据传输或通信,比如用a...
    威少_吴阅读 1,373评论 0 2
  • 1. 什么是跨域? 跨域一词从字面意思看,就是跨域名嘛,但实际上跨域的范围绝对不止那么狭隘。具体概念如下:只要协议...
    w_zhuan阅读 510评论 0 0
  • 1. 什么是跨域? 跨域一词从字面意思看,就是跨域名嘛,但实际上跨域的范围绝对不止那么狭隘。具体概念如下:只要协议...
    他在发呆阅读 822评论 0 0
  • 我和HY初见面成为同桌后了一段时间,他打死也不相信,我是个内向的小孩。 期末,老师发张自我评价的纸,让我们自己写,...
    彼岸青园阅读 204评论 0 0
  • 咱们先聊聊题外话,为什么说ajax目前还是应用非常广泛的呢在以后也是一个强大的通信工具? 目前市场分为CS...
    哈哈腾飞阅读 1,239评论 0 2