iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > GO >godoudou应用中使用注解示例详解
  • 175
分享到

godoudou应用中使用注解示例详解

godoudou应用注解godoudou 2022-12-08 20:12:23 175人浏览 薄情痞子
摘要

目录快速上手准备初始化工程设计业务接口生成代码下载依赖Auth中间件修改main函数启动服务测试效果注解实现原理总结快速上手 我们都知道Go语言没有原生的注解,但是做业务开发有些时候

快速上手

我们都知道Go语言没有原生的注解,但是做业务开发有些时候没有注解确实不方便。go-doudou通过go语言标准库ast/parser实现了对注解的支持。b站配套视频教程地址:[golang] go-doudou微服务框架入门03-如何使用注解,如果喜欢看视频,可直接跟视频上手实践。

我们通过一个简单的基于go-doudou开发的服务来演示用法和效果。

准备

  • 本地安装最新版go-doudou CLI
go install -v GitHub.com/uNIOnj-cloud/go-doudou@v1.1.9
复制代码

本地安装postman,用于测试接口:www.postman.com/

本地安装goland

初始化工程

我们的服务名称和模块名称都叫annotation

go-doudou svc init annotation
复制代码

设计业务接口

go-doudou应用的接口定义文件是项目根路径下的svc.go文件。打开文件后按照如下代码修改:

package service

import "context"

//go:generate go-doudou svc Http --handler --doc

type Annotation interface {
	// 此接口可公开访问,无需校验登录和权限
	GetGuest(ctx context.Context) (data string, err error)
	// 此接口只有登录用户有权访问
	// @role(USER,ADMIN)
	GetUser(ctx context.Context) (data string, err error)
	// 此接口只有管理员有权访问
	// @role(ADMIN)
	GetAdmin(ctx context.Context) (data string, err error)
}
复制代码

@role(USER,ADMIN)@role(ADMIN)就是本文的主角。注解定义格式为:@注解名称(参数1,参数2,参数3...)。可以根据业务实际需求,自定义各种不同的注解,@role仅是一个例子,你还可以定义其他如@permission(create,update,del),以及无参数注解@inner()

生成代码

点击截图中左上角的绿色三角形,执行go:generate指令,生成接口路由和http handler相关代码,以及遵循Openapi 3.0规范的JSON文档。

run.png

我们重点看一下transport/httpsrv/handler.go文件。

package httpsrv

import (
	"net/http"

	ddmodel "github.com/unionj-cloud/go-doudou/framework/http/model"
)

// http handler接口
type AnnotationHandler interface {
	GetGuest(w http.ResponseWriter, r *http.Request)
	GetUser(w http.ResponseWriter, r *http.Request)
	GetAdmin(w http.ResponseWriter, r *http.Request)
}

// 接口路由
func Routes(handler AnnotationHandler) []ddmodel.Route {
	return []ddmodel.Route{
		{
			"GetGuest",
			"GET",
			"/guest",
			handler.GetGuest,
		},
		{
			"GetUser",
			"GET",
			"/user",
			handler.GetUser,
		},
		{
			"GetAdmin",
			"GET",
			"/admin",
			handler.GetAdmin,
		},
	}
}

// 在内存中存储解析出来的注解信息
// ddmodel.AnnotationStore是map[string][]Annotation类型的别名,
// 键是路由名称,值是注解结构体切片。注解结构体中存放了注解名称和参数切片,
// 下文我们实现的校验权限的中间件原理就是通过http.Request对象拿到路由名称,
// 然后用路由名称从RouteAnnotationStore中找出存储的注解结构体切片,
// 最后比对从内存数据源或外部数据源拿到的用户角色和注解结构体的参数切片中的元素
// 判断该用户是否有权限继续访问接口
var RouteAnnotationStore = ddmodel.AnnotationStore{
	"GetUser": {
		{
			Name: "@role",
			Params: []string{
				"USER",
				"ADMIN",
			},
		},
	},
	"GetAdmin": {
		{
			Name: "@role",
			Params: []string{
				"ADMIN",
			},
		},
	},
}
复制代码

下载依赖

执行命令go mod tidy,下载项目依赖。此时,服务已经可以启动了,但是我们不急。下面我们要根据注解信息,编写中间件,实现我们依据用户角色控制访问权限的需求。

Auth中间件

本示例项目的登录凭证采用http basic的base64 token。我们打开transport/httpsrv/middleware.go文件,黏贴进去如下代码:

package httpsrv

import (
	"annotation/vo"
	"github.com/gorilla/mux"
	"github.com/unionj-cloud/go-doudou/toolkit/sliceutils"
	"net/http"
)

