Magento 2: Bagaimana cara mengembalikan objek JSON dari API?


8

Saya mencoba mengembalikan objek JSON dari salah satu Model REST saya, sesuatu seperti ini:

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

Namun, apa yang tampaknya sepele, tidak mudah diimplementasikan. Masalahnya adalah saya tidak yakin apa yang harus jenis kembali di antarmuka dan model.

<?php

namespace AppFactory\Core\Api;

/**
 * @api
 */

interface SettingsInterface
{


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

Kelas objek akan kembali

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

saat memanggil API. Tipe primitif, int, angka, dan array yang tersedia tidak akan berfungsi untuk saya. Saya tidak ingin membuat kelas untuk setiap tipe kompleks juga kembali. Bagaimana saya bisa melakukan ini?

Terima kasih.


json data adalah string untuk php jadi buatlah string
Mohammad Mujassam

@MohammadMujassam mengembalikan string di docBlock akan membuat Magento mengubah objek output menjadi string yang lolos dari "dengan backslash dan mengelilingi seluruh objek dengan". Saya membaca artikel ini maxchadwick.xyz/blog/… dan menyarankan tidak ada cara lain untuk mengembalikan objek selain membuat model data untuk itu, tetapi saya hanya ingin memastikan ini adalah satu-satunya cara dan tidak ada cara lain.
Yehia A.Salam

ya tentu saja.
Mohammad Mujassam

Jawaban:


17

Saya berasumsi itu AppFactory\Core\Api\SettingInterface::get()adalah titik akhir REST. Dalam hal ini dalam komentar phpdoc, Anda perlu menentukan apa yang akan dikembalikan. Handler Magento REST akan mengambil nilai itu dan memprosesnya untuk menghapus semua data yang tidak perlu. Apa yang tersisa akan dikodekan ke dalam JSON sehingga dalam javascript Anda dapat mengambilnya sebagai JS hash yang tepat dan bukan string yang dikodekan json.

Trik tentang titik akhir itu adalah bahwa Anda perlu mendefinisikan dengan tepat apa yang akan Anda kembalikan. Magento tidak akan dapat memproses sesuatu yang umum seperti "array" di mana Anda akan mengatur apa pun yang Anda suka.

Dalam kasus Anda, agar tidak mencoba bermain dengan serangkaian string, akan lebih mudah untuk membuat antarmuka yang akan dikembalikan oleh titik akhir Anda.

 <?php

 namespace AppFactory\Core\Api;

 /**
  * @api
  */

