jQuery ajax 调用无法从 CakePHP 3.8 读取 json 编码数据(获取一个空数组)

Posted

技术标签:

【中文标题】jQuery ajax 调用无法从 CakePHP 3.8 读取 json 编码数据(获取一个空数组)【英文标题】:jQuery ajax call can't read json encoded data from CakePHP 3.8 (gets an empty array) 【发布时间】:2020-05-22 20:17:26 【问题描述】:

读取我的 Cakephp3 API 返回的 json 编码数据以响应来自 jQuery 的 ajax 调用时,我遇到了一个奇怪的问题。我已经阅读了关于 *** 和其他地方的 20 多篇文章,以及人们遇到的常见问题,原因是错误的 dataType、contentType 或服务器没有从 ajax 获取数据。这些情况都不适用于这里(我尝试了不同的设置,但对我的问题没有影响)。

问题:

我的 ajax 调用向我的 CakePHP3 API 发送一些参数,API 正确获取参数并返回 CakePHP 实体的 json 编码数组(每个实体在发送回 ajax 调用之前添加了一个附加属性“available_yield”) .我在浏览器中使用直接 URL 获得了正确的输出(使用 json 验证器检查,一切都很好),但是我的 ajax 调用(我在 Chrome devtools 中使用控制台和网络选项卡进行调查)显示了一个空数组,用于格式良好json.

我的调查表明,当我修改 CakePHP 实体时会出现问题。如果我从 API json 编码返回原始数据,jquery ajax 将获得正确的数据。但是当我修改任何实体时,jquery ajax 中的数组是空的。

从 CakePHP 调试显示,两个数组(未修改和修改)看起来完全一样,除了添加的属性,即它们格式正确且在所有方面都可以,都在 json 中,在浏览器中都可以。但是jquery不接受修改后的json。

目前的解决方案似乎是:不要修改您的数据!但这就是我们在将相关和处理后的数据发送到客户端之前在服务器上所做的事情,不是吗?

有人遇到过类似的问题吗?

我附上我的代码:

CakePHP API 函数:

function myFunction()
$params = $this->getRequest()->getQueryParams();
        //debug($params);
        $componentReference = $params['component_reference'];
        $componentTypeId = $params['component_type_id'];

        $matchingCrops = $this->Crops->find()->select(['id', 'grower_name', 'bulk'])->where(['reference' => $componentReference]);

        $cropsWithYieldInfo = []; //to hold modify crop
        foreach($matchingCrops as $crop)
            $availableYield = $this->Crops->calculateAvailableYield($crop->id); //returns a string
            if(isset($availableYield) && !empty($availableYield))
                $crop->available_yield = number_format($availableYield,1);  //tried $crop['available_yield'] as well, same result
                $cropsWithYieldInfo[] = $crop;
            
        

//        debug($cropsWithYieldInfo);
//        debug($matchingCrops);

        //$content = json_encode($cropsWithYieldInfo);  // <<-- changing to $matchingCrops makes ajax see the array, but the array does not have my calculated data
        $content = json_encode($matchingCrops);

        $this->response = $this->response->withStringBody($content);
        $this->response = $this->response->withType('json');  
        $this->autoRender = false; 
        return $this->response;
 

我的 AJAX:

function myAjax()
 $.ajax(
                type: 'GET',
                url: url,
                //contentType: "application/json",
                dataType: "json"
            )
            .done(function (data) 
                console.log(data);  
            )
            .fail(function (data) 
                console.log('AJAX call to /'+errMsg+' function failed');
            )

从 API 返回的 JSON 数据:

编辑: 可能很重要: 当我在浏览器中通过 URL 访问 API 时,它总是返回修改后的数据;看起来我的代码修改了 $matchingCrops 集中的实际实体。因此,如果将 $content 设置为 $matchingCrops 或 $cropsWithYieldInfo,浏览器中的结果总是相同的。但是通过 ajax 访问 API 时会有所不同:当 $content = json_encoded($matchingCrops) 我得到原始未修改的数据数组时,当 $content = json_encoded($cropsWithYieldInfo) 我得到一个空数组。

这真的很奇怪:为什么浏览器总是得到修改后的数组,而 ajax 得到一个或另一个???我知道如果我修改 $crop 实体,那么它会修改结果集中的实体,但我希望这对于浏览器和 ajax 调用都是一致的。

编辑: 我尝试了一个稍微修改过的代码,看看克隆实体是否会产生任何影响,但唯一的区别是现在浏览器得到了我期望发生的事情(原始未修改的数组或修改过的数组)并且它与 ajax 得到的一致.但这并不能解决问题(如果数组被修改,ajax 仍然会得到空数组)。

foreach($matchingCrops as $crop)
            $modCrop = clone $crop;
            $availableYield = $this->Crops->calculateAvailableYield($crop->id); //returns a string
            if(isset($availableYield) && !empty($availableYield))
                $modCrop->available_yield = number_format($availableYield,1);  //tried $crop['available_yield'] as well, same result
                $cropsWithYieldInfo[] = $modCrop;
            
        

已修改(ajax 将其作为空数组获取;浏览器始终从 API 获取):

["id":12345,"grower_name":"XYZ","bulk":false,"available_yield":"4.1","id":23456,"grower_name":null,"bulk":true,"available_yield":"190.0"]

未修改(ajax 正确理解):

["id":12345,"grower_name":"XYZ","bulk":false,"id":23456,"grower_name":null,"bulk":true]

【问题讨论】:

