简体   繁体   English

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

[英]Laravel: cannot access resource collection in middleware

I'm trying to create a middleware which wraps the returned response into a uniform JSON response for my API.我正在尝试创建一个中间件,它将返回的响应包装为我的 API 的统一 JSON 响应。 This will ensure that all API responses have the same base structure which looks like this:这将确保所有 API 响应具有相同的基本结构,如下所示:

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

Now this is the middleware I am currently using:现在这是我目前使用的中间件:

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;
    }
}

The two key points of this middleware are:这个中间件的两个关键点是:

  1. The $original = $response->getOriginalContent(); $original = $response->getOriginalContent(); line gets the content of the response to wrap. line 获取要换行的响应内容。
  2. The content needs to be a string in order to be set as the new content.内容必须是字符串才能设置为新内容。 That's why I json_encode the $content with the default encoding options for a JSON response.这就是为什么我使用 JSON 响应的默认编码选项对$content进行json_encode

The problem arises when I want to pass a paginated ResourceCollection into the middleware.当我想将分页的ResourceCollection传递到中间件时,就会出现问题。 The controller method looks like this:控制器方法如下所示:

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

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

Normally, the ->paginate(...) method does not exist in a Laravel Collection.通常,Laravel 集合中不存在->paginate(...)方法。 It is a macro taken from here: gist.github.com/simonhamp/549e8821946e2c40a617c85d2cf5af5e .这是从这里获取的宏: gist.github.com/simonhamp/549e8821946e2c40a617c85d2cf5af5e

If I now hit the endpoint that calls the index() method, the expected output is something like this:如果我现在点击调用index()方法的端点,预期的输出是这样的:

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

But what I actually get is this:但我实际得到的是:

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

I thought I might have to call a method to encode the $original (which is of type Illuminate\\Support\\Collection ) into a paginated resource collection, but I didn't find one.我想我可能需要调用一种方法来将$original (类型为Illuminate\\Support\\Collection )编码为分页资源集合,但我没有找到。

All other responses are fine.其他回复都正常。 For example on another controller method I simply return an array or a string and they are wrapped as expected.例如,在另一个控制器方法上,我只返回一个数组或字符串,它们按预期包装。

Does anyone have an idea what is wrong here?有谁知道这里有什么问题?


Edit:编辑:

I also tried to add this before the $content variable is set:我还尝试在设置$content变量之前添加它:

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

It results in the same output for ResourceCollections .它导致ResourceCollections输出相同。

After investigating the variable types and looking through the Laravel API, I found that the easiest solution was to just check if the response is a JsonResponse and get the rendered pagination data from it:在调查变量类型并查看 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;
}

This results in the expected responses for my use case:这导致了我的用例的预期响应:

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

Also note that I originally specified the data , meta and links keys within the wrapping data key.另请注意,我最初在包装data键中指定了datametalinks键。 I find the result above a bit prettier because I got around double wrapped data .我发现上面的结果有点漂亮,因为我绕过了双包装data

I hope this will help anyone who came across this question!我希望这会帮助任何遇到这个问题的人!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM