jquery.form 和跨域请求

Posted

技术标签:

【中文标题】jquery.form 和跨域请求【英文标题】:jquery.form and cross-domain requests 【发布时间】:2011-07-01 06:34:54 【问题描述】:

我很难通过跨域请求创建jquery.form。我在使用 Firefox 和 Chrome 时遇到问题(甚至还没有尝试过 IE)。

解释:我的整个网站都位于http://www.mysite.com 内。但是,我的联系表格在另一台服务器上,由 http://contact.mysite.com 引用。我认为将它放在子域上会回避有关跨域请求的问题,但显然事实并非如此。 http://contact.mysite.com 在 Sinatra 中实现。

我的 javascript 设置没有什么花哨的。表单的action指向http://contact.mysite.com,方法是POST:

<form id="contact" action="http://contact.mysite.com/" method="post">

jquery.form 配置了ajaxForm 调用:

$(document).ready(function() 

  $('#contact').ajaxForm(
    success: function()  $('#success').fadeIn("slow"); ,
    error: function()   $('#error').fadeIn("slow"); 
  );

);

我遇到的第一个问题是 Firefox 3.5 - 显然它发送了一个 OPTIONS 请求,期望服务器提供特定的答案。我使用this question 来配置我的 Sinatra 应用程序,使其达到预期效果(似乎更新版本的 sinatra 包含一个选项动词):

require 'rubygems'
require 'sinatra'
require 'pony'

# patch sinatra so it handles options requests - see https://***.com/questions/4351904/sinatra-options-http-verb
configure do
  class << Sinatra::Base
    def options(path, opts=, &block)
      route 'OPTIONS', path, opts, &block
    end
  end
  Sinatra::Delegator.delegate :options
end

# respond to options requests so that firefox can do cross-domain ajax requests
options '/' do
  response['Access-Control-Allow-Origin'] = '*'
  response['Access-Control-Allow-Methods'] = 'POST'
  response['Access-Control-Max-Age'] = '2592000'
end

post '/' do
  # use Pony to send an email
  Pony.mail(...)
end

使用 jquery 1.4.3,我在 firebug 上看到一个 OPTIONS 请求,然后是一个 POST 请求(状态 200。电子邮件已发送)。使用 jquery 1.3.2 或 1.5,仅显示 OPTIONS 请求(未发送电子邮件)。

尽管如此,error 回调总是会在我尝试过的所有版本的 jquery 中触发。我将其追溯到$.ajax(...) 调用,所以我不确定这个问题是来自 jquery.form 还是 jquery 本身。

我尝试注销来自错误的信息:

$('#contact').ajaxForm(
  success: function()  $('#success').fadeIn("slow"); ,
  error: function(jqXHR, textStatus, errorThrown) 
    console.log(jqXHR.status);
    console.log(jqXHR.statusText);
  
); 

jquery 1.4.3 上的输出(在发送 OPTIONS 和 POST 请求后,状态均为 200):

0
(empty string)

jquery 1.5 上的输出(在 OPTIONS 返回 200 状态后;从不发送 POST)

302
error

我真的迷路了。

是否有插件可以处理这个问题? 我是否在某处遗漏了什么?

任何帮助将不胜感激。

【问题讨论】:

【参考方案1】:

AJAX 请求无法跨域执行(UPD: 不再适用,所有现代浏览器都支持CORS),但您可以改用 JSONP。虽然 JSONP 可以跨域工作,但它不能用于 POST 请求,您需要将表单的方法更改为 get 并使用:

$('#contact').ajaxForm(
  success: function()  $('#success').fadeIn("slow"); ,
  error: function()   $('#error').fadeIn("slow"); ,
  dataType: 'jsonp'
);

上述解决方案依赖于您的服务器以有效的 jsonp 响应进行响应,否则将不会执行 success 处理程序。例如:response.write(request.callback + '(' + result.to_json + ')')


最新版本的 jQuery 可以在没有 ajaxForm 插件的情况下序列化表单。如果你不需要文件上传,你可以使用这个:

$('form').submit(function() 
  var url = $(this).attr('action')
  var params = $(this).serialize()
  $.getJSON(url + '?' + params + "&callback=?", function(data) 
    // success
  )
  return false
);

【讨论】:

感谢您的回答。我仍在努力解决这个 jsonp 业务。一个问题认为: $.ajax 似乎能够执行 jsonp 请求(或者它在文档中说,我仍然不确定它是如何工作的)。既然jquery.form内部使用了$.ajax,那它不也可以使用jsonp吗? 你知道吗,我查看了 $.ajaxForm 源代码,我认为它可以做到。我现在无法尝试该示例,但我希望它有效。 查看我的新更新,我测试过,如果您从服务器返回有效的 json 响应,它可以正常工作。 JSONP 通过将 您的解决方案效果很好。我最终放弃了 jquery.form 并直接使用 getJSON(你需要用+ '?' + 替换+ '/' +;我已经在你的答案中改变了)【参考方案2】:

我认为 JSONP 是唯一可以跨域的 AJAX 请求。

http://en.wikipedia.org/wiki/JSON#JSONP

【讨论】:

感谢您的回答。这不是一个完整的答案,但至少给了我一个提示。我会调查 JSONP。【参考方案3】:

您还可以使用本地代理 URL 来执行请求,因为服务器通常可以使用 HttpRequest 或 cURL 等方式进行跨域调用。所以基本上你使用 ajax 对本地域上的 URL 进行调用,然后将请求转发到跨域 URL,并将来自 HttpRequest/cURL 的响应在本地域的响应中传递回浏览器。

【讨论】:

这不是我当前设置的选项。服务于主应用程序的服务器不受我的控制。无论如何感谢您的回答。【参考方案4】:

经过大量的战斗,在 Alexey 的帮助下,我终于战胜了它。这是我现在的解决方案:

Javascript(直接使用jquery,不用jquery.form):

$(document).ready(function() 
  $('#contact').submit(function() 
    $('#success').fadeOut("slow");
    $('#bademail').fadeOut("slow");

    var url = $(this).attr('action')
    var params = $(this).serialize()
    $.getJSON(url + '?' + params + "&callback=?", function(data) 
      if(data == true)  // success
        $('#success').fadeIn("slow");
        $('#contact')[0].reset();
       else  // error
        $('#bademail').fadeIn("slow");
      
    );

    return false;
  );
);

对于 Sinatra,我使用了 sinatra-jsonp gem。我让 get 操作返回“true”或“false”,具体取决于是否可以发送电子邮件(例如,对于无效的电子邮件地址)。

require 'rubygems'
require 'sinatra'
require 'sinatra/jsonp'
require 'pony'


get '/' do

  # check for blanks, etc
  return jsonp false unless fields_valid(params)

  Pony.mail(
    ...
  )

  return jsonp true

end

【讨论】:

以上是关于jquery.form 和跨域请求的主要内容,如果未能解决你的问题,请参考以下文章

jQuery 和跨域 POST 请求

浅谈jquery中的ajax请求和跨域请求

AngularJS和跨域请求被阻止[关闭]

Laravel解决预请求和跨域的问题

一篇搞定vue请求和跨域

获取 API、自定义请求标头、CORS 和跨域重定向