GithubHelp home page GithubHelp logo

yoyofx / yoyogo Goto Github PK

View Code? Open in Web Editor NEW
580.0 13.0 44.0 8.51 MB

🦄🌈 YoyoGo is a simple, light and fast , dependency injection based micro-service framework written in Go. Support Nacos ,Consoul ,Etcd ,Eureka ,kubernetes.

Home Page: http://dev.yoyogo.run

License: MIT License

Go 99.93% Makefile 0.01% Shell 0.05% Kotlin 0.01%
go web middleware microservices containers kubernetes grpc ioc dependency-injection mvc

yoyogo's Introduction

中文 / English

YoyoGo 简单、轻量、快速、基于依赖注入的微服务框架

Release Go GoVersion Report Documentation Contributors License

YoyoGo 特色

  • 漂亮又快速的路由器 & MVC 模式 .
  • 丰富的中间件支持 (handler func & custom middleware) .
  • 微服务框架抽象了分层,在一个框架体系兼容各种server实现,如 rest,grpc等 .
  • 充分运用依赖注入DI,管理运行时生命周期,为框架提供了强大的扩展性 .
  • 功能强大的微服务集成能力 (Nacos, Eureka, Consul, ETCD) .
  • 受到许多出色的 Go Web 框架的启发,并实现了多种 server : fasthttpnet.httpgrpc .

framework desgin

QQ交流群: 780385870 (Go浪文学院) , 在这里感谢贾国锦帮忙设计的logo很漂亮。

也可以加入我的公众号,通过公众号入群菜单进入微信群,主要还是在微信上活动啦。

框架安装

go get github.com/yoyofx/yoyogo

安装依赖 (由于某些原因国内下载不了依赖)

go version < 1.20

window 下在 cmd 中执行:
set GO111MODULE=on
set GOPROXY=https://goproxy.cn,direct
linux  下执行:
export GO111MODULE=on
export GOPROXY=https://goproxy.cn,direct

go version >= 1.13

go env -w GOPROXY=https://goproxy.cn,direct

vendor

go mod vendor       // 将依赖包拷贝到项目目录中去

简单的例子

package main
import ...

func main() {
	WebApplication.CreateDefaultBuilder(func(rb router.IRouterBuilder) {
        rb.GET("/info",func (ctx *context.HttpContext) {    // 支持Group方式
            ctx.JSON(200, context.H{"info": "ok"})
        })
    }).Build().Run()       //默认端口号 :8080
}

依赖框架模块

只涉及框架使用或贡献过的库

github.com/yoyofxteam/dependencyinjection

github.com/yoyofxteam/nacos-viper-remote  

github.com/yoyofxteam/reflectx

github.com/shima-park/agollo

实现进度

标准功能

  • 打印Logo和日志(YoyoGo)
  • 统一程序输入参数和环境变量 (YoyoGo)
  • 简单路由器绑定句柄功能
  • HttpContext 上下文封装(请求,响应)
  • 静态文件端点(静态文件服务器)
  • JSON 序列化结构(Context.H)
  • 获取请求文件并保存
  • 获取请求数据(form-data,x-www-form-urlencoded,Json ,XML,Protobuf 等)
  • Http 请求的绑定模型(Url, From,JSON,XML,Protobuf)

响应渲染功能

  • Render Interface
  • JSON Render
  • JSONP Render
  • Indented Json Render
  • Secure Json Render
  • Ascii Json Render
  • Pure Json Render
  • Binary Data Render
  • TEXT
  • Protobuf
  • MessagePack
  • XML
  • YAML
  • File
  • Image
  • Template
  • Auto formater Render

中间件

  • Logger
  • StaticFile
  • Router Middleware
  • CORS
  • Binding
  • JWT
  • RequestId And Tracker for SkyWorking

路由

  • GET,POST,HEAD,PUT,DELETE 方法支持
  • 路由解析树与表达式支持
  • RouteData路由数据 (/api/:version/) 与 Binding的集成
  • 路由组功能
  • MVC默认模板功能
  • MVC 自定义路由
  • 路由过滤器 Filter

