Laravel: 无法访问中间件中的资源集合

Laravel: cannot access resource collection in middleware

我正在尝试创建一个中间件,它将 returned 响应包装到我的 API 的统一 JSON 响应中。这将确保所有 API 响应具有相同的基本结构,如下所示:

{
    "success": true,
    "data": {...}
}

这是我目前使用的中间件:

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Http\Response;

class ApiResponseWrapper
{
    /**
     * Handle an incoming request.
     *
     * @param Request $request
     *
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        /** @var Response $response */
        $response = $next($request);
        $original = $response->getOriginalContent();

        $content = [
            'success' => $response->isSuccessful(),
            'data' => $original,
        ];

        $jsonContent = json_encode($content, JsonResponse::DEFAULT_ENCODING_OPTIONS);
        $response->setContent($jsonContent);

        return $response;
    }
}

这个中间件的两个关键点是:

  1. $original = $response->getOriginalContent(); 行获取要换行的响应内容。
  2. 内容必须是字符串才能设置为新内容。这就是为什么我 json_encode $content 具有 JSON 响应的默认编码选项。

当我想将分页 ResourceCollection 传递到中间件时,问题就出现了。控制器方法如下所示:

public function index(): AnonymousResourceCollection
{
    /** @var User $user */
    $user = auth()->user();

    return PostResource::collection($user->posts->paginate(20));
}

通常,Laravel 集合中不存在 ->paginate(...) 方法。这是从这里获取的宏:gist.github.com/simonhamp/549e8821946e2c40a617c85d2cf5af5e.

如果我现在点击调用 index() 方法的端点,预期的输出是这样的:

{
    "success": true,
    "data": {
        "data": [
            <PostResource as JSON object>
        ],
        "meta": {...},
        "links": {...},
    }
}

但我实际得到的是:

{
    "success": true,
    "data": [
        <Post Model as array>
    ]
}

我想我可能必须调用一种方法来将 $original(属于 Illuminate\Support\Collection 类型)编码为分页资源集合,但我没有找到。

所有其他回复都很好。例如,在另一个控制器方法上,我只是 return 一个数组或一个字符串,它们按预期包装。

有人知道这里出了什么问题吗?


编辑:

我也尝试在设置 $content 变量之前添加:

if (method_exists($original, 'toArray')) {
    $original = $original->toArray();
}

ResourceCollections 的结果相同。

调查变量类型并查看 Laravel API 后,我发现最简单的解决方案是检查响应是否为 JsonResponse 并获取呈现的分页来自它的数据:

public function handle($request, Closure $next)
{
    /** @var Response $response */
    $response = $next($request);
    $content = [
        'success' => $response->isSuccessful(),
    ];

    if ($response instanceof JsonResponse) {
        /** @var JsonResponse $response */

        $data = $response->getData(true);

        $content = array_merge($content, $data);
    } else {
        $content['data'] = $response->getOriginalContent();
    }

    $jsonContent = json_encode($content, JsonResponse::DEFAULT_ENCODING_OPTIONS);
    $response->setContent($jsonContent);

    return $response;
}

这导致了我的用例的预期响应:

{
    "success": true,
    "data": [
        <PostResource as JSON object>
    ],
    "meta": {...},
    "links": {...},
}

另请注意,我最初在包装 data 键中指定了 datametalinks 键。 我发现上面的结果有点漂亮,因为我绕过双包裹 data.

我希望这对遇到这个问题的任何人有所帮助!