【问题标题】:Pass additional parameter to a JSONP callback将附加参数传递给 JSONP 回调
【发布时间】:2012-03-23 04:02:05
【问题描述】:

对于我的一个项目,我需要使用 JSONP 对(远程)API 进行多次调用以处理 API 响应。所有调用都使用相同的回调函数。所有调用都是在客户端使用 JavaScript 动态生成的。

问题如下:如何将附加参数传递给该回调函数,以便告诉该函数我使用的请求参数。因此,例如,在以下示例中,我需要 myCallback 函数来了解 id=123

<script src="http://remote.host.com/api?id=123&jsonp=myCallback"></script>

有没有什么方法可以实现这一点,而不必为我的每个调用创建单独的回调函数?首选原生 JavaScript 解决方案。

编辑

在第一次遇到并回答之后,出现了以下几点:

  • 我对远程服务器没有任何控制权。因此,将参数添加到响应中不是一种选择。
  • 我同时启动了多个请求,因此存储我的参数的任何变量都不能解决问题。
  • 我知道,我可以动态创建多个回调并分配它们。但问题是,我是否能以某种方式避免这种情况。如果没有其他解决方案弹出,这将是我的后备计划。

【问题讨论】:

  • 你能让服务器返回 id 作为响应的一部分吗?服务器知道它,所以它可以为你把它放在响应代码中。
  • 您正在做需要做的事情。剩下的就由远程主机来识别你的参数了。
  • 可能是的.... jQuery 会让这个 very 变得简单,但如果你不想包含它,我理解。
  • @jfriend00 我无法控制远程服务器,所以那里没有变化
  • JSONP调用是代码动态生成的吗?还是静态包含在您的页面中?如果是通过代码动态生成的,还有更多的选择。

标签: javascript jsonp


【解决方案1】:

有一个更简单的方法。 在“?”之后将参数附加到您的网址。并在回调函数中访问如下。

var url = "yourURL";
    url += "?"+"yourparameter";
    $.jsonp({
        url: url,
        cache: true,
        callbackParameter: "callback",
        callback: "cb",
        success: onreceive,
        error: function () {
            console.log("data error");
        }
    });

以及回调函数如下

function onreceive(response,temp,k){
  var data = k.url.split("?");
  alert(data[1]);   //gives out your parameter
 }

注意:如果 URL 中已有其他参数,则可以在 URL 中以更好的方式附加参数。我在这里展示了一个快速肮脏的解决方案。