// vo.UserStore是map[Auth]RoleEnum的别名类型,键为用户名和密码构成的结构体,值为角色枚举
// 我们用userStore代表数据库
func Auth(userStore vo.UserStore) func(inner http.Handler) http.Handler {
	return func(inner http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			// 从http.Request中拿到路由名称
			currentRoute := mux.CurrentRoute(r)
			if currentRoute == nil {
				inner.ServeHTTP(w, r)
				return
			}
			routeName := currentRoute.GetName()
			// 查询该路由是否有关联的注解结构体切片
			// 如果没有,则放行
			if !RouteAnnotationStore.HasAnnotation(routeName, "@role") {
				inner.ServeHTTP(w, r)
				return
			}
			// 从请求头中提取并解析http basic用户名和密码
			user, pass, ok := r.BasicAuth()
			// 如果不成功,则禁止访问,返回401
			if !ok {
				w.Header().Set("WWW-Authenticate", `Basic realm="Provide user name and passWord"`)
				w.WriteHeader(401)
				w.Write([]byte("Unauthorised.\n"))
				return
			}
			// 从userStore中查询是否存在此用户
			role, exists := userStore[vo.Auth{user, pass}]
			// 如果不存在,则禁止访问,返回401
			if !exists {
				w.Header().Set("WWW-Authenticate", `Basic realm="Provide user name and password"`)
				w.WriteHeader(401)
				w.Write([]byte("Unauthorised.\n"))
				return
			}
			// 如果存在,则判断该接口是否允许该用户所属角色访问
			params := RouteAnnotationStore.GetParams(routeName, "@role")
			// 判断该路由的@role注解的参数切片中是否包含该用户的角色
			// 如果不包含,则禁止访问,返回403
			if !sliceutils.StrinGContains(params, role.StringGetter()) {
				w.WriteHeader(403)
				w.Write([]byte("Access denied\n"))
				return
			}
			// 如果包含,则放行
			inner.ServeHTTP(w, r)
		})
	}
}
复制代码

至此,我们已经完成核心逻辑开发。最后我们只要把这个中间件加到go-doudou服务里即可。

修改main函数

package main

import (
	service "annotation"
	"annotation/config"
	"annotation/transport/httpsrv"
	"annotation/vo"
	ddhttp "github.com/unionj-cloud/go-doudou/framework/http"
)

func main() {
	conf := config.LoadFromEnv()
	svc := service.NewAnnotation(conf)
	handler := httpsrv.NewAnnotationHandler(svc)
	srv := ddhttp.NewDefaultHttpSrv()
	// 将上文编写的Auth中间件加入go-doudou服务中
	srv.AddMiddleware(httpsrv.Auth(vo.UserStore{
		vo.Auth{
			User: "guest",
			Pass: "guest",
		}: vo.GUEST,
		vo.Auth{
			User: "user",
			Pass: "user",
		}: vo.USER,
		vo.Auth{
			User: "admin",
			Pass: "admin",
		}: vo.ADMIN,
	}))
	srv.AddRoute(httpsrv.Routes(handler)...)
	srv.Run()
}
复制代码

启动服务

启动服务有多种方式:

go-doudou内置启动命令(仅用于开发阶段):

go-doudou svc run
复制代码

go run cmd/main.go

点击main函数左边的绿色图表

main.png

测试效果

将生成的annotation_openapi3.json文件导入postman中即可测试。postman的用法超出了本文的范畴,此处只附上部分截图供参考。

test.png

注解实现原理

go-doudou实现注解的原理非常简单,就是通过go语言标准库ast/parser对接口定义文件svc.go文件中的源码进行解析,将注释块里的注解通过正则表达式提取出来,创建Annotation结构体实例,关联到对应的接口上,最后作为静态变量RouteAnnotationStore的值通过代码生成器输出到transport/httpsrv/handler.go文件里的,供开发者调用。

以下是提取注解的源码,供参考:

var reAnno = regexp.MustCompile(`@(\S+?)\((.*?)\)`)

func GetAnnotations(text string) []Annotation {
	if !reAnno.MatchString(text) {
		return nil
	}
	var annotations []Annotation
	matches := reAnno.FindAllStringSubmatch(text, -1)
	for _, item := range matches {
		name := fmt.Sprintf(`@%s`, item[1])
		var params []string
		if stringutils.IsNotEmpty(item[2]) {
			params = strings.Split(strings.TrimSpace(item[2]), ",")
		}
		annotations = append(annotations, Annotation{
			Name:   name,
			Params: params,
		})
	}
	return annotations
}
复制代码

总结

本文通过一个快速上手实例,讲解了go-doudou注解特性的用法和原理。有任何疑问都可以在下方留言。示例源码地址:github.com/unionj-clou…。

关于go-doudou的更多特性和用法请参考官方文档:go-doudou.unionj.cloud/

以上就是go doudou应用中使用注解示例详解的详细内容,更多关于go doudou应用注解的资料请关注编程网其它相关文章!

您可能感兴趣的文档:

--结束END--

本文标题: godoudou应用中使用注解示例详解

本文链接: https://www.lsjlt.com/news/174466.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作