当前位置: 首页 > news >正文

一个go的支持多语言的error自动生成插件

大家好,我是peachesTao,今天给大家推荐一个go的支持多语言的error自动生成的插件,插件主页可以访问下方链接。

在一个多语言国际化的项目中,后端接口返回给前端的错误描述也需要国际化,我们来看一下后端给前端返回多语言错误描述的实现方式有哪些。

常规实现

服务端将错误码和不同语言的错误描述硬编码在代码中,通过前端从http head中传过来的language来决定是返回中文还是英文。

1、定义Error结构体

该结构体实现标准库的error接口,实现自定义error

type Error struct {Code intMsg  string
}func (e *Error) Error() string {return fmt.Sprintf("%d,%s", e.Code, e.Msg)
}

2、定义错误码和错误描述map

const (Err_Code_Success       = 0Err_Code_UnKnown       = -1Err_Code_InValid_Phone = 10001
)const (Language_Chinese = 0 //中文Language_Enligh  = 1 //英文
)//不同语言对应的错误描述
var errMap = map[int]map[int]string{Language_Chinese: {Err_Code_Success:       "成功",Err_Code_InValid_Phone: "手机号格式不正确",Err_Code_UnKnown:       "未知错误",},Language_Enligh: {Err_Code_Success:       "success",Err_Code_InValid_Phone: "invalid phone no",Err_Code_UnKnown:       "unknown err",},
}

3、申明一个用户注册的api

根据客户端传过来的http header中的language的值决定返回中文还是英文的错误描述

func main() {http.HandleFunc("/user/register", func(w http.ResponseWriter, r *http.Request) {languageStr := r.Header.Get("language")language, _ := strconv.Atoi(languageStr)values, _ := url.ParseQuery(r.URL.RawQuery)phone := values["phone"][0]err := checkPhone(phone)response(w, language, err)})http.ListenAndServe(":8080", nil)
}func response(w http.ResponseWriter, language int, err error) {e := &Error{Code: Err_Code_Success}if err != nil {var ok boolif e, ok = err.(*Error); !ok {e = &Error{Code: Err_Code_UnKnown}}}msg := errMap[language][e.Code]res := make(map[string]interface{})res["code"] = e.Coderes["msg"] = msgjson, _ := json.Marshal(res)w.WriteHeader(200)w.Write(json)
}func checkPhone(phoneNo string) error {if len(phoneNo) != 11 {return &Error{Code: Err_Code_InValid_Phone}}return nil
}

我们通过curl命令来看看效果

语言设置为中文时:

curl -H "language:0" "http://127.0.0.1:8080/user/register?phone=187111111112"
{"code":10001,"msg":"手机号格式不正确"}

语言设置为英文时:

curl -H "language:1" "http://127.0.0.1:8080/user/register?phone=187111111112"
{"code":10001,"msg":"invalid phone no"}

这种实现方式确实能满足业务需求,但是有下面几个缺点:

  • 当要将手机号格式不正确的描述改时需要修改代码
  • 当添加新的错误时需要改动多个地方代码:添加新的错误码和在errMap中添加对应语言的错误描述,容易遗漏
  • 当添加新的语言时要向errMap添加所有错误码的新语言错误描述,容易遗漏

一旦涉及到修改代码就存在出现bug的风险,有没有一种更优雅的方案,尽量减少修改代码?

有人会想到将错误描述放在json文件中维护,这种方案只是在修改错误描述时比较便利,不需要改动业务代码,但在新增错误和新语言时存在同样的问题。

下面我们来看看通过go-error-generator插件的方法来实现

更优雅的实现

go-error-generator是一个通过protobuf文件的Enum对象自动生成Error的插件,通过在扩展的EnumValueOptions中定义多个option轻松实现error的多语言。

它包含如下功能:

  • 根据Enum定义的errCode和msg自动生成error;
  • 支持定义多个EnumValueOption,实现多语言;
  • 支持error合并功能;
  • 支持自定义Error结构体、error Code和Msg的名称;

关于插件的原理和其他细节可以访问github主页了解。

我们回到刚才那个需求,用插件的方式怎么实现错误多语言

1、定义error模板

删除代码中的的Error结构体,取代的是在protobuf中定义,新建一个protobuf文件,取名为error.proto,在这里自定义error结构体和语言标识。

其中:

  • msg:默认的语言标识,在错误码定义文件中没有定义其他语言的错误描述时就用它的错误描述
  • msg_english:英文标识,当然你也可以取别的名字
syntax = "proto3";
package errors;
option go_package = "github.com/classtorch/go-error-generator-examples/internal/errors";
import "google/protobuf/descriptor.proto";message Error {int32 code = 1;string msg = 2;
};
extend google.protobuf.EnumValueOptions {string msg = 1108;string msg_english = 1109;
}