【讨论】:

    【解决方案2】:

    您的选择如下:

    1. 让服务器将 ID 放入响应中。这是最干净的,但通常您无法更改服务器代码。
    2. 如果您可以保证一次不会有多个 JSONP 调用涉及 ID inflight,那么您可以将 ID 值填充到全局变量中,并在调用回调时从全局变量中获取 id 值.这很简单,但很脆弱,因为如果同时有多个 JSONP 调用涉及正在处理的 ID,它们将相互踩踏,某些事情将无法正常工作。
    3. 为每个 JSONP 调用生成一个唯一的函数名称,并使用与该函数名称关联的函数闭包将 id 连接到回调。

    这是第三个选项的示例。

    您可以使用闭包为您跟踪变量,但由于您可以同时进行多个 JSON 调用,您必须使用动态生成的全局可访问函数名称,该名称对于每个连续的 JSONP 都是唯一的称呼。它可以像这样工作:

    假设您为 JSONP 生成标签的函数是这样的(您可以替换您现在使用的任何内容):

    function doJSONP(url, callbackFuncName) {
       var fullURL = url + "&" + callbackFuncName;
       // generate the script tag here
    }
    

    然后,您可以在它之外有另一个函数来执行此操作:

    // global var
    var jsonpCallbacks = {cntr: 0};
    
    
    function getDataForId(url, id, fn) {
        // create a globally unique function name
        var name = "fn" + jsonpCallbacks.cntr++;
    
        // put that function in a globally accessible place for JSONP to call
        jsonpCallbacks[name] = function() {
            // upon success, remove the name
            delete jsonpCallbacks[name];
            // now call the desired callback internally and pass it the id
            var args = Array.prototype.slice.call(arguments);
            args.unshift(id);
            fn.apply(this, args);
        }
    
        doJSONP(url, "jsonpCallbacks." + name);
    }
    

    您的主代码将调用getDataForId(),传递给它的回调将像这样传递 id 值,然后是 JSONP 在函数上具有的任何其他参数:

    getDataForId(123, "http://remote.host.com/api?id=123", function(id, /* other args here*/) {
        // you can process the returned data here with id available as the argument
    });
    

    【讨论】:

    • 也就是说,我想到的后备计划。然而,这意味着创建(尽管是动态的)多个回调函数,这是我想避免的。
    • @Sirko - 除非你能保证你一次只会有一个这种类型的 JSONP 请求,你必须做这样的事情来匹配请求的状态返回。根据定义,JSONP 非常愚蠢。它所做的只是调用一个全局函数。而且,您已经让服务器为您解决了这个问题。因此,您必须将 id 存储在可访问的位置。如果你想将正确的 ID 与正确的函数调用唯一匹配,并且一次可以有多个函数调用,那么每个函数调用必须是全局唯一的。
    • @Sirko - 如果您一次只有一个 JSONP 调用(并且您可以保证是这种情况),那么只需将 ID 值存储在一个全局变量中并从您的回调中检索它。这是一种脆弱的方法,因为当他们不小心同时进行两个 JSONP 调用并且当时找出问题将是一团糟时,其他人对代码的细微更改很容易破坏它。
    • 仅供参考,我知道 YUI 为它的 JSONP 处理做了类似的事情,以保持状态附加到给定的 JSONP 调用。它有点丑(JSONP 首先是一个巨大的 hack),但丑陋只需要实现一次,然后就可以干净地使用了。
    • @Sirko - 我在回答的开头总结了你的选择。
    【解决方案3】:

    现在已经一岁了,但我认为 jfriend00 走在了正确的轨道上,尽管它比所有这些都简单 - 仍然使用闭包,只是在指定回调时添加参数:

    http://url.to.some.service?callback=myFunc('optA')

    http://url.to.some.service?callback=myFunc('optB')

    然后使用闭包来传递:

    function myFunc (opt) {
        var myOpt = opt; // will be optA or optB
    
        return function (data) {
            if (opt == 'optA') {
                // do something with the data
            }
            else if (opt == 'optB') {
                // do something else with the data
            }
        }
    }
    

    【讨论】:

      【解决方案4】:

      由于我似乎无法发表评论,所以我必须写一个答案。我已按照 jfriend00 对我的案例的说明进行操作,但在我的回调中没有收到来自服务器的实际响应。我最终做的是:

      var callbacks = {};
      
      function doJsonCallWithExtraParams(url, id, renderCallBack) {
          var safeId = id.replace(/[\.\-]/g, "_");
          url = url + "?callback=callbacks." + safeId;
          var s = document.createElement("script");
          s.setAttribute("type", "text/javascript");
          s.setAttribute("src", url);
      
          callbacks[safeId] = function() {
              delete callbacks[safeId];
              var data = arguments[0];
              var node = document.getElementById(id);
              if (data && data.status == "200" && data.value) {
                  renderCallBack(data, node);
              }
              else {
                  data.value = "(error)";
                  renderCallBack(data, node);
              }
      
              document.body.removeChild(s);
          };
      
          document.body.appendChild(s);
      }
      

      本质上,我将 goJSONP 和 getDataForUrl 压缩为 1 个函数,该函数写入脚本标记(稍后将其删除),并且不使用“unshift”函数,因为这似乎从 args 数组中删除了服务器的响应。所以我只是提取数据并使用可用的参数调用我的回调。这里的另一个区别是,我重用了回调名称,我可能会用计数器将其更改为完全唯一的名称。

      目前缺少的是超时处理。我可能会启动一个计时器并检查回调函数是否存在。如果它存在,它还没有自行删除,所以它是一个超时,我可以采取相应的行动。

      【讨论】:

        猜你喜欢
        • 2011-06-29
        • 1970-01-01
        • 1970-01-01
        • 2019-08-09
        • 2016-09-19
        • 2014-06-14
        • 1970-01-01
        • 1970-01-01
        • 2011-09-20
        相关资源
        最近更新 更多