go-zero的配置及gorm、自定义返回等的引入以及扩展

工程维度(摘自官网)

.
├── consumer
├── go.mod
├── internal
│   └── model
├── job
├── pkg
├── restful
├── script
└── service

  • consumer: 队列消费服务
  • internal: 工程内部可访问的公共模块
  • job: cron job 服务
  • pkg: 工程外部可访问的公共模块
  • restful:HTTP 服务目录,下存放以服务为维度的微服务
  • script:脚本服务目录,下存放以脚本为维度的服务
  • service:gRPC 服务目录,下存放以服务为维度的微服务

服务维度(项目目录)(摘自官网)

example
├── etc
│   └── example.yaml
├── main.go
└── internal
    ├── config
    │   └── config.go
    ├── handler
    │   ├── xxxhandler.go
    │   └── xxxhandler.go
    ├── logic
    │   └── xxxlogic.go
    ├── svc
    │   └── servicecontext.go
    └── types
        └── types.go

  • example:单个服务目录,一般是某微服务名称
  • etc:静态配置文件目录
  • main.go:程序启动入口文件
  • internal:单个服务内部文件,其可见范围仅限当前服务
  • config:静态配置文件对应的结构体声明目录
  • handler:handler 目录,可选,一般 http 服务会有这一层做路由管理,handler 为固定后缀
  • logic:业务目录,所有业务编码文件都存放在这个目录下面,logic 为固定后缀
  • svc:依赖注入目录,所有 logic 层需要用到的依赖都要在这里进行显式注入
  • types:结构体存放目录

一、基本配置

1、数据库

1.1、docker-compose.yaml

version: '3'
services:mysql:container_name: mysql8image: mysql:${MYSQL_VERSION}restart: alwaysports:- ${MYSQL_PORT}:3306environment:TZ: Asia/ShanghaiMYSQL_ROOT_PASSWORD: 123456MYSQL_DATABASE: zero_demovolumes:- ${MYSQL_DIR}/data:/var/lib/mysql- ${MYSQL_DIR}/conf:/etc/mysql/conf.d/- ${MYSQL_DIR}/logs:/logscommand:--default-authentication-plugin=mysql_native_password--character-set-server=utf8mb4--collation-server=utf8mb4_general_ci--explicit_defaults_for_timestamp=true--lower_case_table_names=1Redis:container_name: redis6image: redis:${REDIS_VERSION}restart: alwaysvolumes:- ${REDIS_DIR}/data:/data- ${REDIS_DIR}/conf:/etc/redis/redis.confports:- ${REDIS_PORT}:6379command: redis-server /etc/redis/redis.conf

1.2、etc目录下的yaml文件配置

Name: demo   # 由api中的service名决定
Host: 0.0.0.0
Port: 8080Mysql:DataSource: root:111111@tcp(127.0.0.1:3306)/demo?charset=utf8mb4&parseTime=true&loc=Local#jwtAuth   go-zero 中内置了 JWT 的解密和验证功能,只需开启jwt使用即可
JwtAuth:AccessSecret: demo-aslfhsafsfsflaskfasfAccessExpire: 7200#Redis:
#  Address: 127.0.0.1:6379
#  Pass: 123456

1.3、internal中的config目录下的config.go文件配置

package configimport "github.com/zeromicro/go-zero/rest"//yaml文件中的配置参数经过解析后会解析到此文件中,所以此文件中的参数要与yaml中的参数相对应
type Config struct {rest.RestConfMysql struct {DataSource string}JwtAuth struct {AccessSecret stringAccessExpire int64}//Redis struct {//	Address string//	Pass    string//}
}

1.4、svc下servicecontext.go文件配置

package svcimport ("demo/common/database""demo/internal/config"
)type ServiceContext struct {Config      config.ConfigUserRepo    repo.UserRepo//Redis  *redis.Redis
}func NewServiceContext(c config.Config) *ServiceContext {dbConn := database.NewDB(c.Mysql.DataSource) //引入数据库得到数据库链接//newRedis := redis.New(c.Redis.Address, redisConfig(c))return &ServiceContext{Config:      c,UserRepo:    repo.NewUserRepo(dbConn), //调用数据库(数据库初始化,因为NewServiceContext()函数在main函数中已经调用初始化了)//Redis:  newRedis,}
}//func redisConfig(c config.Config) redis.Option {
//	return func(r *redis.Redis) {
//		r.Type = redis.NodeType
//		r.Pass = c.Redis.Pass
//	}
//}