简单的问题是最好的问题! Controllers should never echo data,只会出问题! @ndm 我可以返回响应(而不是回显数据),但这不会改变我的情况。但感谢您的一般建议。 @djevulen 问题在于,它不仅会给您带来(额外的)问题,对于试图提供帮助的人来说也是一个问题,因为不清楚究竟是什么导致了这种行为,而且事情可能回声时与在您的环境中不同,在人们的本地环境中表现不同,因此即使它看起来对您没有任何影响,建议您提供并使用一个按照预期方式执行的示例,以便可能的来源问题尽可能地小。 你试过return $this-&gt;response-&gt;withType("application/json")-&gt;withStringBody(json_encode($result));而不是return $this-&gt;response; 【参考方案1】:
$array = ['foo'=>'bar'];

$this->set([
    'response' => $array,
    '_serialize' => 'response',
]);
$this->Request->renderAs($this, 'json');

而且我会序列化 ajax!所以,你不需要对对象进行字符串化,你可以直接将它用于数据属性。

$.ajax(
    type: 'POST',
    url: url,                      
    data: YourArray: YourVariables,
    success: function(data) 
      alert(data);
    
);

你可以在这里找到更多:https://api.jquery.com/serialize/

让我们看看您的 ajax,您需要将值从数组传递到 ajax 以获得您尝试执行的每个值的响应 +errMsg+

你的 ajax 在失败和成功中应该是这样的:

失败:function( jqXHR, Status, errMsg) 然后你可以显示类似console.log('AJAX call to /'+errMsg+' function failed'); 的响应

$.ajax(
    type: "GET",
    url: url,
    data: 
      title: $(value[0]).val(),
      description: $(value[1]).val()
    ,
    success: function (data) 
        if(data === "success") 
            // do something with data or whatever other data on success
            console.log('success');
         else if(data === "error") 
            // do something with data or whatever other data on error
            console.log('error');
        
    
);

要显示指定的错误,您需要在成功函数中传递title = $(value[0]).val()

或者在这里使用 ajax serializeArray()each() 示例 https://www.w3schools.com/jquery/tryit.asp?filename=tryjquery_ajax_serializearray

从CakePHP 3.4开始,使用应该使用

$content = json_encode($matchingCrops);
return $this->response->withType("application/json")->withStringBody($content);

不是这个

$content = json_encode($matchingCrops);

$this->response = $this->response->withStringBody($content);
$this->response = $this->response->withType('json');  
$this->autoRender = false; 
return $this->response;

【讨论】:

好吧,在这种情况下,问题的根源在于键盘和椅子之间;)但感谢您的回答。我喜欢你简洁的 $this->response... 我更改了代码以节省行数。 @djevulen 我在 1 小时前的问题下的评论中评论说:) 你浪费了 1 小时 :) 反正!很高兴看到你解决了。 您在问题 1 下的评论不能解决问题。实际上,我得到的结果与您的代码和我的代码相同。你的更简洁,这是主要区别。【参考方案2】:

天哪……找到了!好吧,这很尴尬,但我还是会把它作为一个教训发布,并作为对其他人的警告:如果你晚上遇到无法解决的问题,回家好好睡一觉吧,早上重新开始!

问题原因:

1) 我的计算函数实际上返回的是浮点数而不是字符串,并且我正在检查是否为空,所以当它返回 0 时,代码没有将 'available_yield' 属性添加到 $crop 实体中(因为负责的代码也放错了地方!应该在 if 块之外)

此时我仍然觉得“好吧,但我应该在浏览器和 ajax 调用中都获得一致的行为!!!”,除非...

2) 我没有注意到我在浏览器检查和 ajax 调用中使用了不同的 id,所以计算机是正确的... :-/

一直在学习...

仔细检查每一行并调试所有可能的变量! 仔细检查您的测试数据!

可以正常运行的代码版本:

function myFunction()
$params = $this->getRequest()->getQueryParams();
        //debug($params);
        $componentReference = $params['component_reference'];
        $componentTypeId = $params['component_type_id'];

        $matchingCrops = $this->Crops->find()->select(['id', 'grower_name', 'bulk'])->where(['reference' => $componentReference]);

        $cropsWithYieldInfo = []; //to hold modify crop
        $cropsWithYieldString = '';
        foreach($matchingCrops as $crop)
            $availableYield = $this->Crops->calculateAvailableYield($crop->id); //returns a float not string! 
            if(isset($availableYield)) //<<- that was the cause of the problem; !empty(float) will ignore 0, just check if it's set
                $crop->available_yield = number_format($availableYield,1); 
            
            $cropsWithYieldInfo[] = $crop;
        

//        debug($cropsWithYieldInfo);
//        debug($matchingCrops);

        $content = json_encode($cropsWithYieldInfo); 

        //$this->response = $this->response->withStringBody($content);
        //$this->response = $this->response->withType('application/json');  
        $this->autoRender = false; 
        //return $this->response;
        //more concisely
        return $this->response->withType('application/json')->withStringBody($content);


感谢你们的时间,你们让我专注于寻找解决方案。

【讨论】:

你应该等两分钟 :) @Dlk 已阅读您的答案。感谢您指出我的 ajax 失败方法可以受益于函数(jqXHR、Status、errMsg):)

以上是关于jQuery ajax 调用无法从 CakePHP 3.8 读取 json 编码数据(获取一个空数组)的主要内容,如果未能解决你的问题,请参考以下文章

我无法从 JQuery ajax 调用中加载部分 Django 模板

CakePHP Jquery.ajax()

如何在 cakephp 中通过 ajax 提交 jquery 移动弹出表单

jquery-无法从我通过ajax调用获得的对象类型响应中检索数据

无法从 jQuery Ajax 调用中获得正确的返回值 [重复]

chrome 更新后,jQuery ajax 调用无法从网页解码为 utf-8 字符集到 Chrome 扩展中