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;
}
}
这个中间件的两个关键点是:
$original = $response->getOriginalContent();
行获取要换行的响应内容。
- 内容必须是字符串才能设置为新内容。这就是为什么我
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
键中指定了 data
、meta
和 links
键。
我发现上面的结果有点漂亮,因为我绕过双包裹 data
.
我希望这对遇到这个问题的任何人有所帮助!
我正在尝试创建一个中间件,它将 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;
}
}
这个中间件的两个关键点是:
$original = $response->getOriginalContent();
行获取要换行的响应内容。- 内容必须是字符串才能设置为新内容。这就是为什么我
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
键中指定了 data
、meta
和 links
键。
我发现上面的结果有点漂亮,因为我绕过双包裹 data
.
我希望这对遇到这个问题的任何人有所帮助!