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"`
	}
)

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

相关推荐
cui_win7 小时前
【基础】Golang语言开发环境搭建(Linux主机)
linux·golang·运维开发
叹一曲当时只道是寻常9 小时前
Softhub软件下载站实战开发(十):实现图片视频上传下载接口
golang·go·音视频
qq_1682789516 小时前
Protobuf在游戏开发中的应用:TypeScript + Golang 实践
服务器·golang·游戏引擎
大模型铲屎官10 天前
【Go语言-Day 7】循环控制全解析:从 for 基础到 for-range 遍历与高级控制
开发语言·人工智能·后端·golang·大模型·go语言·循环控制
mxpan11 天前
深入探究 Go 语言中使用 SQLite 数据库
数据库·golang·sqlite
唯独不开心11 天前
GO 语言学习 之 helloWorld
学习·golang
Go Dgg11 天前
Go 语言的堆糖图片爬虫
开发语言·爬虫·golang
{⌐■_■}11 天前
【编程语言】javascript、java、go对比应用场景
java·javascript·golang
IT艺术家-rookie11 天前
golang--数据类型与存储
开发语言·后端·golang
小诸葛的博客12 天前
go语言实现进度条
开发语言·后端·golang