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 后端错误消息处理范例的更多相关文章

  1. ASP.NET WebApi+Vue前后端分离之允许启用跨域请求

    前言: 这段时间接手了一个新需求,将一个ASP.NET MVC项目改成前后端分离项目.前端使用Vue,后端则是使用ASP.NET WebApi.在搭建完成前后端框架后,进行接口测试时发现了一个前后端分 ...

  2. Vue之前后端交互

    Vue之前后端交互 一.前后端交互模式 接口调用方式 原生ajax 基于jQuery的ajax fetch axios 异步 JavaScript的执行环境是「单线程」 所谓单线程,是指JS引擎中负责 ...

  3. 三、vue前后端交互(轻松入门vue)

    轻松入门vue系列 Vue前后端交互 六.Vue前后端交互 1. 前后端交互模式 2. Promise的相关概念和用法 Promise基本用法 then参数中的函数返回值 基于Promise处理多个A ...

  4. vue根据后端菜单自动生成路由(动态路由)

    vue根据后端菜单自动生成路由(动态路由) router.js import Vue from 'vue' import Router from 'vue-router' import store f ...

  5. 解决Django+Vue前后端分离的跨域问题及关闭csrf验证

      前后端分离难免要接触到跨域问题,跨域的相关知识请参:跨域问题,解决之道   在Django和Vue前后端分离的时候也会遇到跨域的问题,因为刚刚接触Django还不太了解,今天花了好长的时间,查阅了 ...

  6. Flask + vue 前后端分离的 二手书App

    一个Flask + vue 前后端分离的 二手书App 效果展示: https://blog.csdn.net/qq_42239520/article/details/88534955 所用技术清单 ...

  7. 喜大普奔,两个开源的 Spring Boot + Vue 前后端分离项目可以在线体验了

    折腾了一周的域名备案昨天终于搞定了. 松哥第一时间想到赶紧把微人事和 V 部落部署上去,我知道很多小伙伴已经等不及了. 1. 也曾经上过线 其实这两个项目当时刚做好的时候,我就把它们部署到服务器上了, ...

  8. 基于Spring Boot+Spring Security+JWT+Vue前后端分离的开源项目

    一.前言 最近整合Spring Boot+Spring Security+JWT+Vue 完成了一套前后端分离的基础项目,这里把它开源出来分享给有需要的小伙伴们 功能很简单,单点登录,前后端动态权限配 ...

  9. 两个开源的 Spring Boot + Vue 前后端分离项目

    折腾了一周的域名备案昨天终于搞定了. 松哥第一时间想到赶紧把微人事和 V 部落部署上去,我知道很多小伙伴已经等不及了. 1. 也曾经上过线 其实这两个项目当时刚做好的时候,我就把它们部署到服务器上了, ...

  10. beego-vue URL重定向(beego和vue前后端分离开发,beego承载vue前端分离页面部署)

    具体过程就不说,是搞这个的自然会动,只把关键代码贴出来. beego和vue前后端分离开发,beego承载vue前端分离页面部署 // landv.cnblogs.com //没有授权转载我的内容,再 ...

随机推荐

  1. iceoryx源码阅读(三)——共享内存通信(一)

    目录 0 导引 1 整体通信结构 2 RelativePointer 2.1 原理 2.2 PointerRepository 2.3 构造函数 2.4 get函数 3 ShmSafeUnmanage ...

  2. 用 C 语言开发一门编程语言 — 变量元素设计

    目录 文章目录 目录 前文列表 变量 变量语法规则 变量的读取和存储 将变量加入 Lisp Value 体系 变量的计算 变量的定义与赋值 异常处理优化 源代码 前文列表 <用 C 语言开发一门 ...

  3. MinIo对象存储文件上传,下载,预览,批量上传,创建桶等

    MinIo 操作工具类 MinIo 旧中文文档 MinIo 英文文档 MinIo 官网地址 https://min.io/ package com.ming.utils; import io.mini ...

  4. NumPy 正态分布与 Seaborn 可视化指南

    正态分布(高斯分布) 简介 正态分布(也称为高斯分布)是一种非常重要的概率分布,它描述了许多自然和人为现象的数据分布情况.正态分布的形状呈钟形,其峰值位于平均值处,两侧对称下降. 特征 正态分布可以用 ...

  5. mvc5接口报错:The JSON request was too large to be deserialized的一种原因

    是mvc5版本的接口,接口使用了dynamic接收数组,json对象数组只有56个,length长度不到10万,但是提交就报The JSON request was too large to be d ...

  6. Servlet转发与重定向的资源路径问题解析

    一. 问题引入 转发和重定向可以说是Servlet中最重要的知识点也不为过,因为它决定着整个向Servlet体系中,执行流程的问题.      转发: request.getRequestDispat ...

  7. 一个免费、时尚、强大的 Windows GitHub 客户端

    前言 今天大姚给大家分享一个.NET开源(MIT License).免费.时尚.功能强大的 Windows GitHub 客户端:FluentHub. 工具功能 多任务标签页. 上下文菜单扩展. 对问 ...

  8. vue.js的M-V-VM思想

    MVVM 是Model-View-ViewModel 的缩写,它是一种基于前端开发的架构模式. Model 指代的就是vue对象的data属性里面的数据.这里的数据要显示到页面中. View 指代的就 ...

  9. CF914C

    problem & blog 数位 dp 模板题. 经过一次操作,可以把 \(n\) 变成一个小于 \(10^3\) 的数. 所以我们可以把所有小于 \(10^3\) 的数操作的次数全部处理出 ...

  10. FFmpeg开发笔记全目录(FFmpeg开发实战详解,含直播系统的搭建过程)

    ​记录下FFmpeg的学习笔记目录,完整的FFmpeg开发实战内容详见<FFmpeg开发实战:从零基础到短视频上线>一书. 下面是补充的FFmpeg开发笔记内容目录,主要是对<FFm ...