【问题标题】:Laravel nesting all json responsesLaravel 嵌套所有 json 响应
【发布时间】:2014-01-21 23:55:23
【问题描述】:

我正在使用 Laravel 创建一个 JSON REST API,到目前为止它已经很流行了。但是,我需要用一些由 metaDataController(或可能是模型)创建的元状态 JSON 来包装我的 JSON 输出,我很好奇这可能是什么好方法。

例如,所有响应都将采用以下格式:

{
    "meta": {
        "status": 200, 
        "notifications": 2
    },
    "response": {
        //JSON from the route's Controller/Model/etc
    }
}

据我所知,我要么需要修改 Laravel Response 默认值并委托给 metaDataController,要么创建某种 Route::any 来合并 JSON 的两个部分,如 Returning Multiple Laravel Eloquent Models as JSON 中所述。虽然我一直都知道 metaDataController,但另一个控制器根据路由在不断变化。

我认为必须有一种方法可以将此结构声明为所有路由或 Route::group 的默认结构。

谢谢!

【问题讨论】:

    标签: php json rest laravel laravel-4


    【解决方案1】:

    我不认为执行 json_decode->json_encode 循环是一个可以接受的解决方案(如 Chris 的回答)。

    这是另一个解决方案

    use Illuminate\Http\Response;
    use Illuminate\Http\Request;
    
    Route::filter('apisuccess', function($route, Request $request, Response $response = null) {
        $response->setContent(json_encode([
            'data' => $response->original, 
            'meta' => ['somedata': 'value']
        ]));
    });
    

    然后我会将此过滤器附加到我的 REST API 路由。

    编辑:另一种解决方案(更复杂)。

    1. 创建自定义响应类:
    use Illuminate\Http\Response;
    class ApiResponse extends Response
    {
        protected $meta;
        protected $data;
    
        public function __construct($content = '', $status = 200, $headers = array())
        {
            parent::__construct([], $status, $headers);
            $this->meta = [];
        }
    
        public function withMeta($property, $value = null)
        {
            if (is_array($property))
                $this->meta = $property;
            else 
                array_set($this->meta, $property, $value);
            return $this;
        }
    
        public function withData($data)
        {
            $this->data = $data;
            return $this;
        }
    
        public function sendContent()
        {
            echo json_encode(['success' => true, 'data' => $this->data, 'meta' => $this->meta, 'echo' => ob_get_contents()]);
        }
    }
    
    1. 将其作为单例放入 IOC 容器中:

      $this->app->bindShared('ApiResponse', function() {
          return new \Truinject\Http\ApiResponse();
      });
      
    2. 最后,创建过滤器并将其用作路线的“之前”:

    Route::filter('apiprepare', function(Illuminate\Routing\Route $route, Illuminate\Http\Request $request) {
        $data = $route->run();
        return App::make('ApiResponse')->withData($data);
    });
    

    所以我们基本上是用我们自己的覆盖默认响应类,但仍然使用 $route->run() 调用适当的控制器来获取数据。

    要设置元数据,请在控制器中执行以下操作:

    \App::make('ApiResponse')->withMeta($property, $value);
    

    我在我的基础 API 控制器类中添加了方法“meta”,它封装了这个。

    【讨论】:

    • 这很有趣,我可以使用其中一种解决方案进行重构。据我了解,我们正在谈论将封装移动到路由层返回/过滤器。几个问题: - 为什么之前而不是之后? - 需要放置的 IOC 容器在哪里? - 控制器的方法如何抛出 503 或路由层抛出 404?非常棒的想法。看来是对的。
    • -为什么是之前而不是之后? - 如果我没记错的话,当“之后”过滤器运行时,响应已经建立。不过不要问其他问题。使用这种方法,代码的工作方式几乎相同,因此控制器代码没有变化。
    【解决方案2】:

    您可以使用app.php 中的全局 after 过滤器来捕获所有响应,然后根据需要重新配置它:

    App::after(function($request, $response)
    {
        if(is_a($response, 'Illuminate\Http\JsonResponse')) {
            $response->setContent(json_encode(array(
                'data' => json_decode($response->getContent()),
                'foo' => 'bar',
                'cat' => 'dog'
            )));
        }
    });
    

    在上面的示例中,您将所有现有的 json 数据放入一个子数据元素中(在您的示例中这将是“响应”),然后添加 foo 和 bar。所以 foo、bar 和 data 将是顶级 json 对象。

    如果你不喜欢全球定位,after 是一个发送的事件,所以你也可以在控制器/其他地方收听它。

    【讨论】:

    • 嗯,有道理,工作得很好,谢谢!我正在使用全球定位,因为这需要影响 laravel 的所有 JSON 响应。它在app.php 中不喜欢它,所以我最终将它放在filters.php 中,我注意到一个空的App::after 等待使用。
    • 酷 - 我相信 app.php 应该已经包含 App::after 函数,您可以将代码放入其中,但过滤器也可以工作 - 对您自己的布局有意义的东西
    • 反对者愿意解释吗?如果可以改进,很高兴调整答案
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-05-26
    • 2018-08-02
    • 1970-01-01
    • 2020-02-07
    • 2020-12-31
    • 2020-01-13
    • 2020-05-19
    相关资源
    最近更新 更多