 interface SettingsInterface
 {


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

Sekarang ketika Anda mengembalikan sebuah instance dari objek yang mengimplementasikan antarmuka itu, Magento akan membaca phpdocs-nya dan akan memproses nilai pengembaliannya. Sekarang buat file AppFactory\Core\Api\Data\SettingsInterfacesebagai berikut

<?php

namespace AppFactory\Core\Api\Data;

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

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

Sekarang ketika Anda membuat kelas aktual yang akan mengimplementasikan 2 metode get tersebut dan Anda akan mengembalikannya AppFactory\Core\Api\SettingsInterface::get()lalu magento akan mengembalikan sesuatu seperti

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

Jika Anda ingin level lain, Anda perlu membuat antarmuka lain yang akan mempertahankan settingsstruktur dan menambahkannya sebagai nilai balik untuk AppFactory\Core\Api\Data\SettingsInterface::getSettings().

Jika Anda perlu memiliki sesuatu yang dinamis dan Anda tidak ingin atau tidak dapat mempersiapkan antarmuka struktur ini maka Anda dapat mencoba mengatur string json-encoded dan menempatkan @return stringuntuk salah satu bidang. Namun demikian, Anda harus memastikan untuk mendekode string secara manual setelah menerima respons, sehingga respons Anda akan terlihat seperti ini:

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

dan untuk menggunakan response.extra.testAnda harus terlebih dahulu melakukannya response.extra = JSON.parse(response.extra);secara manual


terima kasih atas penjelasan terperinci, menguraikan kode pada sisi klien tidak terasa alami, dan menulis semua kelas untuk mewakili setiap item adalah mimpi buruk, apakah ada opsi keempat untuk mengembalikan objek json tanpa harus menulis kelas atau mengembalikan string, mungkin kembali campuran anotasi, meskipun saya mencobanya tetapi tidak berhasil sayangnya. Sepertinya saya akan berakhir dengan menyertakan json terakhir dalam sebuah array mis array ($ json_object), ini akan melakukan trik, tetapi tidak terasa alami juga, harus mengambil item pertama dalam array dari sisi klien
Yehia A.Salam

Anda dapat membuat aksi pengontrol reguler yang akan mengembalikan string json dan mengatur header ke teks / json atau aplikasi / json dan itu akan diterjemahkan di sisi browser. Jika Anda ingin menggunakan api sisanya maka saya tidak menemukan apa pun yang dapat melewati pemrosesan pos itu.
Zefiryn

ya kelihatannya, Magento harus mendukung ini dalam beberapa cara dan melonggarkan tanpa menerapkan jenis validasi pada tipe pengembalian
Yehia A.Salam

@ YehiaA. Salam saya sedang memeriksa sesuatu. Saya tidak memiliki cara mudah untuk menguji ini, tetapi cobalah untuk menggunakan "campuran" sebagai metode pengembalian AppFactory\Core\Api\DataSettingsInterface. Jika ini berhasil, Anda hanya perlu melakukan respons tingkat pertama.
Zefiryn

Jawaban yang sangat berguna
Pandurang

5

Saya juga menghadapi masalah ini, dan sebagai alternatif untuk solusi yang diusulkan oleh Zefiryn, saya telah mengatasinya dengan melampirkan data yang dikembalikan dalam sebuah array (atau dua). Silakan perhatikan contoh di bawah ini.

/**
 * 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)
  ];
}

Karena cara Magento 2 memungkinkan array konten campuran sebagai nilai pengembalian, struktur data yang lebih kompleks dapat disematkan di dalam array lainnya. Sampel di atas menghasilkan respons JSON berikut (terpotong agar mudah dibaca).

[
{
    "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"
    }
}
]

Menutupnya dalam satu lapisan menghapus kunci-kunci array, dan tanpa melampirkannya dalam array apa pun menghasilkan kesalahan.

Dapat dimengerti tidak ada dari ini yang ideal, tetapi pendekatan ini memungkinkan saya untuk mengontrol konsistensi dalam struktur data yang dikembalikan ke tingkat tertentu (struktur dan jenis data yang diharapkan). Jika Anda juga mengendalikan penulisan perpustakaan sisi klien, pencegat dapat diimplementasikan untuk menghapus array luar sebelum mengembalikannya ke aplikasi.


1

Untuk Magento 2.3.1, jika Anda perlu memotong serialisasi array, Anda dapat memeriksa file ini untuk memperbarui logika inti. Saya pikir ini adalah titik masuk yang baik. Tetapi dengan melakukan ini Anda pasti akan merusak kompatibilitas Sabun.

Selain itu di Magento 2.1.X, Anda tidak memiliki masalah ini jika Anda memasukkan anyType sebagai tipe pengembalian.

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

Referensi perubahan komit: 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);
    }

Dan ganti dengan:

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

Saya tahu pertanyaan ini sudah cukup lama, tetapi ada satu solusi yang cukup sederhana untuk ini:

Anda harus mengganti Json-Renderer Magento\Framework\Webapi\Rest\Response\Renderer\Jsonatau menulis Plugin untuknya.

Berikut sedikit contoh plugin:

Di Anda 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>

Di Kelas Plugin baru Anda 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);
}

}

Apa yang terjadi di sini:

  • Jika rest-route adalah "/ V1 / my / rest-route", maka metode rendering baru digunakan, yang berarti secara sederhana, bahwa data tidak dikodekan.
  • Metode pemeriksaan tambahan digunakan untuk mengevaluasi apakah string benar-benar objek json. Kalau tidak (misalnya, jika responsnya adalah kesalahan 401, akan mengakibatkan kesalahan internal dan mengembalikan kode status yang salah)
  • Dengan cara ini, dalam metode istirahat Anda, Anda dapat memberikan kembali string json, yang tidak akan diubah.

Tentu saja Anda juga dapat menulis Renderer Anda sendiri, yang memproses array misalnya.


0

Saya menghadapi masalah yang sama dan butuh beberapa saat untuk mencari tahu masalahnya.

Magento melakukan sesuatu yang aneh di sana, web prosesor layanan output api yang terletak di bawah Magento \ Framework \ Webapi \ ServiceOutputProcessor Di kelas ini ada metode bernama convertValue (); yang merupakan alasan untuk kawat gigi [].

Solusi terbaik bagi saya untuk menyelesaikan masalah adalah membuat plugin sekitar untuk mengatasi kondisi ini jika di convertValue (); metode di mana mereka memeriksa apakah $ data adalah sebuah array dan melakukan hal-hal aneh dengannya.

Berikut ini contoh kode plugin saya: Saya pikir setiap orang tahu cara membuat modul Magento 2 dasar, jadi saya hanya memposting kode plugin itu sendiri di sini.

  • Buat folder Plugin

  • Buat kelas 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);
    }
}
  • Buat deklarasi Plugin di 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>

Ini harus menyelesaikan masalah output json array di api web

Semoga ini membantu

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.