MVC

  • 路由请求触发Controller&Action
  • Action方法参数绑定
  • 内部对象的DI化
  • 关键对象的参数传递

Dependency injection

  • 抽象集成第三方DI框架
  • MVC模式集成
  • 框架级的DI支持功能

扩展

  • 配置
  • WebSocket
  • JWT
  • swagger
  • GRpc
  • Prometheus

进阶范例

package main

import
...

func main() {
	webHost := CreateCustomWebHostBuilder().Build()
	webHost.Run()
}

// 自定义HostBuilder并支持 MVC 和 自动参数绑定功能,简单情况也可以直接使用CreateDefaultBuilder 。
func CreateCustomBuilder() *abstractions.HostBuilder {

	configuration := abstractions.NewConfigurationBuilder().
		AddEnvironment().
		AddYamlFile("config").Build()

	return WebApplication.NewWebHostBuilder().
		UseConfiguration(configuration).
		Configure(func(app *WebApplication.WebApplicationBuilder) {
			app.UseMiddleware(middlewares.NewCORS())
			//WebApplication.UseMiddleware(middlewares.NewRequestTracker())
			app.UseStaticAssets()
			app.UseEndpoints(registerEndpointRouterConfig)
			app.UseMvc(func(builder *mvc.ControllerBuilder) {
				//builder.AddViews(&view.Option{Path: "./static/templates"})
				builder.AddViewsByConfig()
				builder.AddController(contollers.NewUserController)
				builder.AddFilter("/v1/user/info", &contollers.TestActionFilter{})
			})
		}).
		ConfigureServices(func(serviceCollection *dependencyinjection.ServiceCollection) {
			serviceCollection.AddTransientByImplements(models.NewUserAction, new(models.IUserAction))
			//eureka.UseServiceDiscovery(serviceCollection)
			//consul.UseServiceDiscovery(serviceCollection)
			nacos.UseServiceDiscovery(serviceCollection)
		}).
		OnApplicationLifeEvent(getApplicationLifeEvent)
}

//region endpoint 路由绑定函数
func registerEndpoints(rb router.IRouterBuilder) {
	Endpoints.UseHealth(rb)
	Endpoints.UseViz(rb)
	Endpoints.UsePrometheus(rb)
	Endpoints.UsePprof(rb)
	Endpoints.UseJwt(rb)
	
	//swagger api document
	endpoints.UseSwaggerDoc(rb,
		swagger.Info{
			Title:          "YoyoGO 框架文档演示",
			Version:        "v1.0.0",
			Description:    "框架文档演示swagger文档 v1.0 [ #yoyogo](https://github.com/yoyofx/yoyogo).",
			TermsOfService: "https://dev.yoyogo.run",
			Contact: swagger.Contact{
				Email: "[email protected]",
				Name:  "yoyogo",
			},
			License: swagger.License{
				Name: "MIT",
				Url:  "https://opensource.org/licenses/MIT",
			},
		},
		func(openapi *swagger.OpenApi) {
			openapi.AddSecurityBearerAuth()
		})

	rb.GET("/error", func(ctx *context.HttpContext) {
		panic("http get error")
	})

	//POST 请求: /info/:id ?q1=abc&username=123
	rb.POST("/info/:id", func(ctx *context.HttpContext) {
		qs_q1 := ctx.Query("q1")
		pd_name := ctx.Param("username")

		userInfo := &UserInfo{}

		_ = ctx.Bind(userInfo) // 手动绑定请求对象

		strResult := fmt.Sprintf("Name:%s , Q1:%s , bind: %s", pd_name, qs_q1, userInfo)

		ctx.JSON(200, context.H{"info": "hello world", "result": strResult})
	})

	// 路由组功能实现绑定 GET 请求:  /v1/api/info
	rb.Group("/v1/api", func(router *router.RouterGroup) {
		router.GET("/info", func(ctx *context.HttpContext) {
			ctx.JSON(200, context.H{"info": "ok"})
		})
	})

	// GET 请求: HttpContext.RequiredServices获取IOC对象
	rb.GET("/ioc", func(ctx *context.HttpContext) {
		var userAction models.IUserAction
		_ = ctx.RequiredServices.GetService(&userAction)
		ctx.JSON(200, context.H{"info": "ok " + userAction.Login("zhang")})
	})
}

