Magento 2:APIからJSONオブジェクトを返す方法は?


8

次のようなRESTモデルの1つから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 2017年

そうです、そうです。
Mohammad Mujassam 2017年

回答:


17

私はそれAppFactory\Core\Api\SettingInterface::get()がRESTエンドポイントであると想定しています。その場合、phpdocコメントでは、これが返すものを定義する必要があります。Magento RESTハンドラーはその値を取得して処理し、不要なすべてのデータを削除します。残ったものはJSONにエンコードされるため、JavaScriptでは、jsonエンコードされた文字列ではなく、すでに適切なJSハッシュとして取得できます。

これらのエンドポイントに関するトリックは、何を返すかを非常に正確に定義する必要があることです。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();
}

これらの2つのgetメソッドを実装する実際のクラスを作成し、AppFactory\Core\Api\SettingsInterface::get()それを返すと、magentoは次のようなものを返します

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

別のレベルが必要な場合は、settings構造を保持し、の戻り値として追加する別のインターフェイスを作成する必要がありますAppFactory\Core\Api\Data\SettingsInterface::getSettings()

動的なものを必要とする場合に、この構造インターフェースを望まないか準備できない場合は、jsonでエンコードされた文字列を設定して@return string、任意のフィールドに配置できます。ただし、この方法では、応答が次のようになるため、応答を受け取った後にその文字列を手動でデコードする必要があります。

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

使用response.extra.testするには、最初にresponse.extra = JSON.parse(response.extra);手動で行う必要があります


詳細な説明に感謝します。クライアント側で文字列をデコードするのは自然な感じではなく、各項目を表すためにすべてのクラスを書き込むのは悪夢です。クラスを記述したり、返す必要なしにjsonオブジェクトを返すだけの4番目のオプションがあります。文字列、多分return混合アノテーションですが、試してみましたが残念ながら動作しませんでした。配列($ json_object)などの配列で最後のjsonを囲むことになりそうですが、これはうまくいきますが、自然に感じられず、クライアント側から配列の最初の項目を取得する必要があります
イヒアA.サラーム2017年

json文字列を返し、ヘッダーをtext / jsonまたはapplication / jsonに設定する通常のコントローラーアクションを作成すると、ブラウザー側でデコードされます。REST APIを使用したい場合、その後処理をバイパスできるものは見つかりませんでした。
Zefiryn 2017年

そうですね、magentoはこれを何らかの形でサポートし、戻り値の型にこのタイプの検証を適用せずに緩和する必要があります
Yehia A.Salam

@ YehiaA.Salam私は何かをチェックしていました。これをテストする簡単な方法はありませんが、のメソッドの戻りとして「混合」を使用してみてくださいAppFactory\Core\Api\DataSettingsInterface。これが機能する場合、最初のレベルの応答を行うだけで済みます。
Zefiryn 2017年

非常に便利な回答
Pandurang 2017年

5

私もこの問題に直面しており、@ Zefirynが提案したソリューションの代替策として、戻りデータを配列(または2つ)で囲むことで回避しました。以下の例を検討してください。

/**
 * 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

vendor / magento / framework / 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

私はこの質問がかなり古いことを知っていますが、これに対する非常に簡単な解決策が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);
}

}

ここでは何が起きるのですか:

  • rest-routeが「/ V1 / my / rest-route」の場合、新しいレンダリング方法が使用されます。つまり、データがエンコードされていないということです。
  • 追加のチェックメソッドを使用して、文字列が本当にjsonオブジェクトかどうかを評価します。それ以外の場合(たとえば、応答が401エラーの場合、内部エラーが発生し、誤ったステータスコードが返されます)
  • このように、rest-methodで、json-stringを返すことができますが、これは変更されません。

もちろん、たとえば配列を処理する独自のレンダラーを作成することもできます。


0

同じ問題に直面し、問題を理解するのにしばらく時間がかかりました。

Magentoは、Magento \ Framework \ Webapi \ ServiceOutputProcessorの下にあるWeb APIサービス出力プロセッサーで奇妙なことを行います。このクラスには、convertValue()という名前のメソッドがあります。これが[]波括弧の理由です。

私が問題を解決するための最良の解決策は、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出力の問題が解決されます

お役に立てれば

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.