怎么做垂直自营网站,百度网盘网址是多少,好看的wordpress图片主题,公众号开发者登录密码填哪个提示#xff1a; 所有体系课见专栏#xff1a;Go 项目开发极速入门实战课#xff1b;欢迎加入 云原生 AI 实战 星球#xff0c;12 高质量体系课、20 高质量实战项目助你在 AI 时代建立技术竞争力#xff08;聚焦于 Go、云原生、AI Infra#xff09;#xff1b;本节课最终… 提示 所有体系课见专栏Go 项目开发极速入门实战课欢迎加入 云原生 AI 实战 星球12 高质量体系课、20 高质量实战项目助你在 AI 时代建立技术竞争力聚焦于 Go、云原生、AI Infra本节课最终源码位于 fastgo 项目的 feature/s10 分支更详细的课程版本见Go 项目开发中级实战课16 | 基础 Go 包开发错误返回设计和实现 在 Go 项目开发中为了方便客户端处理返回排查错误还需要实现统一的错误返回。统一的错误返回包括以下 2 个方面
错误格式统一返回统一的错误格式方便客户端解析并获取错误自定义业务错误码HTTP 的错误码有限并且不适合业务错误码。所以在实际开发中还需要自定义业务错误码。
为了实现统一的错误返回接下来还需要实现错误包和自定义错误码。
错误返回方法
先来看下错误返回的方式。在 Go 项目开发中错误的返回方式通常有以下两种
始终返回 HTTP 200 状态码并在 HTTP 返回体中返回错误信息返回 HTTP 400 状态码Bad Request并在 HTTP 返回体中返回错误信息。
方式一成功返回返回体中返回错误信息
例如 Facebook API 的错误返回设计始终返回 200 HTTP 状态码
{error: {message: Syntax error \Field picture specified more than once. This is only possible before version 2.1\ at character 23: id,name,picture,picture,type: OAuthException,code: 2500,fbtrace_id: xxxxxxxxxxx}
}在上述错误返回的实现方式中HTTP 状态码始终固定返回 200仅需关注业务错误码整体实现较为简单。然而此方式存在一个明显的缺点对于每一次 HTTP 请求既需要检查 HTTP 状态码以判断请求是否成功还需要解析响应体以获取业务错误码从而判断业务逻辑是否成功。理想情况下我们期望客户端对成功的 HTTP 请求能够直接将响应体解析为需要的 Go 结构体并进行后续的业务逻辑处理而不用再判断请求是否成功。
方式二失败返回返回体中返回错误信息
Twitter API 的错误返回设计会根据错误类型返回对应的 HTTP 状态码并在返回体中返回错误信息和自定义业务错误码。成功的业务请求则返回 200 HTTP 状态码。例如
HTTP/1.1 400 Bad Request
x-connection-hash: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
set-cookie: guest_idxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Date: Thu, 01 Jun 2017 03:04:23 GMT
Content-Length: 62
x-response-time: 5
strict-transport-security: max-age631138519
Connection: keep-alive
Content-Type: application/json; charsetutf-8
Server: tsa_b{errors: [{code: 215,message: Bad Authentication data.}]
}方式二相比方式一对于成功的请求不需要再次判错。然而方式二还可以进一步优化整数格式的业务错误码 215 可读性较差用户无法从 215 直接获取任何有意义的信息。建议将其替换为语义化的字符串例如NotFound.PostNotFound。
Twitter API 返回的错误是一个数组在实际开发获取错误时需要先判断数组是否为空如不为空再从数组中获取错误开发复杂度较高。建议采用更简单的错误返回格式
{code: InvalidParameter.BadAuthenticationData,message: Bad Authentication data.
}需要特别注意的是message 字段会直接展示给外部用户因此必须确保其内容不包含敏感信息例如数据库的 id 字段、内部组件的 IP 地址、用户名等信息。返回的错误信息中还可以根据需要返回更多字段例如错误指引文档 URL 等。
fastgo 错误返回设计和实现
fastgo 项目错误返回格式采用了方式二在接口失败时返回对应的 HTTP/gRPC 状态码并在返回体中返回具体的错误信息例如
HTTP/1.1 404 Not Found
...
{code: NotFound.UserNotFound,message: User not found.
}制定错误码规范
错误码是直接暴露给用户的因此需要设计一个易读、易懂且规范化的错误码。在设计错误码时可以根据实际需求自行设计也可以参考其他优秀的设计方案。
一般来说当调研某项技术实现时建议优先参考各大公有云厂商的实现方式例如腾讯云、阿里云、华为云等。这些公有云厂商直接面向企业和个人专注于技术本身拥有强大的技术团队因此它们的设计与实现具有很高的参考价值。
经过调研此处采用了腾讯云 API 3.0 的错误码设计规范。腾讯云采用了两级错误码设计。以下是两级错误码设计相较于简单错误码如 215、InvalidParameter的优势
语义化 语义化的错误码可以通过名字直接反映错误的类型便于快速理解错误更加灵活 二级错误码的格式为平台级.资源级。其中平台级错误码是固定值用于指代某一类错误客户端可以利用该错误码进行通用错误处理。资源级错误码则用于更精确的错误定位。此外服务端既可根据需求自定义错误码也可使用默认错误码。
fastgo 项目预定义了一些错误码这些错误码位于以下 3 个文件中
internal/pkg/errorsx/code.go定义了一些通用的错误码internal/pkg/errorsx/user.go定义了用户相关的错误码internal/pkg/errorsx/post.go定义了博客相关的错误码。
一些错误码举例解释如下
错误码错误描述错误类型OK请求成功-InternalError内部错误1NotFound资源不存在0
上表中错误类型 0 代表客户端错误1 代表服务端错误2 代表客户端错误/服务端错误- 代表请求成功。
fastgo 错误包设计
为了避免与标准库的 errors 包命名冲突fastgo 项目的错误包命名为 errorsx寓意为“扩展的错误处理包”。
由于 fastgo 项目的错误包命名为 errorsx为保持命名一致性定义了一个名为 ErrorX 的结构体用于描述错误信息具体定义如下
// ErrorX 定义了 OneX 项目体系中使用的错误类型用于描述错误的详细信息.
type ErrorX struct {// Code 表示错误的 HTTP 状态码用于与客户端进行交互时标识错误的类型.Code int json:code,omitempty// Reason 表示错误发生的原因通常为业务错误码用于精准定位问题.Reason string json:reason,omitempty// Message 表示简短的错误信息通常可直接暴露给用户查看.Message string json:message,omitempty
}ErrorX 是一个错误类型因此需要实现 Error 方法
// Error 实现 error 接口中的 Error 方法.
func (err *ErrorX) Error() string {return fmt.Sprintf(error: code %d reason %s message %s, err.Code, err.Reason, err.Message)
}Error() 返回的错误信息中包含了 HTTP 状态码、错误发生的原因、错误信息。通过这些详尽的错误信息返回帮助开发者快速定位错误。
在 Go 项目开发中发生错误的原因有很多大多数情况下开发者希望将真实的错误信息返回给用户。因此还需要提供一个方法用来设置 ErrorX 结构体中的 Message 字段。为了满足上述诉求给 ErrorX 增加 WithMessage方法。实现方式如下是代码所示位于文件 internal/pkg/errorsx/errorsx.go 中
// ErrorX 定义了 fastgo 项目中使用的错误类型用于描述错误的详细信息.
type ErrorX struct {// Code 表示错误的 HTTP 状态码用于与客户端进行交互时标识错误的类型.Code int json:code,omitempty// Reason 表示错误发生的原因通常为业务错误码用于精准定位问题.Reason string json:reason,omitempty// Message 表示简短的错误信息通常可直接暴露给用户查看.Message string json:message,omitempty
}// New 创建一个新的错误.
func New(code int, reason string, format string, args ...any) *ErrorX {return ErrorX{Code: code,Reason: reason,Message: fmt.Sprintf(format, args...),}
}// Error 实现 error 接口中的 Error 方法.
func (err *ErrorX) Error() string {return fmt.Sprintf(error: code %d reason %s message %s, err.Code, err.Reason, err.Message)
}// WithMessage 设置错误的 Message 字段.
func (err *ErrorX) WithMessage(format string, args ...any) *ErrorX {err.Message fmt.Sprintf(format, args...)return err
}在 Go 项目开发中通常需要将一个 error 类型的错误 err解析为 *ErrorX 类型并获取 *ErrorX 中的 Code 字段和 Reason 字段的值。Code 字段可用来设置 HTTP 状态码Reason 字段可用来判断错误类型。为此errorsx 包实现了 FromError 方法具体实现如下所示。
// FromError 尝试将一个通用的 error 转换为自定义的 *ErrorX 类型.
func FromError(err error) *ErrorX {// 如果传入的错误是 nil则直接返回 nil表示没有错误需要处理.if err nil {return nil}// 检查传入的 error 是否已经是 ErrorX 类型的实例.// 如果错误可以通过 errors.As 转换为 *ErrorX 类型则直接返回该实例.if errx : new(ErrorX); errors.As(err, errx) {return errx}// 默认返回未知错误错误. 该错误代表服务端出错return New(ErrInternal.Code, ErrInternal.Reason, err.Error())
}fastgo 错误码定义
在实现了 errorsx 错误包之后便可以根据需要预定义项目需要的错误。这些错误可以在代码中便捷的引用。通过直接引用预定义错误不仅可以提高开发效率还可以保持整个项目的错误返回是一致的。
fastgo 的预定义错误定义在 internal/pkg/errorsx 目录下。一些基础错误定义如下
// errorsx 预定义标准的错误.
var (// OK 代表请求成功.OK ErrorX{Code: http.StatusOK, Message: }// ErrInternal 表示所有未知的服务器端错误.ErrInternal ErrorX{Code: http.StatusInternalServerError, Reason: InternalError, Message: Internal server error.}// ErrNotFound 表示资源未找到.ErrNotFound ErrorX{Code: http.StatusNotFound, Reason: NotFound, Message: Resource not found.}
)更完整的预定义错误可直接查看 master 分支中internal/pkg/errorsx 中的错误定义文件。
fastgo 错误返回规范
为了标准化接口错误返回fastgo 项目提供了通用的接口返回函数该函数可以解析错误并返回固定的错误返回格式。实现代码位于 internal/pkg/core/core.go 文件中代码内容如下
package coreimport (net/httpgithub.com/gin-gonic/gingithub.com/onexstack/fastgo/internal/pkg/errorsx
)// ErrorResponse 定义了错误响应的结构
// 用于 API 请求中发生错误时返回统一的格式化错误信息.
type ErrorResponse struct {// 错误原因标识错误类型Reason string json:reason,omitempty// 错误详情的描述信息Message string json:message,omitempty
}// WriteResponse 是通用的响应函数.
// 它会根据是否发生错误生成成功响应或标准化的错误响应.
func WriteResponse(c *gin.Context, err error, data any) {if err ! nil {// 如果发生错误生成错误响应errx : errorsx.FromError(err) // 提取错误详细信息c.JSON(errx.Code, ErrorResponse{Reason: errx.Reason,Message: errx.Message,})return}// 如果没有错误返回成功响应c.JSON(http.StatusOK, data)
}上述代码定义了一个通用的错误返回结构体ErrorResponse。ErrorResponse 中包含了错误返回的原因和消息。
在 API 接口返回时会调用 WriteResponse 函数。WriteResponse 函数会判断是否发生了错误如果发生了错误会解析错误为 errorsx.ErrorX 类型的错误并从中获取 Code 和 Reason 字段并设置给ErrorResponse 类型的变量。如果没有发生错误直接返回自定义数据。
返回统一的错误格式
上面我们开发了错误包、自定义了错误返回码并提供了接口返回函数 WriteResponse。接下来就可以使用 WriteResponse 来返回接口数据。
更新 internal/apiserver/server.go 文件中的 NoRoute 返回实现和 /healthz 接口的返回实现。代码变更如下
package apiserverimport (...github.com/onexstack/fastgo/internal/pkg/coregithub.com/onexstack/fastgo/internal/pkg/errorsx...
)
...
// NewServer 根据配置创建服务器.
func (cfg *Config) NewServer() (*Server, error) {...// 注册 404 Handler.engine.NoRoute(func(c *gin.Context) {core.WriteResponse(c, errorsx.ErrNotFound.WithMessage(Page not found), nil)})// 注册 /healthz handler.engine.GET(/healthz, func(c *gin.Context) {core.WriteResponse(c, nil, map[string]string{status: ok})})...
}上述代码通过调用 WriteResponse 返回了标准的错误返回。并且在 NoRoute 路由函数中还指定了要返回的自定义错误码 ErrNotFound并给 ErrNotFound 设置了自定义返回消息。
编译并测试
运行以下命令编译并测试错误返回功能
$ ./build.sh
$ _output/fg-apiserver -c configs/fg-apiserver.yaml打开一个新的 Linux 终端执行以下命令
$ curl http://127.0.0.1:6666/noroute
{reason:NotFound,message:Page not found}可以看到当我们方位一个不存在的路径时返回了自定义错误码及自定义消息。