Vue 3 后端错误消息处理范例

vue · 浏览次数 : 5

小编点评

本文介绍了如何在 Vue.js 应用程序中实现错误消息的处理和传递。文章首先解释了错误消息的格式,然后提供了一个在 composables 文件夹中定义的错误处理工具类。接着,文章展示了如何将错误状态与应用中的输入框和全局消息进行绑定。最后,文章给出了一个完整的 Vue.js 组件示例,演示了如何在实际项目中应用这些概念。 1. **错误消息格式**:文章建议使用 JSON 格式的 errors 字段来传递错误信息,其中 global 字段包含全局错误,而其他特定字段则包含各自领域的错误。 2. **错误处理工具类**:在 composables 文件夹中,文章创建了一个 error.ts 文件,其中定义了错误处理相关的工具函数,包括获取、添加、移除和检查错误等操作。 3. **输入框与错误状态绑定**:文章展示了如何将错误状态与输入框进行绑定,通过 v-model 和 @focus 事件来控制错误信息的显示和隐藏。 4. **全局错误显示**:对于全局错误,文章提出了在表格顶端显示错误信息的方案,并使用列表的形式展示错误详情。 5. **提交按钮状态控制**:文章还讨论了如何在提交按钮上禁用或启用,以便在表单有错误时阻止用户提交。 文章最后提供了一个完整的 Vue.js 组件案例,该案例结合了错误状态处理和用户注册场景,可以作为学习的参考。

正文

1. 错误消息格式

前后端消息传递时,我们可以通过 json 的 errors 字段传递错误信息,一个比较好的格式范例为:

{
  errors: {
    global: ["网络错误"],
    password: ["至少需要一个大写字母", "至少需要八位字符"]
  }
}

errors 中,字段名代表出错位置(如果是输入框的话,对应错误要显示在框下面),内容为一个数组,每个字符串代表一个错误。

2. 处理函数

可以新建一个 composables 文件夹,以存储各个 components 中共用的逻辑,例如错误消息处理。这里在 composables 文件夹中新建一个 error.ts

import { ref, type Ref } from 'vue';

export interface ErrorFields {
  global: string[];
  [key: string]: string[];
}

export function useErrorFields(fields: string[]) {
  const errors: Ref<ErrorFields> = ref({ global: [], ...fields.reduce((acc, field) => ({ ...acc, [field]: [] }), {}) });
  const clearErrors = () => {
    for (const field in errors.value) {
      errors.value[field] = [];
    }
  };
  const hasErrors = (field?: string) => {
    if (field) {
      return errors.value[field].length > 0;
    }
    return Object.values(errors.value).some((field) => field.length > 0);
  };
  const addError = (field: string, message: string) => {
    if (field === '') {
      field = 'global';
    }
    const array = errors.value[field];
    if (!array.includes(message)) {
      array.push(message);
    }
    return array;
  };
  const removeError = (field: string, message?: string) => {
    if (field === '') {
      field = 'global';
    }
    if (message) {
      errors.value[field] = errors.value[field].filter((m) => m !== message);
    } else {
      errors.value[field] = [];
    }
  };
  return { errors, clearErrors, hasErrors, addError, removeError };
}

这里我们就定义了错误类及其处理函数。

3. 组件中的使用

定义的 useErrorFields 工具可以在 component 中这样使用:

<script setup lang="ts">
import axios from 'axios';
import { computed, onMounted, ref, type Ref } from 'vue';
import { useErrorFields } from '@/composables/error';

const { errors, clearErrors, addError, hasErrors } = useErrorFields(['username', 'password']);

const username = ref('');

function onSubmit() {
  const api = axios.create({
    baseURL: import.meta.env.VITE_API_URL,
  });
  api.get("/user/register")
  .catch((error) => {
    if (error.response && error.response.data && error.response.data.errors) {
      errors.value = { ...errors.value, ...error.response.data.errors };
    } else if (error.response) {
      addError('', '未知错误');
    } else {
      addError('', '网络错误');
    }
  })
}
</script>

