提问人:MikkeyDevelop 提问时间:6/9/2023 最后编辑:matiaslauritiMikkeyDevelop 更新时间:6/9/2023 访问量:92
Laravel FormRequest Validation for Rest API 获取带有 2 个查询 json 参数的调用无法正常工作
Laravel FormRequest Validation for Rest API get calls with 2 query json params not working properly
问:
我得到了一个 Laravel FormRequest 类,它应该在我的 JSON Rest API 上的 GET 路由上验证两个 JSON 查询字符串(嵌套对象和数组)。我拿起 json 查询字符串,将它们解码为 php 对象,并在验证准备时间期间存储它们,以请求进行验证。
出于某种原因,这似乎无法正常工作。“sorters”json 验证正在工作,“required”和“array”的“sorters_decoded”验证也是如此。数组元素验证和之后的所有内容似乎都不起作用,因为即使发送了无效数据,我也会到达控制器功能。修改了请求上的查询输入包集合(无效数据设置为 null),但未生成验证 422 响应。你可能会看到这段代码有问题吗?
class RecipeFindRequest extends FormRequest
{
protected function prepareForValidation(): void
{
try {
$sorters = null;
if ($this->query->has('sorters')) {
$sorters = json_decode($this->query->get('sorters'));
$this->merge([
'sorters_decoded' => $sorters,
]);
}
$filters = null;
if ($this->query->has('filters')) {
$filters = json_decode($this->query->get('filters'));
$this->merge([
'filters_decoded' => $filters,
]);
}
$this->merge([
'language' => $this->headers->get('language'),
]);
} catch(\Throwable $e) {
//die silently, fail response will get raised on validation time
}
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, mixed>
*/
public function rules(): array
{
return [
'sorters' => ['json'],
'sorters_decoded' => ['required', 'array'],
'sorters_decoded.*.name' => ['required', 'string', Rule::in(['likes', 'ratings', 'calories', 'carbs', 'protein', 'fat', 'created'])],
'sorters_decoded.*.direction' => ['required', 'string', Rule::in(['asc', 'desc'])],
'sorters_decoded.*.order' => ['required', 'integer'],
'filters' => ['json'],
'filters_decoded.duration_high' => ['integer', 'min:0'],
'filters_decoded.duration_low' => ['integer', 'min:0'],
'filters_decoded.title' => ['string'],
'filters_decoded.difficulty' => ['array'],
'filters_decoded.difficulty.*' => ['string', 'exists:difficulties,id'],
'filters_decoded.ingredients' => ['array'],
'filters_decoded.ingredients.*.id' => ['string', 'exists:ingredients,id'],
'filters_decoded.ingredients.*.relation' => ['string', Rule::in(['include', 'exclude'])],
'filters_decoded.liked_by_me' => ['boolean'],
'filters_decoded.cookbooks' => ['array'],
'filters_decoded.cookbooks.*' => ['string', 'exists:cookbooks,id'],
'filters_decoded.nutritions' => ['array'],
'filters_decoded.nutritions.*.category_id' => ['string', 'exists:nutrition_categories,id'],
'filters_decoded.nutritions.*.nutrition_high' => ['numeric', 'min:0'],
'filters_decoded.nutritions.*.nutrition_low' => ['numeric', 'min:0'],
'language' => ['string', 'size:2', 'exists:i18n_languages,short'],
'page' => ['integer', 'min:1'],
'per_page' => ['integer', 'min:1'],
];
}
}
这两个参数都作为 json 查询字符串发送,并且解码步骤有效,因此它与 url de/encoding 无关。
我试图将sorters.*.name数组元素验证从字符串更改为整数,当我发送一些数据(如[{'name':'a'})时,“integer”验证将$response->query->sorters_decoded中的数据更改为[{'name':null}]。但是没有出现 422 验证失败响应。
谢谢你考虑我的问题, 米基
答:
0赞
miken32
6/9/2023
#1
这里存在许多问题:
- 在 中,您有一个 try/catch 块,它对捕获的异常不执行任何操作。与您的评论所建议的相反,它不会作为验证错误返回,因为您正在捕获它。即使它没有被捕获,我不确定它是否会在客户端显示为 422 错误。
prepareForValidation()
json_decode()
默认情况下不会抛出错误,因此您的 try/catch 语句无论如何都没有什么可以使用的!json_decode()
默认情况下,还可以解码为对象;如果没有数组,后续验证规则将无法运行。- 对于在查询字符串中发送的数据,您有一个验证规则。表单请求验证仅适用于邮件正文,而不适用于查询字符串。您想要验证的任何内容都需要手动合并。
这是我会尝试的:
<?php
namespace App\Http\Requests;
use Illuminate\Validation\ValidationException;
use Throwable;
class RecipeFindRequest extends FormRequest
{
protected function prepareForValidation(): void
{
try {
if ($this->query->has('sorters')) {
$sorters = json_decode($this->query->get('sorters'), associative: true, flags: \JSON_THROW_ON_ERROR);
}
if ($this->query->has('filters')) {
$filters = json_decode($this->query->get('filters'), associative: true, flags: \JSON_THROW_ON_ERROR);
}
$this->merge([
// user cannot easily change the headers of a request
// so you should set a sensible default for this value
'language' => $this->headers->get('language', 'en'),
'page' => $this->query->get('page'),
'per_page' => $this->query->get('per_page'),
'sorters_decoded' => $sorters ?? null,
'filters_decoded' => $filters ?? null,
]);
} catch(Throwable $e) {
if (isset($sorters)) {
// if this is set, the error was with the second decode
$messages = ['filters' => 'An invalid filter value was passed'];
} else {
$messages = ['sorters' => 'An invalid sort value was passed'];
}
throw ValidationException::withMessages($messages);
}
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, mixed>
*/
public function rules(): array
{
return [
'sorters_decoded' => ['required', 'array'],
'sorters_decoded.*.name' => ['required', 'string', Rule::in(['likes', 'ratings', 'calories', 'carbs', 'protein', 'fat', 'created'])],
'sorters_decoded.*.direction' => ['required', 'string', Rule::in(['asc', 'desc'])],
'sorters_decoded.*.order' => ['required', 'integer'],
'filters_decoded.duration_high' => ['integer', 'min:0'],
'filters_decoded.duration_low' => ['integer', 'min:0'],
'filters_decoded.title' => ['string'],
'filters_decoded.difficulty' => ['array'],
'filters_decoded.difficulty.*' => ['string', 'exists:difficulties,id'],
'filters_decoded.ingredients' => ['array'],
'filters_decoded.ingredients.*.id' => ['string', 'exists:ingredients,id'],
'filters_decoded.ingredients.*.relation' => ['string', Rule::in(['include', 'exclude'])],
'filters_decoded.liked_by_me' => ['boolean'],
'filters_decoded.cookbooks' => ['array'],
'filters_decoded.cookbooks.*' => ['string', 'exists:cookbooks,id'],
'filters_decoded.nutritions' => ['array'],
'filters_decoded.nutritions.*.category_id' => ['string', 'exists:nutrition_categories,id'],
'filters_decoded.nutritions.*.nutrition_high' => ['numeric', 'min:0'],
'filters_decoded.nutritions.*.nutrition_low' => ['numeric', 'min:0'],
'language' => ['string', 'size:2', 'exists:i18n_languages,short'],
'page' => ['integer', 'min:1'],
'per_page' => ['integer', 'min:1'],
];
}
}
这些 URL 的大小必须很大;这是 POST 请求最适合使用的那种事情。(但这不是一个硬性规定,只是我的意见。
评论
0赞
MikkeyDevelop
6/10/2023
谢谢,这就是我一直在找的。我是PHP的新手,最近几年一直在使用C#。
评论
dump($this->sorters_decoded)
prepareForValidation()
json_decode()
array(1) { [0]=> object(stdClass)#1450 (3) { ["name"]=> string(5) "likes" ["direction"]=> string(3) "asc" ["order"]=> int(1) } }