//endregion

//region 请求对象
type UserInfo struct {
	UserName string `param:"username"`
	Number   string `param:"q1"`
	Id       string `param:"id"`
}

// ----------------------------------------- MVC 定义 ------------------------------------------------------

// 定义Controller
type UserController struct {
	*mvc.ApiController
	userAction models.IUserAction // IOC 对象参数
}

// 构造器依赖注入
func NewUserController(userAction models.IUserAction) *UserController {
	return &UserController{userAction: userAction}
}

// 请求对象的参数化绑定 , 使用 doc属性标注 支持swagger文档
type RegisterRequest struct {
	mvc.RequestBody `route:"/api/users/register" doc:"用户注册"`
	UserName   string `uri:"userName" doc:"用户名"`
	Password   string `uri:"password" doc:"密码"`
	TestNumber uint64 `uri:"num" doc:"数字"`
}

// Register函数自动绑定参数
func (this *UserController) Register(ctx *context.HttpContext, request *RegiserRequest) actionresult.IActionResult {
	result := mvc.ApiResult{Success: true, Message: "ok", Data: request}
	return actionresult.Json{Data: result}
}

// use userAction interface by ioc  
func (this *UserController) GetInfo() mvc.ApiResult {
	return this.OK(this.userAction.Login("zhang"))
}

// DocumentResponse custom document response , use doc tag for swagger
type DocumentResponse struct {
	Message string        `json:"message" doc:"消息"`
	List    []DocumentDto `json:"list" doc:"文档列表"`
	Success bool          `json:"success" doc:"是否成功"`
}

// Swagger API 文档支持
func (controller UserController) GetDocumentList(request *struct {
	mvc.RequestGET `route:"/v1/user/doc/list" doc:"获取全部文档列表"`
}) DocumentResponse {

	return DocumentResponse{Message: "GetDocumentList", List: []DocumentDto{
		{Id: 1, Name: "test1", Time: time.Now()}, {Id: 2, Name: "test2", Time: time.Now()},
		{Id: 3, Name: "test3", Time: time.Now()}, {Id: 4, Name: "test4", Time: time.Now()},
		{Id: 5, Name: "test5", Time: time.Now()}, {Id: 6, Name: "test6", Time: time.Now()},
	}, Success: true}
}


// Web程序的开始与停止事件
func fireApplicationLifeEvent(life *abstractions.ApplicationLife) {
	printDataEvent := func(event abstractions.ApplicationEvent) {
		fmt.Printf("[yoyogo] Topic: %s; Event: %v\n", event.Topic, event.Data)
	}
	for {
		select {
		case ev := <-life.ApplicationStarted:
			go printDataEvent(ev)
		case ev := <-life.ApplicationStopped:
			go printDataEvent(ev)
			break
		}
	}
}

yoyogo's People

Contributors

anjoy8 avatar artanis-c avatar wsx864321 avatar yoyofx avatar yoyofx7 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

yoyogo's Issues

mvc绑定结构体无报错提示

使用web.mvc的 action 方法,自动绑定结构体时,如果绑定如json 类型错误会导致结构体为空,但并没有任何提示

需要全局打Log。

文档中的简单实例无法启动

使用文档中的简单实例代码放到本地执行后,无法正常启动监听端口,没有出现文档中的效果图

error:panic: runtime error: invalid memory address or nil pointer dereference
[signal 0xc0000005 code=0x0 addr=0x20 pc=0x6e2685]

DefaultViewEngine Not set config default value

Does not have view config value , that call DefaultViewEngine.SetTemplatePath throw error:

panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x17333a4]

goroutine 1 [running]:
github.com/yoyofx/yoyogo/WebFramework/View.(*DefaultEngine).SetTemplatePath(0xc000206880, 0x0)
/Users/yoyofx/go/pkg/mod/github.com/yoyofx/[email protected]/WebFramework/View/DefaultViewEngine.go:19 +0x24

nacos-viper-remote 连接阿里云MSE Nacos 取不回任何内容,也不报错