2、定义错误码和错误描述

新建一个protobuf文件,取名为account.proto
导入上面定义好的error.proto,自定义msg和msg_english对应的错误描述

syntax = "proto3";
package uclass.service.account;
option go_package = "/golang/account";
import "errors/errors.proto";enum ErrorCode {SUCCESS = 0 [(errors.msg) = "成功", (errors.msg_english) = "success"];  // 成功UnKnown = -1 [(errors.msg) = "未知错误", (errors.msg_english) = "unknown err"]; // 账号不存在InValid_Phone = 10001 [(errors.msg) = "手机号格式不正确", (errors.msg_english) = "invalid phone no"];  // 登录失效,请重新登录
}

3、通过插件生成代码

该插件需要安装go和protobuf运行环境

  • go
  • protoc
  • protoc-gen-go

安装好运行环境后再安装go-error-generator插件

go install github.com/classtorch/go-error-generator/protoc-gen-go-error-generator

安装好后执行下面脚本生成代码

protoc --go-error-generator_out=:. \--go-error-generator_opt descriptor_file=errors/errors.proto \--go-error-generator_opt merge_error=false \--go-error-generator_opt merge_error_path=golang/errors \--go_out=. -I . account.proto

插件自动生成的代码如下,包含error对象和error map

var (SUCCESS       = &errors.Error{Code: 0, Msg: "成功"}           //成功UnKnown       = &errors.Error{Code: -1, Msg: "未知错误"}        //未知错误InValid_Phone = &errors.Error{Code: 10001, Msg: "手机号格式不正确"} //手机号格式不正确
)var (Msg = map[int32]*errors.Error{0:     &errors.Error{Code: 0, Msg: "成功"},-1:    &errors.Error{Code: -1, Msg: "未知错误"},10001: &errors.Error{Code: 10001, Msg: "手机号格式不正确"},}Msg_English = map[int32]*errors.Error{0:     &errors.Error{Code: 0, Msg: "success"},-1:    &errors.Error{Code: -1, Msg: "unknown err"},10001: &errors.Error{Code: 10001, Msg: "invalid phone no"},}
)

4、使用生成的error对象

使用生成的error对象和error map改写response和checkPhone方法

func response(w http.ResponseWriter, language int, err error) {e := account.SUCCESSvar ok boolif err != nil {if e, ok = err.(*errors.Error); !ok {e = account.UnKnown}}if language == Language_Chinese {if e, ok = account.Msg[e.Code]; !ok {e = account.UnKnown}} else if language == Language_Enligh {if e, ok = account.Msg_English[e.Code]; !ok {e = account.UnKnown}}res := make(map[string]interface{})res["code"] = e.Coderes["msg"] = e.Msgjson, _ := json.Marshal(res)w.WriteHeader(200)w.Write(json)
}func checkPhone(phoneNo string) error {if len(phoneNo) != 11 {return account.InValid_Phone}return nil
}

完整的代码可以访问go-error-generator-examples项目进行了解

我们来看下这是实现方式的优点

  • 当我们需要修改某个错误描述时直接在account.proto文件中修改,无须修改代码
  • 当需要增加新的错误时直接在account.proto文件中定义,生成代码后直接在业务代码中引用即可
  • 当添加新的语言时只需要在error.proto中增加新的语言标识即,然后在account.proto中引入即可

可以看出对于第一个和和第三个需求来说只需要修改protobuf文件,重新生成代码就可以,无须修改业务代码。第二个需求也只是简单的引入新的错误对象。

由于该插件是基于protobuf实现的,如果项目中没有使用prorobuf技术栈的话会带来一些引入成本。不过这点成本相对于频繁修改业务代码还是值得的。

相关链接

go-error-generator

go-error-generator-examples

相关文章:

一个go的支持多语言的error自动生成插件

大家好,我是peachesTao,今天给大家推荐一个go的支持多语言的error自动生成的插件,插件主页可以访问下方链接。 在一个多语言国际化的项目中,后端接口返回给前端的错误描述也需要国际化,我们来看一下后端给前端返回多语…...

wireshark抓包新手使用教程(超详细)

一、简介 Wireshark是一款非常流行的网络封包分析软件,可以截取各种网络数据包,并显示数据包详细信息。 为了安全考虑,wireshark只能查看封包,而不能修改封包的内容,或者发送封包。 wireshark能获取HTTP,也…...

平均列顺序对列排斥能的影响

