【问题标题】:What is the correct way to send validation errors back to a Vue component from a Laravel controller?将验证错误从 Laravel 控制器发送回 Vue 组件的正确方法是什么?
【发布时间】:2020-12-21 18:51:18
【问题描述】:

[我已经稍微修改了这个问题以考虑另一种方法。如果这个问题看起来过长,我也很抱歉。我想我有点啰嗦,但我真的只是想表达清楚。]

我对 Laravel、Vue 和 Vuetify 还很陌生,但是经过一番努力,我已经让我的 CRUD 应用程序与 MySQL 一起工作,至少只要我传递了我的 Controller 良好数据。但现在我正在努力寻找正确的错误处理方式。

我使用 Vuetify 进行前端错误检查,它似乎工作得很好。我已经能够为我能想到的每个条件编写验证,并在最适合显示它的地方显示一条有意义的错误消息。我知道我需要在我的 Laravel 控制器后端进行相同的验证,并且在那里编写好的验证似乎也很简单。但是,我无法弄清楚如何将后端验证错误传达给我的 Vue 组件。

我的 CRUD 应用正在执行经典的待办事项列表。我的输入表单上用于添加新任务的字段之一是新任务的截止日期,而前端和后端都应进行的编辑之一是不能指定截止日期在过去。我“不小心”省略了对前端的检查,但已将其包含在后端,以确保当我选择过去的日期作为到期日时后端会检测到它。我的 Laravel 控制器按预期检测到,我的浏览器控制台显示:

显然,控制器中的验证正在运行,并且它们已正确检测到到期日期的问题。现在,我的问题是如何将相关信息获取到我的 Vue 组件,以及在相关时(如本例所示),如何将其显示给我的用户?

我能找到的所有使用 Vue 组件的 Laravel 应用程序示例都使用 thencatch 块来处理 Axios 响应和错误信息。我想到的另一个选择是访问 Laravel 控制器生成的错误包,但我找不到任何关于如何在 Vue 组件中完成的信息,所以看起来我必须采用 Axios 方法...... .

我无法弄清楚如何在 catch 块中的 Vue 组件中显示来自响应负载的相关信息。在我发现的每个示例中,响应返回到 then 块,错误返回到 catch 块但是当出现错误时,then 块永远不会被执行以支持 @987654329 @ 堵塞。但是当catch 块执行时,它无法看到响应,因此它无法从响应中向我显示任何内容,例如验证消息。我为答案所做的所有搜索都让我感到困惑而不是开明,因为许多答案都假设验证错误会回到 Laravel 刀片,而其他答案则是针对更旧版本的 Laravel。 (我正在运行 Laravel 8.x。)

我知道并非所有错误都是用户错误。例如,数据库可能已关闭,从而无法对数据库进行所有访问。我还需要注意这些情况。需要告诉用户一些事情——可能是“数据库已关闭。请稍后再试。” - 并且需要告知管理员数据库已关闭。我仍在尝试找出最好的方法,但我倾向于将 ErrorBoundary 作为解决方案。

目前,如果有人能解释在出现用户可修复的错误时如何从响应负载中获取我想要的信息,我会很高兴。关于如何处理错误不是用户可修复的情况的一些建议会更好。

这是我在 Vue 组件中插入逻辑的相关位:

/* We are creating a new item so determine the id for the new item. It should be 
   one greater than the highest id currently in the array. Every id of an existing
   task should always be at least 1. */
console.log(this.name + ".save() - saving a new item");
var highestTaskID = 0; //must be one less than the lowest possible id value
for (let todo of this.todos) {
    if (todo.id > highestTaskID) highestTaskID = todo.id;
}
var newTaskID = highestTaskID + 1; /* Calculate the ID of the new task. */
this.editedItem.id = newTaskID;  
this.form.id = this.editedItem.id;
this.form.description = this.editedItem.description;
this.form.status = this.editedItem.status;
this.form.priority = this.editedItem.priority;
this.form.due = this.editedItem.due;
let data = new FormData();
data.append('id', this.form.id);
data.append('description', this.form.description);
data.append('status', this.form.status);
data.append('priority', this.form.priority);
data.append('due', this.form.due);
axios.post('/task', data)
     .then((res) => {              
        console.log(this.name + ".save() - response from insert (then): " + res);
        this.snackbarMessage = "Created new task";
        this.showMessageSnackbar = true;
        this.showTable = true; //start showing ToDo table again 
        this.form.reset();
        this.getTasks();
        })
     .catch((error) => {
        console.log(this.name + ".save() - response from insert (catch): " + res);
        console.log(this.name + ".save() - error: " + error);
        this.snackbarMessage = "Failed to create new task";
        this.showMessageSnackbar = true;
        this.showTable = true; //start showing ToDo table again
        })

这是我的 TaskController 中的 store() 方法:

public function store(Request $request)
    {
        app('debugbar')->info('TaskController.store() started');

        $today = date('Y-m-d');

        $this->validate($request, [
            'description' => ['required', 'min:5', 'max:191'], 
            'status' => ['required'],
            'priority' => ['required'],
            'due' => ['required', 'after-or-equal:' . Date('Y-m-d')]
        ],
        [
            'description.required' => 'You must provide a non-blank task description',
            'description.min' => 'The task description must contain at least 5 characters',
            'description.max' => 'The task description must not exceed 191 characters',
            'status.required' => 'You must provide a task status',
            'status.in' => 'You must choose a task status from: Pending or Completed',
            'priority.required' => 'You must provide a task priority',
            'priority.in' => 'You must choose a task priority from: Low, Medium or High',
            'due' => 'You must provide a task due date',
            'due.after_or_equal' => 'You must provide a due date greater than or equal to today'
        ]
        );
        app('debugbar')->info('TaskController.store() validations completed');

        Task::create($request->all());
    }

我应该在控制器本身而不是在 Vue 组件中记录我检测到的所有错误吗?在某些方面这可能会容易得多,尽管我仍然必须能够检测哪些错误可以传回给用户以供他们处理,并且当我只需要告诉他们稍后再试因为应用程序不工作时完全没有。

【问题讨论】:

    标签: laravel vue.js axios


    【解决方案1】:

    首先,我建议使用 axios 的响应拦截器,它本质上是一个回调,每次请求完成时都会执行,因此您不必对每个请求进行错误处理。

    创建一个新文件(例如axiosInstance.js

    const axiosInstance = axios.create({
        baseURL: 'https://example.com/api',
    });
    
    axiosInstance.interceptors.response.use((response) => {
        return response;
    }, (error) => {    
        switch(error.response.status) {
            case 422:
                // Here you will be able to process Laravel Validation Errors.
                // You can for example display an error message using your favorite library
    
                console.log("Error Message: " + error.response.data.message);
                console.log("Error Object: " + error.response.data.errors);
                break;
        }
        return Promise.reject(error);
    }
    
    export default axiosInstance;
    

    如您所见,您可以使用error.response 访问您的回复。这也适用于简单 axios 请求中的 catch 块。

    axios.get('https://example.com/xyz').catch(e => {
       console.log(e.response);
    });
    

    我的第一个代码块中的上述拦截器将对所有 Laravel 验证错误进行特殊处理,因为默认情况下这些错误是使用 HTTP 状态 422(不可处理实体)返回的。 要使用它,您可以这样做,而不是 axios.get()

    import axiosInstance from './axiosInstance.js';
    axiosInstance.get('https://example.com/xyz');
    

    您在拦截器的 catch 块中定义的逻辑将在每次使用 axiosInstance 发起的请求时执行。如果特定请求失败,您还可以将.catch() 再次附加到请求以处理其他逻辑。

    要处理其他错误类型,请扩展拦截器switch 条件语句。例如,Laravel 会以状态码 500 返回“数据库不可用”之类的异常。通常,使用error.response.data.message 可以向用户显示一条简单的消息。


    tl;博士 这就是您可以使用您的代码实现的目标:

    axios.post('/task', data)
         .then((res) => {              
            console.log(this.name + ".save() - response from insert (then): " + res);
            this.snackbarMessage = "Created new task";
            this.showMessageSnackbar = true;
            this.showTable = true; //start showing ToDo table again 
            this.form.reset();
            this.getTasks();
            })
         .catch((error) => {
            console.log(this.name + ".save() - response from insert (catch): " + error.response);
            // Given that your erroneous response contains 'message' (like laravel validation errors do):
            console.log(this.name + ".save() - error: " + error.response.data.message); 
            this.snackbarMessage = "Failed to create new task";
            this.showMessageSnackbar = true;
            this.showTable = true; //start showing ToDo table again
            })
    

    【讨论】:

    • 这看起来很有希望,但我想我会有一些后续问题,可能有几个,除非你可以指出一个完整的例子,这样我就可以看到你给出的部分我适合吗?我的问题感觉应该是例行公事,但我在搜索中没有找到类似于此解决方案的内容。你在哪里学的这个技术?还是你自己想出来的?这感觉就像 Laravel 文档、教程和视频中应该有的东西,但我还没有看到有人解决我的问题。
    • 说实话,Laravel 文档缺少很多内容。 Here 你可以看到 Laravel 默认为触发验证错误的 XHR 请求返回状态码 422。不久前,我在一篇中型帖子中了解了 axios 响应拦截器。 This one 解释了更多关于拦截器的信息。然后,随着时间的推移,我根据自己的需要对其进行了调整。与所有事情一样,有很多方法可以实现您的目标。请随时在此处提出更具体的问题。
    • 是的,Laravel 文档没有完全涵盖一些重要的事情。 (我可能会通过我拥有的反向渠道做出一些努力来改进它们,但这需要时间。)我真的很想完成这项工作,但我需要花几天时间来处理紧迫的事情,所以我将致力于根据您的提示编写代码,并在此处再次询问是否有问题。非常感谢您的帮助!!
    猜你喜欢
    • 2019-07-03
    • 2021-10-14
    • 1970-01-01
    • 1970-01-01
    • 2020-12-10
    • 2014-07-20
    • 2017-08-12
    • 1970-01-01
    • 2021-08-08
    相关资源
    最近更新 更多