1.5、引入gorm 链接数据库实现

package databaseimport ("gorm.io/driver/mysql""gorm.io/gorm""gorm.io/gorm/schema"
)type DBConn struct {ConnGorm *gorm.DB
}// NewDB  连接并初始化数据库
func NewDB(dataSource string) *DBConn {db, err := gorm.Open(mysql.Open(dataSource), &gorm.Config{DisableForeignKeyConstraintWhenMigrating: true,SkipDefaultTransaction:                   false,NamingStrategy: schema.NamingStrategy{SingularTable: true, // use singular table name, table for `User` would be `user` with this option enabled},})if err != nil {panic("连接数据库失败")}d := &DBConn{ConnGorm: db,}InitDB(db)return d
}
func InitDB(db *gorm.DB) {if err := db.AutoMigrate(&User{},); err != nil {panic(err)}
}

1.6、数据库操作业务代码

package repoimport ("demo/common/database""demo/internal/types"
)type user struct {db *database.DBConn
}type UserRepo interface {
}func NewUserRepo(conn *database.DBConn) UserRepo {return &user{conn}
}

2、自定义返回错误--返回数据

格式:code、msg、data标准错误

 推荐使用 code-data 统一响应格式用法(官方文档HTTP扩展),此法最简单只需替换即可

2.1、自定义返回错误 

在main函数中下面两种引入方式选择其一就行,好处是只需引入一次即可

2.1.1、官方的:

package mainimport ("demo/internal/config""demo/internal/handler""demo/internal/svc""flag""fmt""github.com/zeromicro/go-zero/core/conf""github.com/zeromicro/go-zero/core/logc""github.com/zeromicro/go-zero/rest"
)var configFile = flag.String("f", "etc/demo.yaml", "the config file")func main() {flag.Parse()//调试用,调试时使错误以plain(比较直观)的方式打印在终端var x logc.LogConfx.Encoding = "plain"logc.MustSetup(x)引入自定义返回错误,也可以引入自己构造的/*	httpx.SetErrorHandlerCtx(func(ctx context.Context, err error) (int, any) {switch e := err.(type) {case *errors.CodeMsg:return http.StatusOK, xhttp.BaseResponse[types.Nil]{Code: e.Code,Msg:  e.Msg,}default:return http.StatusInternalServerError, nil}})*/var c config.Configconf.MustLoad(*configFile, &c)server := rest.MustNewServer(c.RestConf)defer server.Stop()ctx := svc.NewServiceContext(c)handler.RegisterHandlers(server, ctx)fmt.Printf("Starting server at %s:%d...\n", c.Host, c.Port)server.Start()
}

2.1.2:自定义的

引入代码(写法一):

package errorzimport ("fmt"
)//**********************************
//按go-zero官方例子进行,用 /*  */ 标注这段需要配置在main函数中。/*
httpx.SetErrorHandlerCtx(func(ctx context.Context, err error) (int, any) {switch e := err.(type) {case *errorz.CodeMsg:return http.StatusOK, errorz.CodeMsg[errorz.Nil]{Code: e.Code,Msg:  e.Msg,}default:return http.StatusInternalServerError, nil}
})*/// 用来在main函数中赋值
type CodeMsg[T any] struct {Code int    `json:"code"`Msg  string `json:"msg"`
}//*****************************************************type StdCodeMsg struct {Code int    `json:"code"`Msg  string `json:"msg"`
}func (c *StdCodeMsg) Error() string {return fmt.Sprintf("code: %d, msg: %s", c.Code, c.Msg)
}// New creates a new StdCodeMsg.
func NewStdCodeMsg(code int, msg string) error {return &StdCodeMsg{Code: code, Msg: msg}
}func (c *StdCodeMsg) StdCodeMsg() any {return &StdCodeMsg{Code: c.Code,Msg:  c.Msg,}
}// Nil represents the predeclared value nil.
type Nil struct{}

引入代码(写法二):

package errorz//**********************************
//按go-zero官方例子进行,用 /*  */ 标注这段需要配置在main函数中用来返回错误。/*httpx.SetErrorHandlerCtx(func(ctx context.Context, err error) (int, interface{}) {switch e := err.(type) {case *errorx.BizError:return http.StatusOK, e.Data()default:return http.StatusInternalServerError, nil}
})
*/type CoMsg struct {Code int    `json:"code"`Msg  string `json:"msg"`
}type ErrorResponse struct {Code int    `json:"code"`Msg  string `json:"msg"`
}func NewCoMsg(code int, msg string) *CoMsg {return &CoMsg{Code: code,Msg:  msg,}
}func (e *CoMsg) Error() string {return e.Msg
}func (e *CoMsg) Data() any {return &ErrorResponse{e.Code,e.Msg,}
}