( A, B )---3*30*2---( 1, 0 )( 0, 1 ) 让网络的输入只有3个节点,AB训练集各由5张二值化的图片组成,让A有6个1,B有4个1,并且让这10个1的位置没有重合。比较迭代次数的顺序。 其中有9组数据 差值结构 A-B 迭代次数 构造平均列 …...

微信小程序-处理ios无法播放语音的问题

背景 框架:tarovue3 问题:今天搞小程序语音播放功能,开放工具播放正常,但是到ios手机上调试时无法播放,在网上找到个好办法 解决方案 核心代码 Taro.setInnerAudioOption({obeyMuteSwitch: false // 解决有一些IOS无…...

区块链 2.0笔记

区块链 2.0 以太坊概述 相对于比特币的几点改进 缩短出块时间至10多秒ghost共识机制mining puzzle BTC:计算密集型ETH:memory-hard(限制ASIC) proof of work->proof of stake对智能合约的支持 BTC:decentralized currencyETH:decentral…...

深入理解Vue响应式系统:数据绑定探索

🌷🍁 博主猫头虎 带您 Go to New World.✨🍁 🦄 博客首页——猫头虎的博客🎐 🐳《面试题大全专栏》 文章图文并茂🦕生动形象🦖简单易学!欢迎大家来踩踩~🌺 &a…...

web流程自动化详解

今天给大家带来Selenium的相关解释操作 一、Selenium Selenium是一个用于自动化Web浏览器操作的开源工具和框架。它提供了一组API(应用程序接口),可以让开发人员使用多种编程语言(如Java、Python、C#等)编写测试脚本&…...

什么是框架?为什么要学框架?

一、什么是框架 框架是整个或部分应用的可重用设计,是可定制化的应用骨架。它可以帮开发人员简化开发过程,提高开发效率。 项目里有一部分代码:和业务无关,而又不得不写的代码>框架 项目里剩下的部分代码:实现业务…...

什么是 Sass?

Sass 介绍 什么是 Sass? 官方标语 世界上最成熟、最稳定、最强大的专业级 CSS 扩展语言。怎么理解这句话呢?我们平时写的 CSS 代码可以理解为静态样式语言,而 Scss 就是动态样式语言,何为动态?就是让你写 CSS 跟写 …...

Kotlin~Memento备忘录模式

概念 备忘录模式是一种行为型设计模式,用于捕获和存储对象的内部状态,并在需要时将对象恢复到之前的状态。 备忘录模式允许在不暴露对象内部实现细节的情况下,对对象进行状态的保存和恢复。 角色介绍 Originator:原发器&#x…...

单链表的多语言表达:C++、Java、Python、Go、Rust

单链表 是一种链式数据结构,由一个头节点和一些指向下一个节点的指针组成。每个节点包含一个数据元素和指向下一个节点的指针。头节点没有数据,只用于表示链表的开始位置。 单链表的主要操作包括: 添加元素:在链表的头部添加新…...

微信小程序 background-image直接设置本地图片路径,编辑器正常显示,真机运行不显示解决方法

项目场景 微信小程序,设置background-image直接设置本地图片路径。 问题描述 编辑器正常显示,真机运行不显示 原因分析 background-image只能用网络url或者base64图片编码。 解决方案 1、将本地图片转为网络url后设置到background-image上 例如&…...

SQLite Studio 连接 SQLite数据库

1、在SQLite中创建数据库和表 1.1、按WINR,打开控制台,然后把指引到我们的SQLite的安装路径,输入D:,切换到D盘,cd 地址,切换到具体文件夹,输入“sqlite3”,启动服务 1.2、创建数据库…...

【业务功能篇58】Springboot + Spring Security 权限管理 【中篇】

4.2.3 认证 4.2.3.1 什么是认证(Authentication) 通俗地讲就是验证当前用户的身份,证明“你是你自己”(比如:你每天上下班打卡,都需要通过指纹打卡,当你的指纹和系统里录入的指纹相匹配时&…...

Docker挂载目录失败问题解决

天行健,君子以自强不息;地势坤,君子以厚德载物。 每个人都有惰性,但不断学习是好好生活的根本,共勉! 文章均为学习整理笔记,分享记录为主,如有错误请指正,共同学习进步。…...

css中隐藏页面中某一个元素有什么方法?

我们可以使用css的z-index属性,将元素的-index去给它设置一个负值,使它隐藏在其他元素的后面。使用css样式进行隐藏我们可以使用display这个属性。(1)使用display:none完全进行隐藏元素,并且不占据空间也不会影响页面布…...

Unity 多语言问题C#篇

DateTime.ToString()不同语言环境问题 问题描述:PlayerPrefs.SetString("timeKey", DateTime.Now.ToString());切换系统语言后DateTime.Parse(PlayerPrefs.GetString("timeKey"));报错FormatException: String was not recognized as a valid D…...

深度学习和神经网络

人工神经网络分为两个阶段: 1 :接收来自其他n个神经元传递过来的信号,这些输入信号通过与相应的权重进行 加权求和传递给下个阶段。(预激活阶段) 2:把预激活的加权结果传递给激活函数 sum :加权 f:激活…...

在CSDN学Golang云原生(Kubernetes Volume)

一,Volume 与 configMap Kubernetes 中的 Volume 和 ConfigMap 都是 Kubernetes 中常用的资源对象。它们可以为容器提供持久化存储和配置文件等。 Volume 可以将容器内部的文件系统挂载到宿主机上,也可以将多个容器间共享一个 Volume,并且 …...

第十五章 友元 异常和其他

RTTI RTTI是什么 RTTI是运行阶段类型识别,通过运行时类型识别,程序能够使用基类的指针或者引用来检查这些指针或者引用所指向的对象的实际派生类型。 RTTI的三个元素 dynamic_cast运算符 dynamic_cast概念: dynamic_cast运算符能够将基…...

网络六边形受到攻击

大家读完觉得有帮助记得关注和点赞!!! 抽象 现代智能交通系统 (ITS) 的一个关键要求是能够以安全、可靠和匿名的方式从互联车辆和移动设备收集地理参考数据。Nexagon 协议建立在 IETF 定位器/ID 分离协议 (…...

19c补丁后oracle属主变化,导致不能识别磁盘组

补丁后服务器重启,数据库再次无法启动 ORA01017: invalid username/password; logon denied Oracle 19c 在打上 19.23 或以上补丁版本后,存在与用户组权限相关的问题。具体表现为,Oracle 实例的运行用户(oracle)和集…...

【Go】3、Go语言进阶与依赖管理

前言 本系列文章参考自稀土掘金上的 【字节内部课】公开课,做自我学习总结整理。 Go语言并发编程 Go语言原生支持并发编程,它的核心机制是 Goroutine 协程、Channel 通道,并基于CSP(Communicating Sequential Processes&#xff0…...

Android Bitmap治理全解析:从加载优化到泄漏防控的全生命周期管理

引言 Bitmap(位图)是Android应用内存占用的“头号杀手”。一张1080P(1920x1080)的图片以ARGB_8888格式加载时,内存占用高达8MB(192010804字节)。据统计,超过60%的应用OOM崩溃与Bitm…...

Rapidio门铃消息FIFO溢出机制

关于RapidIO门铃消息FIFO的溢出机制及其与中断抖动的关系,以下是深入解析: 门铃FIFO溢出的本质 在RapidIO系统中,门铃消息FIFO是硬件控制器内部的缓冲区,用于临时存储接收到的门铃消息(Doorbell Message)。…...

优选算法第十二讲:队列 + 宽搜 优先级队列

优选算法第十二讲:队列 宽搜 && 优先级队列 1.N叉树的层序遍历2.二叉树的锯齿型层序遍历3.二叉树最大宽度4.在每个树行中找最大值5.优先级队列 -- 最后一块石头的重量6.数据流中的第K大元素7.前K个高频单词8.数据流的中位数 1.N叉树的层序遍历 2.二叉树的锯…...

蓝桥杯 冶炼金属

原题目链接 🔧 冶炼金属转换率推测题解 📜 原题描述 小蓝有一个神奇的炉子用于将普通金属 O O O 冶炼成为一种特殊金属 X X X。这个炉子有一个属性叫转换率 V V V,是一个正整数,表示每 V V V 个普通金属 O O O 可以冶炼出 …...

C++.OpenGL (14/64)多光源(Multiple Lights)

多光源(Multiple Lights) 多光源渲染技术概览 #mermaid-svg-3L5e5gGn76TNh7Lq {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-3L5e5gGn76TNh7Lq .error-icon{fill:#552222;}#mermaid-svg-3L5e5gGn76TNh7Lq .erro…...

ubuntu22.04有线网络无法连接,图标也没了

今天突然无法有线网络无法连接任何设备,并且图标都没了 错误案例 往上一顿搜索,试了很多博客都不行,比如 Ubuntu22.04右上角网络图标消失 最后解决的办法 下载网卡驱动,重新安装 操作步骤 查看自己网卡的型号 lspci | gre…...

沙箱虚拟化技术虚拟机容器之间的关系详解

问题 沙箱、虚拟化、容器三者分开一一介绍的话我知道他们各自都是什么东西,但是如果把三者放在一起,它们之间到底什么关系?又有什么联系呢?我不是很明白!!! 就比如说: 沙箱&#…...