【问题标题】:Laravel validation of request (not parameters)Laravel 验证请求(不是参数)
【发布时间】:2018-11-01 13:00:31
【问题描述】:

我有一个POST 路由:/tasks/{task}/start,它为用户声明了任务。但在声明之前,我需要验证该任务尚未启动(可能由其他人启动)。

Laravel 为请求参数的验证提供了出色的支持。 如果参数有任何问题,laravel 会使用聪明的魔法来传达这些错误。如果请求是使用 Ajax 发出的,则验证失败将导致格式良好的 json 错误。如果请求是从网络发出的,那么它会将您重定向回withErrors

我想要上述行为,但用于验证请求的模型是否处于正确的状态以进行修改。

现在我可以这样做:

public function start(Request $request, Task $task) {
    if ($task->isStarted()) {
        if ($request->isAjax()) {
            abort(400, "The task was already started");
        } else {
            redirect()->back()->withErrors("The task was already started");
        } 
    }
    // Task is not started. Let's start it...
}

但我想知道是否有某种原生支持通过模型的状态验证请求,并让 Laravel 处理错误的通信。

编辑

我找到了另一种解决方案,但似乎比较笨拙:

Validator::make($request->all(), [])->after(function ($validator) use ($task) {
    if ($task->isStarted()) {
            $validator->errors()->add('task', 'The task was already started');
    }
})->validate();

【问题讨论】:

    标签: laravel


    【解决方案1】:

    使用Laravel Policies/Gates

    最终结果将如下所示:

    public function start(Request $request, Task $task) 
    {
        $this->authorize('start', $task);
    
        // Task is not started. Let's start it...
    }
    

    你是如何做到这一点的?这是通过定义策略来完成的。让我们创建一个TaskPolicy

    生成它

    在控制台中运行:

    php artisan make:policy TaskPolicy
    

    注册

    app/Providers/AuthServiceProvider.php

    protected $policies = [
        \App\Task::class => \App\TaskPolicy::class,
    ];
    

    自定义它

    app/Policies/TaskPolicy.php

    <?php
    
    namespace App\Policies;
    
    use App\User;
    use App\Task;
    
    class TaskPolicy
    {
        /**
         * Determine if the given task can be started/claimed by the user.
         *
         * @param  \App\User  $user
         * @param  \App\Task  $task
         * @return bool
         */
        public function start(User $user, Task $task)
        {
            // do your checks here.. you have access to the authenticated user and task
    
            return !$task->isStarted();
        }
    }
    

    应用它

    如开头所述:

    public function start(Request $request, Task $task) 
    {
        $this->authorize('start', $task);
    
        // Task is not started. Let's start it...
    }
    

    查看documentation 了解更多信息。如果您只这样做一次,您可能想要使用 Gates。

    如果授权失败,此错误将自动返回为jsonhtml。它将返回403 错误。

    编辑

    您当然可以在控制器中以不同方式处理授权。

    你可以用 $this-&gt;authorize('start', $task) 代替:

    if (\Auth::user()->cannot('start', $task) ) {
    
    }
    

    此外,在您的刀片视图中,我假设您有类似的东西

    <form method="post" action="route-here">
        <button type="submit">Start Task</button>
    </form>
    

    你可以做的是用@can..@endcan 包装它:

    @can('start', @task)
    <form method="post" action="route-here">
        <button type="submit">Start Task</button>
    </form>
    @endcan
    

    这样用户在无法启动任务时将看不到按钮。

    【讨论】:

    • 但这意味着如果用户点击“开始”按钮,而不是被重定向回来并被显示为人类可读的错误,他们将被带到一个 403 页面,这不是我们想要的行为
    • “@can”部分不能解决问题。我试图防止出现竞争条件,即用户试图声明可能已经被其他人声明的阶段。在他们加载页面时,任务很可能尚未启动
    猜你喜欢
    • 1970-01-01
    • 2021-02-15
    • 2017-11-21
    • 2020-01-13
    • 2019-04-13
    • 2019-10-09
    • 2015-10-05
    • 1970-01-01
    • 2017-10-21
    相关资源
    最近更新 更多