2.2、自定义返回数据 (最简单的是方式二)

方式一:需要在业务代码的返回函数中调用以下函数

// 返回数据---方式一
type RespCoMsgSuccess struct {Code    int    `json:"code"`Message string `json:"message"`Data    any    `json:"data"`
}func CoMsgSuccess(data interface{}) *RespCoMsgSuccess {return &RespCoMsgSuccess{200, "OK", data}
}// 返回数据---方式二
type StdResponse[T any] struct {// Code represents the business code, not the http status code.Code int `json:"code" xml:"code"`// Msg represents the business message, if Code = BusinessCodeOK,// and Msg is empty, then the Msg will be set to BusinessMsgOk.Msg string `json:"msg" xml:"msg"`// Data represents the business data.Data T `json:"data,omitempty" xml:"data,omitempty"`
}func StdSuccess(v any) StdResponse[any] {var resp StdResponse[any]resp.Code = 200resp.Msg = "OK"resp.Data = vreturn resp
}

方式二: code-data 统一响应格式用法(官方文档HTTP扩展

在 zeromicro 下有一个 x 仓库专门用于对 go-zero 的扩展,其中 HTTP 的扩展支持了:

  1. code-data 响应格式支持
  2. xml 响应支持
  3. code-msg error 类型支持

详情可参考 GitHub - zeromicro/x: This repository is part of the go-zero project but outside the main tree. It's developed under looser compatibility requirements than the go-zero project.This repository is part of the go-zero project but outside the main tree. It's developed under looser compatibility requirements than the go-zero project. - GitHub - zeromicro/x: This repository is part of the go-zero project but outside the main tree. It's developed under looser compatibility requirements than the go-zero project.https://github.com/zeromicro/x

package handlerimport ("net/http""demo/internal/logic""demo/internal/svc""demo/internal/types""github.com/zeromicro/go-zero/rest/httpx"xhttp "github.com/zeromicro/x/http"
)func loginHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {return func(w http.ResponseWriter, r *http.Request) {var req types.LoginRequestif err := httpx.Parse(r, &req); err != nil {// httpx.ErrorCtx(r.Context(), w, err)// code-data 响应格式xhttp.JsonBaseResponseCtx(r.Context(), w, err)return}l := logic.NewLoginLogic(r.Context(), svcCtx)resp, err := l.Login(&req)if err != nil {// code-data 响应格式xhttp.JsonBaseResponseCtx(r.Context(), w, err)} else {// code-data 响应格式xhttp.JsonBaseResponseCtx(r.Context(), w, resp)}}
}

 方式三:官方的定制模板法

可以通过官方的定制模板调用以下方法进行模板生成,也可在自己的返回包中实现,在handler中进行调用

//Handler中
func GreetHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {return func(w http.ResponseWriter, r *http.Request) {var req types.Requestif err := httpx.Parse(r, &req); err != nil {httpx.Error(w, err)return}l := logic.NewGreetLogic(r.Context(), svcCtx)resp, err := l.Greet(&req)response.Response(w, resp, err)}
}

package responseimport ("net/http""github.com/zeromicro/go-zero/rest/httpx"
)type Body struct {Code int         `json:"code"`Msg  string      `json:"msg"`Data interface{} `json:"data,omitempty"`
}func Response(w http.ResponseWriter, resp interface{}, err error) {var body Bodyif err != nil {body.Code = -1body.Msg = err.Error()} else {body.Msg = "OK"body.Data = resp}httpx.OkJson(w, body)
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/2623.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

实战:k8s证书续签-2023.6.19(测试成功)

实战:k8s证书续签-2023.6.19(测试成功) 目录 推荐文章 https://www.yuque.com/xyy-onlyone/aevhhf?# 《玩转Typora》 1、前言 k8s集群核心的证书有2套,还有1套非核心的(即使出问题也问题不大)。 ⚠️ 如果是kubeadm搭建的k8s集群,其有效期为…

如何快速判断是否在容器环境

在渗透测试过程中,我们的起始攻击点可能在一台虚拟机里或是一个Docker环境里,甚至可能是在K8s集群环境的一个pod里,我们应该如何快速判断当前是否在容器环境中运行呢? 当拿到shell权限,看到数字和字母随机生成的主机名…

【AICG】【Midjourney】AI自动生成图片的初接触

背景 现在是AI 比较流行,公司也推荐学习和了解AI的相关东西。 公司的内部培训中涉及到了Midjourney的简单讲解。 效果 由于我的账号注册的时候过了free窗口期: 现在要我花钱订阅: 只能看看其他人的帖子过过AI生成的瘾。 如图所示&#x…

Hugging News #0626: 音频课程更新、在线体验 baichuan-7B 模型、ChatGLM2-6B 重磅发布

每一周,我们的同事都会向社区的成员们发布一些关于 Hugging Face 相关的更新,包括我们的产品和平台更新、社区活动、学习资源和内容更新、开源库和模型更新等,我们将其称之为「Hugging News」,本期 Hugging News 有哪些有趣的消息…

LangChain_ChatGLM部署

环境准备 项目地址:https://github.com/imClumsyPanda/langchain-ChatGLM 下载chatglm-6b模型到本地,大约13G: https://huggingface.co/THUDM/chatglm-6b 里面的文件需要一个个手动点击下载,点击下载时,可能需要多次点…

git push 到 github 出现 fatal: Authentication failed 的处理方案

花了不少时间,记录一下这个问题。 问题截图: 解决方式(点击链接有官方文档说明): 将远程 URL 从 SSH 切换到 HTTPS(已经是https的直接忽略)生成细粒度token安装Github CLI缓存token 使用 g…

JAVA3

文章目录 注释核心机制JVM的功能 优缺点优点缺点 注释 例子: 核心机制 JVM的功能 优缺点 优点 缺点

Java基础-多线程JUC-多线程实现的三种形式

1. 第一种 继承Thread,重写run方法 public class demo1 {public static void main(String[] args) {/*** 多线程的第一种启动方式* 1. 定义一个类继承Thread* 2. 重写run方法* 3. 创建子类的对象,并启动线程*/MyThread myThread new MyThread();MyThrea…

【深度学习】3-3 神经网络的学习- 导数梯度

导数 导数就是表示某个瞬间的变化量,式子如下: 式子的左边,表示f(x)关于x的导数,即f(x)相对于x的变化程度。式子表示的导数的含义是,x的“微小变化”将导致函数f(x)的值在多大程度上发生变化。其中,表示…

MySQL ----主从复制、分离解析

文章目录 一、MySQL 主从复制1.1服务性能扩展方式1.2 MySQL的扩展什么是读写分离? 1.3为什么要读写分离呢?1.4什么时候要读写分离?1.5主从复制与读写分离1.6mysql支持的复制类型1.7主从复制的工作过程1.8MySQL 读写分离原理1.9目前较为常见的…

Flex写法系列-Flex布局之基本语法

以前的传统布局,依赖盒装模型。即 display position float 属性。但是对于比较特殊的布局就不太容易实现,例如:垂直居中。下面主要介绍flex的基本语法。 一、什么是Flex布局? Flex布局个人理解为弹性盒子,为盒装模型…

《kafka 核心技术与实战》课程学习笔记(八)

无消息丢失配置怎么实现? Kafka 只对“已提交”的消息(committed message)做有限度的持久化保证。 第一个核心要素是“已提交的消息”。 当 Kafka 的若干个 Broker 成功地接收到一条消息并写入到日志文件后,它们会告诉生产者程序…