<template>
  <div
    v-if="hasErrors('global')"
    class="mb-5 rounded-md border-0 shadow-sm ring-1 ring-inset ring-gray-300 dark:ring-gray-500 px-4 py-2"
  >
    <div class="flex text-red-700 dark:text-rose-400 space-x-2 mb-2">
      <p class="text-lg font-semibold">错误</p>
    </div>
    <ul class="flex flex-col font-medium tracking-wide text-sm list-disc pl-6">
      <li v-for="e in errors.global" v-html="e" />
    </ul>
  </div>
  <form>
    <div>
      <label for="username" class="block text-sm font-medium leading-6">
        用户名
        <span class="text-red-700">*</span>
      </label>
      <div class="mt-2">
        <input
          v-model="username"
          @focus="clearErrors"
          id="username"
          name="username"
          type="text"
          autocomplete="username"
          required
          class="block w-full rounded-md border-0 py-1.5 px-3 shadow-sm ring-1 ring-inset focus:ring-2 focus:ring-inset focus:ring-indigo-600 focus:outline-none sm:text-sm sm:leading-6 dark:bg-white/10 dark:ring-white/20"
          :class="{ 'ring-red-500': hasErrors('username'), 'ring-gray-300': !hasErrors('username') }"
        />
      </div>
      <ul class="flex flex-col font-medium tracking-wide text-red-500 text-xs mt-1 ml-1">
        <li v-for="e in errors.username" v-html="e" />
      </ul>
    </div>
    <div>
      <button
        type="submit"
        class="flex w-full justify-center rounded-md px-3 py-1.5 text-sm font-semibold leading-6 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600 text-white shadow-sm hover:bg-indigo-500"
        :class="{
          'cursor-default pointer-events-none': hasErrors() || processing,
          'bg-gray-400': hasErrors(),
          'bg-indigo-600': !hasErrors(),
        }"
      >
        注册
      </button>
    </div>
  </form>
</template>

接下来,我们一步步解析以上代码。

3.1 根据后端响应更新错误状态

我们首先使用 useErrorFields 定义了一个错误状态类:

const { errors, clearErrors, addError, hasErrors } = useErrorFields(['username', 'password']);

这时候,错误状态 errors 中可访问三个字段,并将绑定到页面的不同位置:

global: 全局错误 / 无具体位置的错误 => 显示在表格顶端的单独框中

username: 用户名上的错误 => 显示在 username 输入框下方
password: 密码上的错误 => 显示在 password 输入框下方

接下来,我们需要定义提交函数,例如这里使用 axios 进行后端访问,后端地址用环境变量提供:

function onSubmit() {
  const api = axios.create({
    baseURL: import.meta.env.VITE_API_URL,
  });
  api.get("/user/register")
  .catch((error) => {
    if (error.response && error.response.data && error.response.data.errors) {
      errors.value = { ...errors.value, ...error.response.data.errors };
    } else if (error.response) {
      addError('', '未知错误');
    } else {
      addError('', '网络错误');
    }
  })
}

这样,后端返回错误信息时,错误状态会被自动更新。如果出现了网络错误或其他错误,addError类会在 global 字段上增加错误 (使用空字符串为第一个参数,默认添加到 global 字段)。

接下来,将错误状态绑定到页面。

3.2 绑定到输入框

<input
  v-model="username"
  @focus="clearErrors"
  id="username"
  name="username"
  type="text"
  autocomplete="username"
  required
  class="block w-full rounded-md border-0 py-1.5 px-3 shadow-sm ring-1 ring-inset focus:ring-2 focus:ring-inset focus:ring-indigo-600 focus:outline-none sm:text-sm sm:leading-6 dark:bg-white/10 dark:ring-white/20"
  :class="{ 'ring-red-500': hasErrors('username'), 'ring-gray-300': !hasErrors('username') }"
/>

这里主要使用了两个个函数:

clearErrors: 当重新开始进行输入时,清除错误状态中的全部错误。

hasErrors: 当对应位置出现错误时,将输入框边框颜色变为红色。

将错误状态显示在输入框下:

<div>
  <label for="username" class="block text-sm font-medium leading-6">
    用户名
    <span class="text-red-700">*</span>
  </label>
  <div class="mt-2">
    <input
      ...
    />
  </div>
  <ul class="flex flex-col font-medium tracking-wide text-red-500 text-xs mt-1 ml-1">
    <li v-for="e in errors.username" v-html="e" />
  </ul>
</div>

这里我们使用 <li> 标签,使用 errors.username 将对应位置的错误消息依次显示在输入框下。

3.4 全局消息显示在表格顶端

<div
  v-if="hasErrors('global')"
  class="mb-5 rounded-md border-0 shadow-sm ring-1 ring-inset ring-gray-300 dark:ring-gray-500 px-4 py-2"
>
  <div class="flex text-red-700 dark:text-rose-400 space-x-2 mb-2">
    <p class="text-lg font-semibold">错误</p>
  </div>
  <ul class="flex flex-col font-medium tracking-wide text-sm list-disc pl-6">
    <li v-for="e in errors.global" v-html="e" />
  </ul>
</div>
<form>
  ...
</form>

这里使用 hasErrors('global') 来检测是否有全局错误,并在输入表顶端显示。

3.5 提交按钮在有错误时不允许点击

<button
  type="submit"
  class="flex w-full justify-center rounded-md px-3 py-1.5 text-sm font-semibold leading-6 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600 text-white shadow-sm hover:bg-indigo-500"
  :class="{
    'cursor-default pointer-events-none': hasErrors(),
    'bg-gray-400': hasErrors(),
    'bg-indigo-600': !hasErrors(),
  }"
>
  注册
</button>

