Magento 2: Làm thế nào để trả về một đối tượng JSON từ API?


8

Tôi đang cố gắng trả về một đối tượng JSON từ một trong các Mô hình REST của mình, đại loại như thế này:

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

Tuy nhiên, những gì có vẻ tầm thường, không dễ thực hiện. Vấn đề là tôi không chắc loại trả về nên có trong giao diện và kiểu máy.

<?php

namespace AppFactory\Core\Api;

/**
 * @api
 */

interface SettingsInterface
{


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

Lớp đối tượng sẽ trở lại

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

khi gọi API. Các kiểu nguyên thủy có sẵn int, number và mảng sẽ không hoạt động với tôi. Tôi không muốn tạo một lớp cho mỗi loại phức tạp cũng sẽ trở lại. Tôi có thể làm cái này như thế nào?

Cảm ơn.


dữ liệu json là chuỗi cho php, vì vậy hãy tạo chuỗi
Mohammad Mujassam

Chuỗi trả về @MohammadMujassam trong docBlock sẽ khiến Magento chuyển đổi đối tượng đầu ra thành chuỗi thoát khỏi "dấu gạch chéo ngược và bao quanh toàn bộ đối tượng với". Tôi đã xem qua bài viết này maxchadwick.xyz/blog/ và nó cho thấy không có cách nào khác để trả về một đối tượng ngoài việc tạo một mô hình dữ liệu cho nó, nhưng tôi chỉ muốn chắc chắn rằng đây là cách duy nhất và không có những cách khác.
Yehia A.Salam

vâng chắc chắn, nó sẽ.
Mohammad Mujassam

Câu trả lời:


17

Tôi giả sử rằng đó AppFactory\Core\Api\SettingInterface::get()là một điểm cuối REST. Trong trường hợp đó trong các bình luận phpdoc, bạn cần xác định cái này sẽ trả về cái gì. Trình xử lý Magento REST sẽ lấy giá trị đó và xử lý nó để loại bỏ tất cả dữ liệu không cần thiết. Những gì còn lại sẽ được mã hóa thành JSON để trong javascript, bạn có thể truy xuất nó dưới dạng băm JS đã được mã hóa đúng và không phải là chuỗi được mã hóa json.

Mẹo về những điểm cuối đó là bạn cần xác định chính xác những gì bạn sẽ trả lại. Magento sẽ không thể xử lý một cái gì đó chung chung như "mảng" nơi bạn sẽ đặt bất cứ thứ gì bạn thích.

Trong trường hợp của bạn, để không thử chơi với một chuỗi các chuỗi, việc tạo giao diện mà điểm cuối của bạn sẽ trở lại sẽ dễ dàng hơn.

 <?php

 namespace AppFactory\Core\Api;

 /**
  * @api
  */