nacos-viper-remote 连接阿里云MSE Nacos 取不回任何内容,也不报错

	remote.SetOptions(&remote.Option{
		Url:         Config.Mse.Server,
		Port:        uint64(Config.Mse.Port),
		NamespaceId: Config.Mse.Namespace,
		GroupName:   Config.Mse.GroupName,
		Config:      remote.Config{DataId: Config.Mse.ConfigName},
		Auth:        nil,
	})
	remoteConfig := viper.New()
             // 这里的 endpoint 填 localhost 也一样取不回任何内容
	err := remoteConfig.AddRemoteProvider("nacos", "mse-8f7b0b60-nacos-ans.mse.aliyuncs.com", "")
	if err != nil {
		fmt.Println("加载远程配置失败 :", err.Error())
	}
	remoteConfig.SetConfigType("yaml")
	err = remoteConfig.ReadRemoteConfig()
	if err != nil {
		fmt.Println("加载远程配置失败 :", err.Error())
	}
	fmt.Println("加载远程配置: Config = ", remoteConfig.GetString("log.level"))

	loadFromViper(remoteConfig)

	fmt.Println("加载远程配置: Config = ", ObjToJSON(Config))

httpcontext.bind source by url.values,that's bad a way!

  • 修改绑定源类型由url.values到map[string]interface{}
  • 实现各种基于httpcontext(from,url, routedata ,json, xml....)的ValueProvider
  • bind函数根据请求判断如何组合ValueProvider (合并还是过滤算法待定)进行字段绑定

MVC自动绑定运行时错误

当不给结构果声明 mvc.RequestBody 的 field by index 0 ,会出现绑定不上数据,报错,但看不来是什么样的错误的问题;解决方案是 提示错误,并打印正确日志。

Server Extension

使用IOC机制注册IServer ,并通过Server Factory 而非字符判断来进行Server实例管理.

日志组件可替换其他实现

目前日志用的是第三方日志实现 logrus "github.com/sirupsen/logrus
希望计划能够抽象日志接口,用户可以通过依赖注入注册自己需要的日志组件。

Action名称中提取Method

在注入Controller时提取Action描述中包含Http Method信息,用于Action执行元数据并将描述与执行进行解耦,减少每次请求的判断。

配置文件中写端口号没有生效

application:
name: Alert
metadata: "prod Env"
server:
type: "fasthttp"
address: ":8089"


[yoyogo] [2020/09/30 14:25:06.69] yoyogo framework version : v1.5.3.release
[yoyogo] [2020/09/30 14:25:06.69] machine host ip : 172.20.10.97
[yoyogo] [2020/09/30 14:25:06.69] listening on port : 8080


version 版本号也不对

Set http response header bugs.

问题:
WriteHeader后再 Set Header 是无效的。
需要在Set Hedader后再WriteHeader(statuscode) 这样才能生效。

config file at workdir

需要支持在启动参数用指定配置文件目录 (抽象 fie:workdir , remote:adress)

错误日志无法输出堆栈

使用框架时程序出现500错误,但是控制台中只输出了错误的信息,并没有输出错误的堆栈,导致查找问题代码十分困难,建议输出全量堆栈信息。
2021/11/09 16:31:26.43 - [YOYOGO] - [INFO] [yoyogo] 2021/11/09 - 16:31:26.38 | 200 | 53 ms| localhost:8080 | GET /v1/tenant/tenantlist?pageSize=20&tCode=44444&pageIdnex=1&pageIndex=1
2021/11/09 16:51:45.85 - [YOYOGO] - [INFO] [yoyogo] Method: GET Url: /v1/cluster/list?current=1&pageSize=20 Content-Type: , Accept: /
2021/11/09 16:51:45.85 - [YOYOGO] - [ERROR] { "Result": -1 , "Data": "error", "Message": "interface conversion: interface {} is nil, not map[string]interface {}" }%!(EXTRA string=)
2021/11/09 16:51:45.85 - [YOYOGO] - [INFO] [yoyogo] 2021/11/09 - 16:51:45.85 | 500 | 8 ms| localhost:8080 | GET /v1/cluster/list?current=1&pageSize=20

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.