Magento 2:如何从API返回JSON对象?


8

我正在尝试从我的一个REST模型中返回一个JSON对象,如下所示:

{
    "settings": {
        "set1" : 2,
        "set2" : "key1" 
    },
    "extra": {
        "e1'" : {
            "e2'": true 
        }
    }
}

但是,看似琐碎的事情并不容易实现。问题是我不确定接口和模型中的返回类型应该是什么。

<?php

namespace AppFactory\Core\Api;

/**
 * @api
 */

interface SettingsInterface
{


    /**
     * @return object
     */
    public function get();
}

对象类将返回

{
  "message": "Class object does not exist",

调用API时。可用的原始类型int,number和array对我不起作用。我不想为每个返回的复杂类型都创建一个类。我怎样才能做到这一点?

谢谢。


json数据是php的字符串,因此使其成为字符串
Mohammad Mujassam

docBlock中的@MohammadMujassam 返回字符串将使Magento将输出对象转换为字符串,该字符串用反斜杠转义“,并用”包围整个对象。我浏览了这篇文章maxchadwick.xyz/blog/…,它暗示着除了为对象创建数据模型外,没有其他方法可以返回对象,但是我只是想确保这是唯一的方法,并且没有其他方法。
Yehia A.Salam

是的,肯定会。
Mohammad Mujassam

Answers:


17

我假设这AppFactory\Core\Api\SettingInterface::get()是一个REST端点。在这种情况下,您需要在phpdoc注释中定义返回的内容。Magento REST处理程序将采用该值并对其进行处理,以删除所有不必要的数据。剩下的将被编码为JSON,因此在javascript中,您可以将其检索为已经正确的JS哈希,而不是json编码的字符串。

关于这些端点的诀窍是,您需要非常精确地定义返回的内容。Magento将无法处理像“数组”这样的常规内容,您可以在其中设置所需的任何内容。

在您的情况下,为了不尝试使用字符串数组,创建一个端点将返回的接口会更加容易。

 <?php

 namespace AppFactory\Core\Api;

 /**
  * @api
  */

 interface SettingsInterface
 {


     /**
      * @return Data\SettingsInterface
      */
     public function get();
 }

现在,当您返回实现该接口的对象的实例时,Magento将读取其phpdocs并将处理其返回值。现在创建一个文件,AppFactory\Core\Api\Data\SettingsInterface如下所示

<?php

namespace AppFactory\Core\Api\Data;

interface SettingsInterface
{
    /**
    * @return int[]
    **/
    public function getSettings();

    /**
    * @return string[]
    **/
    public function getExtra();
}

现在,当您创建将实现这两​​个get方法的实际类时,您将在其中返回它,AppFactory\Core\Api\SettingsInterface::get()那么magento将返回类似

{
    "settings": [1, 2, 5],
    "extra": ["my","array","of","strings"]
}

如果需要另一个级别,则需要创建另一个接口,该接口将保留settings结构并将其添加为的返回值AppFactory\Core\Api\Data\SettingsInterface::getSettings()

如果您需要动态的东西,并且您不希望或无法准备此结构接口,则可以尝试@return string为任何字段设置json编码的字符串和位置。但是,通过这种方式,您必须确保在收到响应后手动解码该字符串,因为这样您的响应将如下所示:

{
    "settings": [1, 2, 5],
    "extra": "{\"test\":\"string\",\"value\":8}"
}

为了使用,response.extra.test您必须先response.extra = JSON.parse(response.extra);手动进行


感谢您的详细解释,在客户端解码字符串不自然,并且编写代表所有项目的所有类是一场噩梦,是否有第四种选择仅返回json对象而无需编写类或返回字符串,也许是返回的混合注释,尽管我尝试过,但不幸的是没有起作用。看来我最终会将最终的json封装在例如array($ json_object)的数组中,这将达到目的,但也不自然,必须从客户端获取数组中的第一项
Yehia A.Salam

您可以创建一个常规的控制器操作,该操作将返回json字符串并将标头设置为text / json或application / json,并将在浏览器端进行解码。如果您想使用rest api,那么我找不到任何可以绕过该后期处理的东西。
Zefiryn

是的,magento应该以某种方式支持它,并且在不对返回类型强制执行这种类型的验证的情况下放松一下
Yehia A.Salam

@ YehiaA.Salam我正在检查某些东西。我没有简单的方法来测试它,但尝试使用“混合”作为中方法的回报AppFactory\Core\Api\DataSettingsInterface。如果这可行,则只需要执行第一级响应即可。
Zefiryn

非常有用的答案
Pandurang

5

我也遇到了这个问题,作为@Zefiryn提出的解决方案的替代方案,我通过将返回数据封装在一个(或两个)数组中来解决此问题。请考虑以下示例。

/**
 * My function
 *
 * @return
 */
public function myFunction()
{
  $searchCriteria = $this->_searchCriteriaBuilder->addFilter('is_filterable_in_grid',true,'eq')->create();
  $productAttributes = $this->_productAttributeRepository->getList($searchCriteria)->getItems();

  $productAttributesArray = [];
  foreach ($productAttributes as $attribute) {
    $productAttributesArray[$attribute->getAttributeCode()] = $this->convertAttributeToArray($attribute);
  }

  return [[
          "attributes"=>$productAttributesArray,
          "another_thing"=>["another_thing_2"=>"two"]
        ]];
}

private function convertAttributeToArray($attribute) {
  return [
    "id" => $attribute->getAttributeId(),
    "code" => $attribute->getAttributeCode(),
    "type" => $attribute->getBackendType(),
    "name" => $attribute->getStoreLabel(),
    "options" => $attribute->getSource()->getAllOptions(false)
  ];
}

由于Magento 2如何允许将混合内容数组作为返回值,因此可以在其他数组中嵌入更复杂的数据结构。上面的示例产生以下JSON响应(出于可读性的考虑,已将其截断)。

[
{
    "attributes": {
        "special_price": {
            "id": "78",
            "code": "special_price",
            "type": "decimal",
            "name": "Special Price",
            "options": []
        },
        "cost": {
            "id": "81",
            "code": "cost",
            "type": "decimal",
            "name": "Cost",
            "options": []
        },
    "another_thing": {
        "another_thing_2": "two"
    }
}
]

将其封装在单层中会删除阵列的键,并且不将其封装在任何阵列中会导致错误。

可以理解,这都不是理想的方法,但是这种方法允许我在一定程度上控制返回的数据结构(预期的数据结构和类型)的一致性。如果您还可以控制编写客户端库,则可以实现拦截器以在将外部数组返回给应用程序之前剥离该外部数组。


1

对于Magento 2.3.1,如果您需要绕过阵列序列化,则可以检查此文件以更新核心逻辑。我认为这是一个很好的切入点。但是这样做肯定会破坏Soap的兼容性。

此外,在Magento 2.1.X上,如果将anyType用作返回类型,则不会出现此问题。

Github参考:https : //github.com/magento/magento2/blob/2.3-develop/lib/internal/Magento/Framework/Reflection/TypeCaster.php

提交更改参考:https : //github.com/magento/magento2/commit/6ba399cdaea5babb373a35e88131a8cbd041b0de#diff-53855cf24455a74e11a998ac1a871bb8

供应商/ magento /框架/Reflection/TypeCaster.php:42

     /**
     * Type caster does not complicated arrays according to restrictions in JSON/SOAP API
     * but interface and class implementations should be processed as is.
     * Function `class_exists()` is called to do not break code which return an array instead
     * interface implementation.
     */
    if (is_array($value) && !interface_exists($type) && !class_exists($type)) {
        return $this->serializer->serialize($value);
    }

并替换为:

     /**
     * Type caster does not complicated arrays according to restrictions in JSON/SOAP API
     * but interface and class implementations should be processed as is.
     * Function `class_exists()` is called to do not break code which return an array instead
     * interface implementation.
     */
    if (is_array($value) && !interface_exists($type) && !class_exists($type)) {
        return $value;
    }

1

我知道这个问题已经很老了,但是有一个非常简单的解决方案:

您需要替换Json-Renderer Magento\Framework\Webapi\Rest\Response\Renderer\Json或为其编写一个插件。

这里是一个插件的小例子:

在你的 di.xml

<type name="Magento\Framework\Webapi\Rest\Response\Renderer\Json">
    <plugin name="namespace_module_renderer_json_plugin" type="Namespace\Module\Plugin\Webapi\RestResponse\JsonPlugin" sortOrder="100" disabled="false" />
</type>

在您的新插件类中 Namespace\Module\Plugin\Webapi\RestResponse\JsonPlugin

<?php
namespace Namespace\Module\Plugin\Webapi\RestResponse;

use Magento\Framework\Webapi\Rest\Request;
use Magento\Framework\Webapi\Rest\Response\Renderer\Json;

class JsonPlugin
{

    /** @var Request */
    private $request;

    /**
     * JsonPlugin constructor.
     * @param Request $request
     */
    public function __construct(
        Request $request
    )
    {
        $this->request = $request;
    }

    /**
     * @param Json $jsonRenderer
     * @param callable $proceed
     * @param $data
     * @return mixed
     */
    public function aroundRender(Json $jsonRenderer, callable $proceed, $data)
    {
        if ($this->request->getPathInfo() == "/V1/my/rest-route" && $this->isJson($data)) {
            return $data;
        }
        return $proceed($data);
    }

    /**
    * @param $data
    * @return bool
    */
    private function isJson($data)
    {
       if (!is_string($data)) {
       return false;
    }
    json_decode($data);
    return (json_last_error() == JSON_ERROR_NONE);
}

}

这里会发生什么:

  • 如果剩余路由为“ / V1 / my / rest-route”,则使用新的渲染方法,这简单地表示未对数据进行编码。
  • 附加的检查方法用于评估字符串是否真的是json对象。否则(例如,如果响应为401错误,则将导致内部错误并返回错误的状态代码)
  • 这样,在您的休息方法中,您可以退回一个不会更改的json-string。

当然,您也可以编写自己的渲染器,例如处理数组。


0

我遇到了同样的问题,花了我一段时间才解决了这个问题。

Magento在位于Magento \ Framework \ Webapi \ ServiceOutputProcessor下的Web api服务输出处理器中做一些奇怪的事情。在此类中,有一个名为convertValue()的方法。这是[]大括号的原因。

对我而言,解决此问题的最佳解决方案是创建一个around插件来克服convertValue()中的if条件。他们检查$ data是否为数组并对其进行处理的奇怪方法。

这是我的插件示例代码:我想每个人都知道如何创建基本的Magento 2模块,因此我只在此处发布插件本身的代码。

  • 创建一个插件文件夹

  • 创建一个类Vendor \ ModuleName \ Plugin \ ServiceOutputProcessorPlugin.php

<?php

namespace Vendor\ModuleName\Plugin;

use Magento\Framework\Webapi\ServiceOutputProcessor;

class ServiceOutputProcessorPlugin
{
    public function aroundConvertValue(ServiceOutputProcessor $subject, callable $proceed, $data, $type)
    {
        if ($type == 'array') {
            return $data;
        }
        return $proceed($data, $type);
    }
}
  • 在Vendor \ ModuleName \ etc \ di.xml中创建插件声明
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <type name="Magento\Framework\Webapi\ServiceOutputProcessor">
        <plugin name="vendor_modulenameplugin" type="Vendor\ModuleName\Plugin\ServiceOutputProcessorPlugin"/>
    </type>
</config>

这应该解决Web API中的数组json输出问题

希望这可以帮助

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.