这里使用 hasErrors() 来检测错误状态类中是否有任何错误,并据此启用或禁用按钮。

4. 完整案例

如果你需要一个完整案例,这里有:错误状态处理在用户注册场景的案例,前端开源,详见:Github,你也可以访问 Githubstar.pro 来查看网页的效果(一个 Github 互赞平台,前端按本文方式进行错误处理)。

感谢阅读,如果本文对你有帮助,可以订阅我的博客,我将继续分享前后端全栈开发的相关实用经验。祝你开发愉快

与Vue 3 后端错误消息处理范例相似的内容:

Vue 3 后端错误消息处理范例

前端如何存储处理后端返回的错误信息,并按不同来源绑定到页面,例如显示在不同输入框的周围。这样即可实现清晰的错误显示。

Flask API 如何接入 i18n 实现国际化多语言

​ 1. 介绍 上一篇文章分享了 Vue3 如何如何接入 i18n 实现国际化多语言,这里继续和大家分享 Flask 后端如何接入 i18n 实现国际化多语言。 用户请求 API 的多语言化其实有两种解决方案: 后端返回:"USER_ERROR" => 前端渲染:"用户错误" 后端接收请求中 "Ac

Vue - 入门

零:前端目前形势 前端的发展史 HTML(5)、CSS(3)、JavaScript(ES5、ES6):编写一个个的页面 -> 给后端(PHP、Python、Go、Java) -> 后端嵌入模板语法 -> 后端渲染完数据 -> 返回数据给前端 -> 在浏览器中查看 Ajax的出现 -> 后台发送异步请

CAS前后端分离解决方案

CAS前后端分离解决方案 关于CSS服务器的搭建和整合SpringBoot参考:CAS5.3服务器搭建与客户端整合SpringBoot以及踩坑笔记 环境与需求 后端:springboot 前端: vue + element UI 在登录后之后登录状态在系统中自主控制。 问题 当接口在CAS过滤器中时

在基于vue-next-admin的Vue3+TypeScript前端项目中,为了使用方便全局挂载对象接口

在基于vue-next-admin 的 Vue3+TypeScript 前端项目中,可以整合自己的 .NET 后端,前端操作一些功能的时候,为了使用方便全局挂载的对象接口,以便能够快速处理一些特殊的操作,如消息提示、辅助函数、正则测试等等。本篇随笔介绍在Vue3+TypeScript 前端项目中全局挂载对象$u,获得相关 $u_interface 的统一入口的接口信息。这样在组件或者页面中就可以方

基于SqlSugar的开发框架循序渐进介绍(18)-- 基于代码生成工具Database2Sharp,快速生成Vue3+TypeScript的前端界面和Winform端界面

我们开发一个系统,在保证风格统一、代码强壮、可读性强等基础上,还能够结合代码生成工具快速开发相关后端,以及各种前端界面的,无疑是非常好的,既保证了项目的代码质量,又能够极大的提高开发效率。代码生成工具Database2Sharp是在完善的开发项目上,抽取出数据变化的部分,通过演绎、归纳、反复演绎和归纳等提炼方式抽取出相关的规则,以工具的方式来快速提高生产率,使得我们在开发各种不同的项目上的时候,能

基于SqlSugar的开发框架循序渐进介绍(29)-- 快速构建系统参数管理界面-Vue3+ElementPlus

在随笔《基于SqlSugar的开发框架循序渐进介绍(28)-- 快速构建系统参数管理界面》中介绍了基于SqlSugar开发框架,构建系统参数管理的后端API部分,以及WInform界面部分内容,本篇随笔介绍基于Vue3+ElementPlus的前端界面开发过程。

Go版RuoYi

RuoYi-Go https://github.com/Kun-GitHub/RuoYi-Go 1. 关于我 个人介绍 2. 介绍 后端用Go写的RuoYi权限管理系统 (功能正在持续实现)后端 Gitee地址 3. 前端 RuoYi-Vue3 官方前端Vue3版 4. Go后端技术栈(持续在对齐项

基于Admin.NET框架的前端的一些改进和代码生成处理(1)

Admin.NET 是一套基于Furion/.NET 6实现的通用管理平台,模块插件式开发,框架包含了常规的权限管理、字典等管理模块,以及一些Vue3的Demo案例,框架前后端分离。后端基于基于Furion/.NET 6实现,底层集成SqlSugar;前端则是采用Vue-Next-Admin的前端框架,整体是一套非常不错的框架。本人比较喜欢研究一些技术框架,最近对该框架进行了一些研究分析,结合我自

终于搞懂了!原来 Vue 3 的 generate 是这样生成 render 函数的

前言 在之前的 面试官:来说说vue3是怎么处理内置的v-for、v-model等指令? 文章中讲了transform阶段处理完v-for、v-model等指令后,会生成一棵javascript AST抽象语法树。这篇文章我们来接着讲generate阶段是如何根据这棵javascript AST抽象