mirror of
https://github.com/quarkcloudio/quark-go.git
synced 2026-04-22 23:37:10 +08:00
688 lines
15 KiB
Go
688 lines
15 KiB
Go
package builder
|
|
|
|
import (
|
|
"io"
|
|
"net/http"
|
|
"reflect"
|
|
"runtime"
|
|
"strings"
|
|
|
|
"github.com/gorilla/sessions"
|
|
"github.com/labstack/echo/v4"
|
|
"github.com/quarkcloudio/quark-go/v2/pkg/dal"
|
|
"github.com/quarkcloudio/quark-go/v2/pkg/gopkg"
|
|
"github.com/quarkcloudio/quark-go/v2/pkg/utils/file"
|
|
"github.com/redis/go-redis/v9"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
const (
|
|
|
|
// 应用名称
|
|
AppName = "QuarkGo"
|
|
|
|
// 版本号
|
|
Version = "2.4.3"
|
|
|
|
// 包名
|
|
PkgName = "github.com/quarkcloudio/quark-go/v2"
|
|
)
|
|
|
|
type Engine struct {
|
|
echo *echo.Echo // Echo框架实例
|
|
useHandlers []func(ctx *Context) error // 中间件方法
|
|
config *Config // 配置
|
|
cookieStore *sessions.CookieStore // Cookie存储,用于保存Session
|
|
providers []interface{} // 服务列表
|
|
urlPaths []*UrlPath // 请求路径列表
|
|
routePaths []*RouteMapping // 路由路径列表
|
|
}
|
|
|
|
type RouteMapping struct {
|
|
Method string
|
|
Path string
|
|
Handler func(ctx *Context) error
|
|
}
|
|
|
|
type UrlPath struct {
|
|
Method string
|
|
Url string
|
|
}
|
|
|
|
type DBConfig struct {
|
|
Dialector gorm.Dialector
|
|
Opts gorm.Option
|
|
}
|
|
|
|
type RedisConfig struct {
|
|
Host string // 地址
|
|
Password string // 密码
|
|
Port string // 端口
|
|
Database int // 数据库
|
|
}
|
|
|
|
type Config struct {
|
|
AppKey string // 应用加密Key,用于JWT认证
|
|
DBConfig *DBConfig // 数据库配置
|
|
RedisConfig *RedisConfig // Redis配置
|
|
CookieStore *sessions.CookieStore // Cookie存储,用于保存Session
|
|
StaticPath string // 静态文件目录
|
|
Providers []interface{} // 服务列表
|
|
}
|
|
|
|
// 定义路由组
|
|
type Group struct {
|
|
engine *Engine
|
|
echoGroup *echo.Group
|
|
}
|
|
|
|
// 定义路由方法类型
|
|
type Handle func(ctx *Context) error
|
|
|
|
// 全局配置
|
|
var AppConfig *Config
|
|
|
|
// 初始化对象
|
|
func New(config *Config) *Engine {
|
|
|
|
// 初始化应用配置
|
|
AppConfig = config
|
|
|
|
// 初始化echo引擎
|
|
e := echo.New()
|
|
|
|
// 隐藏banner
|
|
e.HideBanner = true
|
|
|
|
// 初始化数据库
|
|
if config.DBConfig != nil {
|
|
dal.InitDB(config.DBConfig.Dialector, config.DBConfig.Opts)
|
|
}
|
|
|
|
// 初始化Redis
|
|
if config.RedisConfig != nil {
|
|
dal.InitRedis(&redis.Options{
|
|
Addr: config.RedisConfig.Host + ":" + config.RedisConfig.Port,
|
|
Password: config.RedisConfig.Password,
|
|
DB: config.RedisConfig.Database,
|
|
})
|
|
}
|
|
|
|
cookieStore := sessions.NewCookieStore([]byte(config.AppKey))
|
|
|
|
// 初始化Cookie存储
|
|
if config.CookieStore != nil {
|
|
cookieStore = config.CookieStore
|
|
}
|
|
|
|
// 定义结构体
|
|
engine := &Engine{
|
|
echo: e,
|
|
providers: config.Providers,
|
|
config: config,
|
|
cookieStore: cookieStore,
|
|
}
|
|
|
|
// 默认WEB资源目录
|
|
if config.StaticPath == "" {
|
|
config.StaticPath = "./web"
|
|
}
|
|
|
|
// 下载静态文件
|
|
if !file.IsExist(config.StaticPath) {
|
|
err := gopkg.New(PkgName, Version).Save("web", config.StaticPath)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
}
|
|
|
|
// 初始化请求列表
|
|
engine.initPaths()
|
|
|
|
// 调用初始化方法
|
|
return engine
|
|
}
|
|
|
|
// 获取当前配置
|
|
func GetConfig() *Config {
|
|
return AppConfig
|
|
}
|
|
|
|
// 获取当前配置
|
|
func (p *Engine) GetConfig() *Config {
|
|
return p.config
|
|
}
|
|
|
|
// 获取所有服务
|
|
func (p *Engine) GetProviders() []interface{} {
|
|
return p.providers
|
|
}
|
|
|
|
// 创建上下文
|
|
func (p *Engine) NewContext(writer http.ResponseWriter, request *http.Request) *Context {
|
|
echoContext := p.echo.NewContext(request, writer)
|
|
|
|
return &Context{
|
|
Engine: p,
|
|
EchoContext: echoContext,
|
|
Request: request,
|
|
Writer: writer,
|
|
}
|
|
}
|
|
|
|
// 转换Request、Response对象
|
|
func (p *Engine) TransformContext(fullPath string, header map[string][]string, method string, url string, body io.Reader, writer io.Writer) *Context {
|
|
// 转换为http.ResponseWriter
|
|
w := NewResponse(writer)
|
|
|
|
// 转换为http.Request
|
|
r := NewRequest(method, url, body)
|
|
|
|
// 转换Header
|
|
r.Header = header
|
|
|
|
// 创建上下文
|
|
ctx := p.NewContext(w, r)
|
|
|
|
// 设置当前路由
|
|
ctx.SetFullPath(fullPath)
|
|
|
|
// 返回对象
|
|
return ctx
|
|
}
|
|
|
|
// 初始化请求列表
|
|
func (p *Engine) initPaths() {
|
|
var (
|
|
urlPaths []*UrlPath
|
|
routePaths []*RouteMapping
|
|
)
|
|
if p.urlPaths != nil && p.routePaths != nil {
|
|
return
|
|
}
|
|
for _, provider := range p.providers {
|
|
|
|
// 初始化路由
|
|
provider.(interface {
|
|
RouteInit() interface{}
|
|
}).RouteInit()
|
|
|
|
// 加载自定义路由
|
|
provider.(interface {
|
|
Route() interface{}
|
|
}).Route()
|
|
|
|
// 获取模板定义的路由
|
|
templateRoutes := provider.(interface {
|
|
GetRouteMapping() []*RouteMapping
|
|
}).GetRouteMapping()
|
|
|
|
for _, v := range templateRoutes {
|
|
providerName := reflect.TypeOf(provider).String()
|
|
getNames := strings.Split(providerName, ".")
|
|
structName := getNames[len(getNames)-1]
|
|
|
|
if strings.Contains(v.Path, ":resource") {
|
|
url := strings.Replace(v.Path, ":resource", strings.ToLower(structName), -1)
|
|
|
|
// 处理行为
|
|
if strings.Contains(url, ":uriKey") {
|
|
|
|
// 获取行为
|
|
actions := provider.(interface {
|
|
Actions(ctx *Context) []interface{}
|
|
}).Actions(&Context{})
|
|
|
|
// 解析行为
|
|
for _, av := range actions {
|
|
|
|
// 模版初始化
|
|
av.(interface {
|
|
TemplateInit(ctx *Context) interface{}
|
|
}).TemplateInit(&Context{})
|
|
|
|
// uri唯一标识
|
|
uriKey := av.(interface {
|
|
GetUriKey(interface{}) string
|
|
}).GetUriKey(av)
|
|
|
|
// 行为类型
|
|
actionType := av.(interface {
|
|
GetActionType() string
|
|
}).GetActionType()
|
|
|
|
// 解析行为
|
|
if actionType == "dropdown" {
|
|
|
|
// 获取dropdown里面行为
|
|
dropdownActions := av.(interface {
|
|
GetActions() []interface{}
|
|
}).GetActions()
|
|
|
|
// 解析行为
|
|
for _, dropdownAction := range dropdownActions {
|
|
|
|
// uri唯一标识
|
|
uriKey := dropdownAction.(interface {
|
|
GetUriKey(interface{}) string
|
|
}).GetUriKey(dropdownAction)
|
|
url = strings.Replace(url, ":uriKey", uriKey, -1)
|
|
}
|
|
} else {
|
|
url = strings.Replace(url, ":uriKey", uriKey, -1)
|
|
}
|
|
}
|
|
}
|
|
urlPaths = append(urlPaths, &UrlPath{
|
|
Method: v.Method,
|
|
Url: url,
|
|
})
|
|
}
|
|
|
|
if !hasRoutePath(routePaths, v.Method, v.Path) {
|
|
routePaths = append(routePaths, &RouteMapping{v.Method, v.Path, v.Handler})
|
|
}
|
|
}
|
|
}
|
|
|
|
p.urlPaths = urlPaths
|
|
p.routePaths = routePaths
|
|
}
|
|
|
|
// 判断是否存在RoutePath
|
|
func hasRoutePath(routePaths []*RouteMapping, method string, path string) bool {
|
|
var has bool
|
|
for _, v := range routePaths {
|
|
if v.Method == method && v.Path == path {
|
|
has = true
|
|
}
|
|
}
|
|
|
|
return has
|
|
}
|
|
|
|
// 获取请求列表
|
|
func (p *Engine) GetUrlPaths() []*UrlPath {
|
|
return p.urlPaths
|
|
}
|
|
|
|
// 获取路由列表
|
|
func (p *Engine) GetRoutePaths() []*RouteMapping {
|
|
return p.routePaths
|
|
}
|
|
|
|
// 通用调用方法
|
|
func (p *Engine) Use(args interface{}) {
|
|
argsName := reflect.TypeOf(args).String()
|
|
|
|
switch argsName {
|
|
case "func(*builder.Context) error":
|
|
p.useHandlers = append(p.useHandlers, args.(func(ctx *Context) error))
|
|
default:
|
|
panic(argsName + " arguments was not found")
|
|
}
|
|
}
|
|
|
|
// 获取通用调用方法
|
|
func (p *Engine) UseHandlers() []func(ctx *Context) error {
|
|
return p.useHandlers
|
|
}
|
|
|
|
// 解析模版方法
|
|
func (p *Engine) handleParser(ctx *Context) error {
|
|
var (
|
|
result []reflect.Value
|
|
err error
|
|
templateInstance interface{}
|
|
)
|
|
|
|
// 获取模板实例
|
|
templateInstance = ctx.Template
|
|
if templateInstance == nil {
|
|
return ctx.String(200, "unable to find resource instance")
|
|
}
|
|
|
|
// 执行挂载的方法
|
|
for _, v := range p.routePaths {
|
|
if v.Path == ctx.FullPath() {
|
|
|
|
// 反射实例值
|
|
value := reflect.ValueOf(templateInstance)
|
|
if !value.IsValid() {
|
|
continue
|
|
}
|
|
|
|
// 获取指针
|
|
pc := reflect.ValueOf(v.Handler).Pointer()
|
|
|
|
// 获取func全路径
|
|
fn := runtime.FuncForPC(pc)
|
|
fullPaths := strings.Split(fn.Name(), ".")
|
|
lastPathName := fullPaths[len(fullPaths)-1]
|
|
|
|
// 获取方法名称
|
|
funcNames := strings.Split(lastPathName, "-")
|
|
if len(funcNames) <= 0 {
|
|
continue
|
|
}
|
|
funcName := funcNames[0]
|
|
|
|
// 获取实例上方法
|
|
method := value.MethodByName(funcName)
|
|
if !method.IsValid() {
|
|
continue
|
|
}
|
|
|
|
// 反射执行结果
|
|
result = method.Call([]reflect.Value{
|
|
reflect.ValueOf(ctx),
|
|
})
|
|
if len(result) != 1 {
|
|
continue
|
|
}
|
|
|
|
// 执行结果
|
|
if v, ok := result[0].Interface().(error); ok {
|
|
err = v
|
|
}
|
|
}
|
|
}
|
|
|
|
return err
|
|
}
|
|
|
|
// 渲染
|
|
func (p *Engine) Render(ctx *Context) error {
|
|
// 初始化模板
|
|
err := ctx.InitTemplate(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// 解析UseHandler方法
|
|
err = ctx.useHandlerParser()
|
|
if err != nil {
|
|
if err.Error() == ctx.Next().Error() {
|
|
// 解析模版方法
|
|
return p.handleParser(ctx)
|
|
}
|
|
}
|
|
|
|
// 解析模版方法
|
|
return err
|
|
}
|
|
|
|
// 处理模版上的路由映射关系
|
|
func (p *Engine) routeMappingParser() {
|
|
for _, routePath := range p.routePaths {
|
|
switch routePath.Method {
|
|
case "GET":
|
|
p.GET(routePath.Path, func(ctx *Context) error {
|
|
return p.handleParser(ctx)
|
|
})
|
|
case "HEAD":
|
|
p.HEAD(routePath.Path, func(ctx *Context) error {
|
|
return p.handleParser(ctx)
|
|
})
|
|
case "OPTIONS":
|
|
p.OPTIONS(routePath.Path, func(ctx *Context) error {
|
|
return p.handleParser(ctx)
|
|
})
|
|
case "POST":
|
|
p.POST(routePath.Path, func(ctx *Context) error {
|
|
return p.handleParser(ctx)
|
|
})
|
|
case "PUT":
|
|
p.PUT(routePath.Path, func(ctx *Context) error {
|
|
return p.handleParser(ctx)
|
|
})
|
|
case "PATCH":
|
|
p.PATCH(routePath.Path, func(ctx *Context) error {
|
|
return p.handleParser(ctx)
|
|
})
|
|
case "DELETE":
|
|
p.DELETE(routePath.Path, func(ctx *Context) error {
|
|
return p.handleParser(ctx)
|
|
})
|
|
case "Any":
|
|
p.Any(routePath.Path, func(ctx *Context) error {
|
|
return p.handleParser(ctx)
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
// 获取Echo框架实例
|
|
func (p *Engine) Echo() *echo.Echo {
|
|
return p.echo
|
|
}
|
|
|
|
// 适配Echo框架方法
|
|
func (p *Engine) echoHandle(path string, handle Handle, c echo.Context) error {
|
|
// 创建上下文
|
|
ctx := p.NewContext(c.Response().Writer, c.Request())
|
|
|
|
// 设置路由路径
|
|
ctx.SetFullPath(path)
|
|
|
|
// 初始化模板
|
|
ctx.InitTemplate(ctx)
|
|
|
|
// 解析UseHandler方法
|
|
err := ctx.useHandlerParser()
|
|
if err != nil {
|
|
if err.Error() == ctx.Next().Error() {
|
|
// 执行方法
|
|
return handle(ctx)
|
|
}
|
|
}
|
|
|
|
return err
|
|
}
|
|
|
|
// 加载静态文件
|
|
func (p *Engine) Static(pathPrefix string, fsRoot string) {
|
|
p.echo.Static(pathPrefix, fsRoot)
|
|
}
|
|
|
|
// GET请求
|
|
func (p *Engine) GET(path string, handle Handle) error {
|
|
p.echo.GET(path, func(c echo.Context) error {
|
|
return p.echoHandle(path, handle, c)
|
|
})
|
|
|
|
return nil
|
|
}
|
|
|
|
// HEAD请求
|
|
func (p *Engine) HEAD(path string, handle Handle) error {
|
|
p.echo.HEAD(path, func(c echo.Context) error {
|
|
return p.echoHandle(path, handle, c)
|
|
})
|
|
|
|
return nil
|
|
}
|
|
|
|
// OPTIONS请求
|
|
func (p *Engine) OPTIONS(path string, handle Handle) error {
|
|
p.echo.OPTIONS(path, func(c echo.Context) error {
|
|
return p.echoHandle(path, handle, c)
|
|
})
|
|
|
|
return nil
|
|
}
|
|
|
|
// POST请求
|
|
func (p *Engine) POST(path string, handle Handle) error {
|
|
p.echo.POST(path, func(c echo.Context) error {
|
|
return p.echoHandle(path, handle, c)
|
|
})
|
|
|
|
return nil
|
|
}
|
|
|
|
// PUT请求
|
|
func (p *Engine) PUT(path string, handle Handle) error {
|
|
p.echo.PUT(path, func(c echo.Context) error {
|
|
return p.echoHandle(path, handle, c)
|
|
})
|
|
|
|
return nil
|
|
}
|
|
|
|
// PATCH请求
|
|
func (p *Engine) PATCH(path string, handle Handle) error {
|
|
p.echo.PATCH(path, func(c echo.Context) error {
|
|
return p.echoHandle(path, handle, c)
|
|
})
|
|
|
|
return nil
|
|
}
|
|
|
|
// DELETE请求
|
|
func (p *Engine) DELETE(path string, handle Handle) error {
|
|
p.echo.DELETE(path, func(c echo.Context) error {
|
|
return p.echoHandle(path, handle, c)
|
|
})
|
|
|
|
return nil
|
|
}
|
|
|
|
// Any请求
|
|
func (p *Engine) Any(path string, handle Handle) error {
|
|
p.echo.Any(path, func(c echo.Context) error {
|
|
return p.echoHandle(path, handle, c)
|
|
})
|
|
|
|
return nil
|
|
}
|
|
|
|
// 路由组
|
|
func (p *Engine) Group(path string, handlers ...Handle) *Group {
|
|
echoGroup := p.echo.Group(path, func(next echo.HandlerFunc) echo.HandlerFunc {
|
|
if len(handlers) > 0 {
|
|
for _, handle := range handlers {
|
|
newHandle := func(c echo.Context) error {
|
|
err := p.echoHandle(path, handle, c)
|
|
if err != nil {
|
|
// 执行下一步操作,这里应该放到数组里面执行,暂时用NextUseHandler
|
|
if err.Error() == "NextUseHandler" {
|
|
return next(c)
|
|
}
|
|
}
|
|
return err
|
|
}
|
|
return newHandle
|
|
}
|
|
}
|
|
return next
|
|
})
|
|
|
|
return &Group{engine: p, echoGroup: echoGroup}
|
|
}
|
|
|
|
// GET请求
|
|
func (p *Group) GET(path string, handle Handle) error {
|
|
p.echoGroup.GET(path, func(c echo.Context) error {
|
|
return p.engine.echoHandle(path, handle, c)
|
|
})
|
|
|
|
return nil
|
|
}
|
|
|
|
// HEAD请求
|
|
func (p *Group) HEAD(path string, handle Handle) error {
|
|
p.echoGroup.HEAD(path, func(c echo.Context) error {
|
|
return p.engine.echoHandle(path, handle, c)
|
|
})
|
|
|
|
return nil
|
|
}
|
|
|
|
// OPTIONS请求
|
|
func (p *Group) OPTIONS(path string, handle Handle) error {
|
|
p.echoGroup.OPTIONS(path, func(c echo.Context) error {
|
|
return p.engine.echoHandle(path, handle, c)
|
|
})
|
|
|
|
return nil
|
|
}
|
|
|
|
// POST请求
|
|
func (p *Group) POST(path string, handle Handle) error {
|
|
p.echoGroup.POST(path, func(c echo.Context) error {
|
|
return p.engine.echoHandle(path, handle, c)
|
|
})
|
|
|
|
return nil
|
|
}
|
|
|
|
// PUT请求
|
|
func (p *Group) PUT(path string, handle Handle) error {
|
|
p.echoGroup.PUT(path, func(c echo.Context) error {
|
|
return p.engine.echoHandle(path, handle, c)
|
|
})
|
|
|
|
return nil
|
|
}
|
|
|
|
// PATCH请求
|
|
func (p *Group) PATCH(path string, handle Handle) error {
|
|
p.echoGroup.PATCH(path, func(c echo.Context) error {
|
|
return p.engine.echoHandle(path, handle, c)
|
|
})
|
|
|
|
return nil
|
|
}
|
|
|
|
// DELETE请求
|
|
func (p *Group) DELETE(path string, handle Handle) error {
|
|
p.echoGroup.DELETE(path, func(c echo.Context) error {
|
|
return p.engine.echoHandle(path, handle, c)
|
|
})
|
|
|
|
return nil
|
|
}
|
|
|
|
// Any请求
|
|
func (p *Group) Any(path string, handle Handle) error {
|
|
p.echoGroup.Any(path, func(c echo.Context) error {
|
|
return p.engine.echoHandle(path, handle, c)
|
|
})
|
|
|
|
return nil
|
|
}
|
|
|
|
// 路由组
|
|
func (p *Group) Group(path string, handlers ...Handle) *Group {
|
|
echoGroup := p.engine.echo.Group(path, func(next echo.HandlerFunc) echo.HandlerFunc {
|
|
if len(handlers) > 0 {
|
|
for _, handle := range handlers {
|
|
newHandle := func(c echo.Context) error {
|
|
err := p.engine.echoHandle(path, handle, c)
|
|
if err != nil {
|
|
// 执行下一步操作,这里应该放到数组里面执行,暂时用NextUseHandler
|
|
if err.Error() == "NextUseHandler" {
|
|
return next(c)
|
|
}
|
|
}
|
|
return err
|
|
}
|
|
return newHandle
|
|
}
|
|
}
|
|
return next
|
|
})
|
|
|
|
return &Group{engine: p.engine, echoGroup: echoGroup}
|
|
}
|
|
|
|
// Run Server
|
|
func (p *Engine) Run(addr string) {
|
|
// 处理模版上的路由映射关系
|
|
p.routeMappingParser()
|
|
|
|
// 启动服务
|
|
p.echo.Logger.Fatal(p.echo.Start(addr))
|
|
}
|