go doudou应用中使用注解示例详解

目录
  • 快速上手
    • 准备
    • 初始化工程
    • 设计业务接口
    • 生成代码
    • 下载依赖
    • Auth中间件
    • 修改main函数
    • 启动服务
    • 测试效果
  • 注解实现原理
  • 总结

快速上手

我们都知道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文档。

我们重点看一下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函数左边的绿色 图表

测试效果

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

注解实现原理

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应用注解的资料请关注我们其它相关文章!

(0)

相关推荐

  • go doudou开发单体RESTful服务快速上手教程

    目录 引言 需求清单 学习目标 开发环境准备 安装go-doudou 初始化工程 定义接口 生成代码 启动服务 数据库和表结构准备 生成domain和dao层代码 用户注册接口 修改domain PublicSignUp方法实现 Postman测试 用户登录接口 PublicLogIn方法实现 Postman测试 上传头像接口 修改domain 修改.env配置 JWT校验中间件 UploadAvatar方法实现 下载头像接口 GetPublicDownloadAvatar方法实现 用户详情接口

  • Go gRPC进阶教程gRPC转换HTTP

    目录 前言 gRPC转成HTTP 编写和编译proto 服务端代码修改 使用postman测试 生成swagger文档 把swagger-ui转成Go代码,备用 对外提供swagger-ui 在swagger中配置bearer token 验证测试 总结 前言 我们通常把RPC用作内部通信,而使用Restful Api进行外部通信.为了避免写两套应用,我们使用grpc-gateway把gRPC转成HTTP.服务接收到HTTP请求后,grpc-gateway把它转成gRPC进行处理,然后以JSON

  • Go gRPC服务proto数据验证进阶教程

    前言 上篇介绍了go-grpc-middleware的grpc_zap.grpc_auth和grpc_recovery使用,本篇将介绍grpc_validator,它可以对gRPC数据的输入和输出进行验证. 创建proto文件,添加验证规则 这里使用第三方插件go-proto-validators自动生成验证规则. go get github.com/mwitkow/go-proto-validators 1.新建simple.proto文件 syntax = "proto3"; pa

  • Golang gRPC HTTP协议转换示例

    gRPC HTTP协议转换 正当有这个需求的时候,就看到了这个实现姿势.源自coreos的一篇博客,转载到了grpc官方博客gRPC with REST and Open APIs. etcd3改用grpc后为了兼容原来的api,同时要提供http/json方式的API,为了满足这个需求,要么开发两套API,要么实现一种转换机制,他们选择了后者,而我们选择跟随他们的脚步. 他们实现了一个协议转换的网关,对应github上的项目grpc-gateway,这个网关负责接收客户端请求,然后决定直接转发

  • go doudou应用中使用枚举类型教程示例

    目录 go语言支持语法自己实现枚举类型 结构体类型示例 接口请求参数示例 go语言支持语法自己实现枚举类型 我们都知道go语言没有原生的枚举类型,但是做业务开发有些时候没有枚举类型确实不方便前后端联调.我们可以通过go语言支持的语法自己实现枚举类型.请看以下示例代码和注释说明: // 首先定义一个int类型别名,新类型名称就是枚举类型名称 type KeyboardLayout int // 然后定义若干常量,作为枚举值 // 第一个常量是默认值 const ( UNKNOWN Keyboard

  • go doudou开发gRPC服务快速上手实现详解

    目录 引言 准备 安装go 安装gRPC编译器和插件 安装编译器protoc 安装插件 安装go-doudou 初始化项目 定义服务 生成代码 实现接口 测试服务 总结 引言 go-doudou从v2版本开始已经支持开发gRPC服务.开发流程跟v1版本是一致的,都是先在svc.go文件里的interface里定义方法,然后执行go-doudou代码生成命令生成代码,最后你再写自定义业务逻辑实现接口.go-doudou的学习曲线非常平滑,对新手极其友好,特别是具有其他编程语言开发背景的开发者,比如

  • Go gRPC服务进阶middleware使用教程

    目录 前言 go-grpc-middleware简介 grpc_zap日志记录 grpc_auth认证 grpc_recovery恢复 总结 前言 之前介绍了gRPC中TLS认证和自定义方法认证,最后还简单介绍了gRPC拦截器的使用.gRPC自身只能设置一个拦截器,所有逻辑都写一起会比较乱.本篇简单介绍go-grpc-middleware的使用,包括grpc_zap.grpc_auth和grpc_recovery. go-grpc-middleware简介 go-grpc-middleware封

  • Go Grpc Gateway兼容HTTP协议文档自动生成网关

    目录 前言 一,grpc-gateway介绍 二,grpc-gateway环境准备 二,编写grpc-gateway服务 四,使用gateway生成swagger文档 五,性能对比 http -> go -> grpc -> go http -> go -> http -> grpc_gateway -> grpc -> go 六,总结 前言 调用,让客户端可以更具自身情况自由选择,服务端工作只需要做一份呢?还别说真还有一个准备好的轮子那就是今天的主角<

  • go doudou应用中使用注解示例详解

    目录 快速上手 准备 初始化工程 设计业务接口 生成代码 下载依赖 Auth中间件 修改main函数 启动服务 测试效果 注解实现原理 总结 快速上手 我们都知道go语言没有原生的注解,但是做业务开发有些时候没有注解确实不方便.go-doudou通过go语言标准库ast/parser实现了对注解的支持.b站配套视频教程地址:[golang] go-doudou微服务框架入门03-如何使用注解,如果喜欢看视频,可直接跟视频上手实践. 我们通过一个简单的基于go-doudou开发的服务来演示用法和效

  • SpringAOP中的注解配置详解

    这篇文章主要介绍了SpringAOP中的注解配置详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 使用注解实现SpringAOP的功能: 例子: //表示这是被注入Spring容器中的 @Component //表示这是个切面类 @Aspect public class AnnotationHandler { /* * 在一个方法上面加上注解来定义切入点 * 这个切入点的名字就是这个方法的名字 * 这个方法本身不需要有什么作用 * 这个方法的

  • java中@SuppressWarnings注解用法详解

    SuppressWarnings注解是jse提供的注解.作用是屏蔽一些无关紧要的警告.使开发者能看到一些他们真正关心的警告.从而提高开发者的效率 简介: java.lang.SuppressWarnings是J2SE 5.0中标准的Annotation之一.可以标注在类.字段.方法.参数.构造方法,以及局部变量上.作用:告诉编译器忽略指定的警告,不用在编译完成后出现警告信息. 使用: @SuppressWarnings("") @SuppressWarnings({}) @Suppre

  • java基础之注解示例详解

    目录 定义 作用 注解与注释的区别 JDK内置的标准注解 自定义注解 @Target 属性 定义 注解也叫原数据,它是JDK1.5及之后版本引入的一个特性,它可以声明在类.方法.变量等前面,用来对这些元素进行说明. 作用 生成文档:通过代码里标识的注解生成doc文档[生成doc文档] 代码分析:通过代码里标识的注解对代码进行分析[反射] 编译检查:通过代码里标识的注解让编译器能够实现基本的编译检查[Override] 注解与注释的区别 注解是给编译器看的,注释是给程序员看的. JDK内置的标准注

  • SpringBoot 实现自定义的 @ConditionalOnXXX 注解示例详解

    目录 实现一个自定义的 @Conditional 派生注解 Conditional 派生注解的类如何注入到 spring 容器 实现一个自定义的 @Conditional 派生注解 自定义一个注解,继承 @Conditional 注解 // 派生注解 @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.TYPE, ElementType.METHOD }) @Documented @Conditional(CustomConditi

  • ThinkPHP Where 条件中常用表达式示例(详解)

    Where 条件表达式格式为: $map['字段名'] = array('表达式', '操作条件'); 其中 $map 是一个普通的数组变量,可以根据自己需求而命名.上述格式中的表达式实际是运算符的意义: ThinkPHP运算符 与 SQL运算符 对照表 TP运算符 SQL运算符 例子 实际查询条件 eq = $map['id'] = array('eq',100); 等效于:$map['id'] = 100; neq != $map['id'] = array('neq',100); id !

  • Vue中的vue-resource示例详解

    vue-resource特点 vue-resource插件具有以下特点: 1. 体积小 vue-resource非常小巧,在压缩以后只有大约12KB,服务端启用gzip压缩后只有4.5KB大小,这远比jQuery的体积要小得多. 2. 支持主流的浏览器 和Vue.js一样,vue-resource除了不支持IE 9以下的浏览器,其他主流的浏览器都支持. 3. 支持Promise API和URI Templates Promise是ES6的特性,Promise的中文含义为"先知",Pro

  • django中使用memcached示例详解

    目录 什么是memcached: 安装和启动memcached: windows linux(ubuntu) 启动memcached: telnet操作memcached: 添加数据: 获取数据: 删除数据: 通过python操作memcached: memcached的安全性: 在Django中使用memcached: 什么是memcached: memcached之前是danga的一个项目,最早是为LiveJournal服务的,当初设计师为了加速LiveJournal访问速度而开发的,后来被

  • Go Java算法之从英文中重建数字示例详解

    目录 从英文中重建数字 Java实现 Go实现 从英文中重建数字 给你一个字符串 s ,其中包含字母顺序打乱的用英文单词表示的若干数字(0-9).按 升序 返回原始的数字. 示例 1: 输入:s = "owoztneoer" 输出:"012" 示例 2: 输入:s = "fviefuro" 输出:"45" 提示: 1 <= s.length <= 105 s[i] 为 ["e","g&

  • threejs中使用drawbufferss示例详解

    目录 原因 历程 原生的使用 基本流程 灵光乍现 使用WebGLMultipleRenderTargets 原因 深度剥离实现之后,似乎会使得走样严重起来. 我意识到,这是因为 剥离这个过程,并没有什么讲究,只要是深度小于等于就剔除了,这样很可能就导致了,原本平滑的差值过渡出现了断层,突变. 简单的解决办法就是增顶点数. 一番搜寻weight oit算法的demo,但是只找到了用原生webgl写的,传送门.在费了几个日夜之后,终于看懂了,但是,还要把它用three实现.一般来说,没有使用编译的框

随机推荐