Go-zero(api部分)

目录

api的语法:

type:用于定义请求/响应体

service:定义HTTP服务

@server:控制生成HTTP服务时候的meta信息

根据api文档生成最小HTTP服务

目录结构

api响应封装


api的语法:

首先定义一个api文档

type:用于定义请求/响应体
type LoginRequest {
	Username string `json:"username"`
	Password string `json:"password"`
}

type UserInfoResponse {
	UserId   uint   `json:"userId"`
	Username string `json:"username"`
}

其中LoginRequest表示登录的请求体,要求包含string类型的username和password

语法类似于go的type struct

也支持写在一起

type(
	LoginRequest {
		Username string `json:"username"`
		Password string `json:"password"`
	}
	 UserInfoResponse {
		UserId   uint   `json:"userId"`
		Username string `json:"username"`
	}
)

效果是一样的

service:定义HTTP服务
service users {
	@handler login
	post /login (LoginRequest) returns (string)
}

service users表示定义了一个名称为users的微服务,最后的入口文件名称也会是users.go

@handler 用于对生成的文件和方法命名

post /login (LoginRequest) returns (string)

  • post:表示这是一个post请求
  • /login:表示请求路由
  • (Request) returns (Response) 表示根据Request请求体,返回响应体Response,样例中的就是根据LoginRequest登录请求体返回一个字符串,当然这个后面也是可以自定义的,也可以不需要请求体,写法就是这样post /login returns (string)
@server:控制生成HTTP服务时候的meta信息
@server (
	prefix: /api/users
	jwt:    Auth
)

比如说prefix: /api/users 对该定义后面的services都加上路由前缀/api/users

jwt: Auth用于jwt鉴权(目前自己测试使用只成功了token的创建,token解析还没有成功)

目前支持的功能

  1. 路由分组
  2. 中间件声明
  3. 路由前缀
  4. 超时配置
  5. jwt 鉴权开关

如果需要对多个服务设置不同的meta信息

则可以这样写

type(
	LoginRequest {
		Username string `json:"username"`
		Password string `json:"password"`
	}
	 UserInfoResponse {
		UserId   uint   `json:"userId"`
		Username string `json:"username"`
	}
)

@server (
	prefix: /api/users
)
service users {
	@handler login
	post /login (LoginRequest) returns (string)
}

@server (
	prefix: /api/users
	jwt:    Auth
)
service users {
	@handler userInfo
	get /info returns (UserInfoResponse)
}

//goctl api go -api user.api -dir .

第一个server管理login服务

第二个server管理userInfo服务

根据api文档生成最小HTTP服务

生成代码:

复制代码
goctl api go -api user.api -dir .

goctl为关键字。api go,表示根据api文档生成go文档,-api用于指定api文件的位置这里的位置为(当前目录下的user.api文件,-dir用于指定go文档的生成位置,"."表示在当前目录下生成

目录结构

最终会在指定位置生成这样一个目录结构:

从上到下一个一个解释的话:

  • etc/user.yaml:用来保存基础的配置文件服务名称,主机地址,以及端口号

  • internal/config/config.go:主要用于加载保存user.yaml中的的数据以供使用

  • hander/loginhandler.go:用于处理login请求,过程大概就是下面这样的

    func loginHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
    //提取请求中保存的请求体,如果没有相应的请求体数据,则返回错误信息
    var req types.LoginRequest
    if err := httpx.Parse(r, &req); err != nil {
    httpx.ErrorCtx(r.Context(), w, err)
    return
    }

      	//调用请求处理函数,返回响应结果
      	l := logic.NewLoginLogic(r.Context(), svcCtx)
      	resp, err := l.Login(&req)
      	//根据响应结果返回响应数据
      	if err != nil {
      		httpx.ErrorCtx(r.Context(), w, err)
      	} else {
      		httpx.OkJsonCtx(r.Context(), w, resp)
      	}
      }
    

    }

  • handler/router.go:主要保存路由信息

  • logic/loginlogic.go和logic/userinfologic:数据处理函数,供handler中的方法调用

  • svc/servicontext.go:返回一个携带配置信息的上下文

  • types/types.go:用于存放实体类

  • user.go:程序入口

整体的一个运行流程差不多为:

从user.go开始读取users.yaml配置文件,并将内保存到config.go文件中

根据handler/routers.go中的路由加载路由配置,然后启动服务

接受到对应的请求后就会调用handler中对应的方法,handler会调用logic的具体处理业务,处理完成后返回处理结果,并响应数据

因此对数据的一些特殊处理也都可以在handler中实现

api响应封装

将所有的响应都封装为一个统一的格式

func Response(r *http.Request, w http.ResponseWriter, res any, err error) {
	body := Body{}
	if err != nil {
		body = Body{
			Code: 10086,
			Data: nil,
			Msg:  "请求错误",
		}
	} else {
		body = Body{
			Code: 10086,
			Data: res,
			Msg:  "请求成功",
		}
	}
	//func WriteJson(w http.ResponseWriter, code int, v any)
	//将数据v写入到响应体response中
	httpx.WriteJson(w, http.StatusOK, body)

}

就可以自己写好一个Response方法,然后插入到handler方法中

func loginHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		//提取请求中保存的请求体,如果没有相应的请求体数据,则返回错误信息
		var req types.LoginRequest
		if err := httpx.Parse(r, &req); err != nil {
			httpx.ErrorCtx(r.Context(), w, err)
			return
		}

		//调用请求处理函数,返回响应结果
		l := logic.NewLoginLogic(r.Context(), svcCtx)
		resp, err := l.Login(&req)
		
		
		response.Response(r, w, resp, err)
		
		
		//根据响应结果返回响应数据
		//if err != nil {
		//	httpx.ErrorCtx(r.Context(), w, err)
		//} else {
		//	httpx.OkJsonCtx(r.Context(), w, resp)
		//}
	}
}

唯一麻烦的就是需要一个一个去加

官方本身也提供了一个这样的封装

type (
	Response {
		Code int    `json:"code"`
		Msg  string `json:"msg"`
	}
)

type (
	UserInfo {
		UserId   uint   `json:"userId"`
		Username string `json:"username"`
	}
)

type (
	UserInfoResponse {
		Response
		Data UserInfo `json:"data"`
	}
)

写的结构和最终生成的实体类的结构也是一样的。

相关推荐
杜杜的man9 小时前
【go从零单排】结构嵌套struct embedding
javascript·golang·embedding
半桶水专家21 小时前
go语言中的通道(channel)详解
算法·golang
杜杜的man21 小时前
【go从零单排】实现枚举类型(Enum)
git·golang·github
明月看潮生1 天前
青少年编程与数学 02-003 Go语言网络编程 19课题、Go语言Restful编程
开发语言·青少年编程·golang·网络编程·编程与数学
张飞的猪1 天前
GoLang协程Goroutiney原理与GMP模型详解
golang
tyler_download1 天前
golang 实现比特币内核:实现基于椭圆曲线的数字签名和验证
开发语言·数据库·golang
hlsd#1 天前
go mod 依赖管理
开发语言·后端·golang
杜杜的man1 天前
【go从零单排】迭代器(Iterators)
开发语言·算法·golang
亦世凡华、1 天前
【启程Golang之旅】从零开始构建可扩展的微服务架构
开发语言·经验分享·后端·golang
Chrikk1 天前
Go-性能调优实战案例
开发语言·后端·golang