 interface SettingsInterface
 {


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

Bây giờ khi bạn trả về một thể hiện của một đối tượng triển khai giao diện đó, Magento sẽ đọc phpdocs của nó và sẽ xử lý các giá trị trả về của chúng. Bây giờ tạo một tập tin AppFactory\Core\Api\Data\SettingsInterfacenhư sau

<?php

namespace AppFactory\Core\Api\Data;

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

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

Bây giờ khi bạn tạo lớp thực tế sẽ thực hiện 2 phương thức get đó và bạn sẽ trả về nó AppFactory\Core\Api\SettingsInterface::get()sau đó magento sẽ trả về một cái gì đó như

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

Nếu bạn muốn một cấp độ khác, bạn cần tạo một giao diện khác sẽ giữ settingscấu trúc và thêm nó làm giá trị trả về cho AppFactory\Core\Api\Data\SettingsInterface::getSettings().

Nếu bạn cần phải có thứ gì đó năng động và bạn không muốn hoặc không thể chuẩn bị giao diện cấu trúc này thì bạn có thể thử đặt chuỗi mã hóa json và đặt @return stringcho bất kỳ trường nào. Tuy nhiên, theo cách này, bạn sẽ phải đảm bảo giải mã chuỗi đó theo cách thủ công sau khi nhận được phản hồi vì khi đó phản hồi của bạn sẽ như thế này:

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

và để sử dụng, response.extra.testtrước tiên bạn sẽ phải làm response.extra = JSON.parse(response.extra);thủ công


cảm ơn vì đã giải thích chi tiết, việc giải mã chuỗi ở phía máy khách không cảm thấy tự nhiên và viết tất cả các lớp để thể hiện mỗi mục là một cơn ác mộng, có tùy chọn thứ tư nào để trả về đối tượng json mà không phải viết các lớp hoặc trả về chuỗi, có thể là chú thích hỗn hợp trả về, mặc dù tôi đã thử nhưng không hoạt động. Có vẻ như tôi sẽ kết thúc json cuối cùng trong một mảng, ví dụ như mảng ($ json_object), điều này sẽ thực hiện thủ thuật, nhưng cũng không cảm thấy tự nhiên, phải chọn mục đầu tiên trong mảng từ phía máy khách
Yehia A.Salam

Bạn có thể tạo một hành động điều khiển thông thường sẽ trả về chuỗi json và đặt tiêu đề thành văn bản / json hoặc application / json và nó sẽ được giải mã ở phía trình duyệt. Nếu bạn muốn sử dụng api còn lại thì tôi không tìm thấy bất cứ thứ gì có thể bỏ qua quá trình xử lý bài đó.
Zefiryn

yea trông giống như vậy, magento nên hỗ trợ điều này theo một cách nào đó và nới lỏng mà không thực thi loại xác nhận này trên loại trả về
Yehia A.Salam

@ YehiaA.Salam Tôi đã kiểm tra một cái gì đó. Tôi không có cách dễ dàng để kiểm tra điều này nhưng hãy thử sử dụng "hỗn hợp" như là một sự trở lại của các phương thức trong AppFactory\Core\Api\DataSettingsInterface. Nếu điều này hoạt động, bạn chỉ cần thực hiện cấp độ đầu tiên của phản ứng.
Zefiryn

Câu trả lời rất hữu ích
Pandurang

5

Tôi cũng đã phải đối mặt với vấn đề này và như là một giải pháp thay thế cho giải pháp @Zefiryn đề xuất, tôi đã giải quyết vấn đề này bằng cách đưa dữ liệu trả về trong một mảng (hoặc hai). Hãy xem xét ví dụ dưới đây.

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

Do cách Magento 2 cho phép các mảng nội dung hỗn hợp làm giá trị trả về, các cấu trúc dữ liệu phức tạp hơn có thể được nhúng bên trong các mảng khác. Mẫu ở trên mang lại phản hồi JSON sau (được cắt bớt để dễ đọc).

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

Việc đóng nó trong một lớp sẽ loại bỏ các khóa của mảng và không kèm theo nó trong bất kỳ mảng nào dẫn đến lỗi.

Có thể hiểu không ai trong số này là lý tưởng, nhưng cách tiếp cận này cho phép tôi kiểm soát tính nhất quán trong cấu trúc dữ liệu được trả về ở một mức độ nhất định (cấu trúc và loại dữ liệu dự kiến). Nếu bạn cũng kiểm soát việc viết thư viện phía máy khách, một bộ chặn có thể được thực hiện để loại bỏ mảng bên ngoài trước khi trả lại cho ứng dụng.


1

Đối với Magento 2.3.1, nếu bạn cần bỏ qua việc tuần tự hóa mảng, bạn có thể kiểm tra tệp này để cập nhật logic lõi. Tôi nghĩ rằng đó là một điểm vào tốt. Nhưng bằng cách này, bạn sẽ phá vỡ tính tương thích của Xà phòng cho chắc chắn.

Ngoài ra, trên Magento 2.1.X, bạn không gặp phải vấn đề này nếu bạn đặt anyType làm kiểu trả về.

Tham khảo Github: https://github.com/magento/magento2/blob/2.3-develop/lib/iternal/Magento/Framework/Reflection/TypeCaster.php

Cam kết thay đổi tham khảo: https://github.com/magento/magento2/commit/6ba399cdaea5babb373a35e88131a8cbd041b0de#diff-53855cf24455a74e11a998ac1a871b8

nhà cung cấp / 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);
    }

Và thay thế bằng:

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

Tôi biết câu hỏi này khá cũ, nhưng có một giải pháp khá đơn giản cho việc này:

Bạn cần phải thay thế Json-Renderer Magento\Framework\Webapi\Rest\Response\Renderer\Jsonhoặc bạn viết Plugin cho nó.

Dưới đây là một ví dụ nhỏ về plugin:

Trong bạn 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>

Trong Plugin-Class mới của bạn 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);
}

}

Chuyện gì xảy ra ở đây thế:

  • Nếu tuyến đường nghỉ là "/ V1 / my / tuyến đường nghỉ", thì phương thức kết xuất mới được sử dụng, có nghĩa đơn giản là dữ liệu không được mã hóa.
  • Một phương thức kiểm tra bổ sung được sử dụng để đánh giá xem chuỗi có thực sự là đối tượng json không. Mặt khác (ví dụ: nếu phản hồi là lỗi 401, sẽ dẫn đến lỗi nội bộ và trả lại mã trạng thái sai)
  • Bằng cách này, trong phương thức nghỉ ngơi của bạn, bạn có thể trả lại chuỗi json, chuỗi sẽ không bị thay đổi.

Tất nhiên bạn cũng có thể viết Trình kết xuất của riêng bạn, ví dụ xử lý một mảng.


0

Tôi đã đối mặt với cùng một vấn đề và tôi phải mất một thời gian để tìm ra vấn đề.

Magento thực hiện một cái gì đó kỳ lạ trong bộ xử lý đầu ra dịch vụ api web được đặt trong Magento \ Framework \ Webapi \ ServiceOutputProcessor Trong lớp này có một phương thức có tên là convertValue (); đó là lý do cho [] niềng răng.

Giải pháp tốt nhất để tôi giải quyết vấn đề là tạo một plugin xung quanh để khắc phục điều này nếu có điều kiện trong convertValue (); phương thức trong đó họ kiểm tra xem $ data có phải là một mảng không và thực hiện công cụ kỳ lạ đó với nó.

Dưới đây là mã ví dụ của plugin: Tôi nghĩ rằng mọi người đều biết cách tạo mô-đun Magento 2 cơ bản, vì vậy tôi chỉ đăng mã của chính plugin tại đây.

  • Tạo một thư mục Plugin

  • Tạo một lớp Nhà cung cấp \ 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);
    }
}
  • Tạo khai báo Plugin trong 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>

Điều này sẽ giải quyết vấn đề đầu ra mảng json trong web api

Hi vọng điêu nay co ich

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.