Ai
2 Star 0 Fork 0

ccait-dev/fast-api

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
server.go 10.39 KB
一键复制 编辑 原始数据 按行查看 历史
草耑 提交于 2025-07-02 20:32 +08:00 . release
package fast
import (
"errors"
"fmt"
"gitee.com/ccait-dev/fast-api/fast/config"
"gitee.com/ccait-dev/fast-api/fast/database"
"gitee.com/ccait-dev/fast-api/fast/types"
"gitee.com/ccait-dev/fast-api/fast/utils/jsonUtil"
"gitee.com/ccait-dev/fast-api/fast/utils/loggerUtil"
"gitee.com/ccait-dev/fast-api/internal"
"github.com/gin-gonic/gin"
"github.com/go-playground/validator/v10"
"github.com/logrusorgru/aurora"
"gorm.io/gorm"
"net/http"
"os"
"reflect"
"strings"
)
var (
appName string
appConfig *config.AppConfig
fastHandleMap types.HandleMap
)
type Server struct {
Opts types.Options
}
func init() {
fastHandleMap = internal.Export()
appConfig = config.Config
appName = "fast-api"
}
func BuildServer(opt *types.Options) {
if opt == nil {
opt = new(types.Options)
}
server := Server{}
server.Opts = *opt
server.Start(opt)
if opt.DisableFastApi {
appConfig.Fast = false
}
}
func (*Server) Start(opt *types.Options) {
sigs := make(chan os.Signal, 1)
done := make(chan bool, 1)
go func() {
<-sigs
done <- true
}()
// 设置Gin为发布模式
gin.SetMode(gin.ReleaseMode)
// 创建一个Gin路由器
r := gin.Default()
fmt.Println(fmt.Sprintf("%s before start.", appName))
configServer(r)
fmt.Println(fmt.Sprintf("%s config server.", appName))
if opt.OnBeforeStart != nil {
opt.OnBeforeStart(r, appConfig)
fmt.Sprintf("%s invoke OnBeforeStart event.", appName)
}
r.GET("/fast-api/testing", func(c *gin.Context) {
c.JSON(200, gin.H{"msg": "success"})
})
fmt.Println(fmt.Sprintf("%s register: /fast-api/testing", appName))
//注册Api
registerApi(opt, r)
fmt.Println(fmt.Sprintf("%s register api by options.", appName))
// 启动服务器,不填入参默认在0.0.0.0:8080上监听
if len(strings.TrimSpace(appConfig.Port)) == 0 {
appConfig.Port = "8080"
}
fmt.Println(aurora.Cyan(fmt.Sprintf("%s begin listing port: %s.\n", appName, aurora.Magenta(appConfig.Port))))
fmt.Println(aurora.Cyan(aurora.BgMagenta(fmt.Sprintf("Welcome use %s! Server has been running......", appName))))
r.Run("0.0.0.0:" + string(appConfig.Port))
<-done
fmt.Println(aurora.BgRed(" Closed "))
fmt.Println(aurora.BgGreen(" Finished "))
}
func configServer(r *gin.Engine) {
// 创建一个日志中间件,记录请求的基本信息
r.Use(gin.LoggerWithFormatter(func(param gin.LogFormatterParams) string {
return fmt.Sprintf("[%s] \"%s %s %s\" %d %d\n",
param.TimeStamp.Format("2006/01/02 - 15:04:05"),
param.Request.Method,
param.Path,
param.Request.Proto,
param.StatusCode,
param.BodySize,
)
}))
// 跨域中间件
r.Use(func(c *gin.Context) {
if len(strings.TrimSpace(appConfig.Methods)) == 0 {
appConfig.Methods = "GET, POST, PUT, DELETE, PATCH, OPTIONS"
}
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
c.Writer.Header().Set("Access-Control-Allow-Methods", appConfig.Methods)
c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type")
if c.Request.Method == "OPTIONS" {
c.AbortWithStatus(204)
return
}
c.Next()
})
}
func registerApi(opt *types.Options, r *gin.Engine) {
var handles = types.HandleMap{}
if !appConfig.DisableMapper && opt.ModelMapper != nil {
fillHandlesByModels(appConfig, opt.ModelMapper, handles)
}
if opt.Handles != nil && len(opt.Handles) > 0 {
for key, value := range opt.Handles {
handles[key] = value
}
}
// 在路由器上添加请求处理
for _, route := range appConfig.Routes {
if len(strings.TrimSpace(route.Url)) == 0 {
continue
}
if !strings.HasPrefix(route.Url, "/") {
continue
}
if isFastApi(route.Url) {
continue
}
if len(strings.TrimSpace(route.Handler)) == 0 {
route.Handler = route.Url
}
if handles[route.Handler] == nil {
continue
}
customHandler := func(c *gin.Context) {
handler, params, exists := internal.NewHandles(handles).GetHandler(c, "LCamel")
if !exists {
return
}
rules := make(map[string]interface{})
data := make(map[string]interface{})
if len(route.Validation) > 0 {
for key, rule := range route.Validation {
fieldName := jsonUtil.Case2Camel(key)
rules[fieldName] = rule
if val, ok := params[fieldName]; ok && val != nil {
data[fieldName] = val
} else {
data[fieldName] = nil
}
}
}
err := validate(c, rules, data)
if err != nil {
return
}
factory := database.DBFactory{}
dbname := c.Param("db")
db, err := factory.GetDB(dbname)
defer CloseDatabase(db)
if err != nil {
c.JSON(404, gin.H{"message": "404"})
http.NotFoundHandler()
return
}
db.Exec("SET TRANSACTION ISOLATION LEVEL SERIALIZABLE") // 设置事务隔离级别为可串行化
db.Set("gorm:query_option", "SKIP SELECTED") // 对于某些支持缓存清除的GORM插件或中间件
handler(c, params, db, opt)
}
setRouter(customHandler, route.Url, route.Method, r, "/")
}
if isEnabledFastApi() {
// 创建一个回调函数,用于处理请求
fastHandler := func(c *gin.Context) {
handler, params, exists := internal.NewHandles(fastHandleMap).GetHandler(c, "")
if !exists {
return
}
factory := database.DBFactory{}
dbname := c.Param("db")
db, err := factory.GetDB(dbname)
defer CloseDatabase(db)
if err != nil {
c.JSON(404, gin.H{"message": "404"})
http.NotFoundHandler()
return
}
db.Exec("SET TRANSACTION ISOLATION LEVEL SERIALIZABLE") // 设置事务隔离级别为可串行化
db.Set("gorm:query_option", "SKIP SELECTED") // 对于某些支持缓存清除的GORM插件或中间件
if appConfig.FastGroup != "" {
for _, route := range appConfig.Routes {
if isFastApi(route.Url) && c.Request.URL.Path == route.Url {
rules := make(map[string]interface{})
data := make(map[string]interface{})
if len(route.Validation) > 0 {
for key, rule := range route.Validation {
fieldName := jsonUtil.Case2Camel(key)
rules[fieldName] = rule
if val, ok := params[fieldName]; ok && val != nil {
data[fieldName] = val
} else {
data[fieldName] = nil
}
}
}
err := validate(c, rules, data)
if err != nil {
return
}
}
}
}
handler(c, params, db, opt)
}
if len(strings.TrimSpace(appConfig.FastGroup)) == 0 {
appConfig.FastGroup = "/fast-api"
}
if isArray(appConfig.Fast) {
// 将对象转换为数组
arr := reflect.ValueOf(appConfig.Fast).Interface().([]string)
for _, dbname := range arr {
for _, routeInfo := range internal.ROUTES {
setRouter(fastHandler, strings.Replace(routeInfo.Path, "/:db/", "/"+dbname+"/", -1), routeInfo.Method, r, appConfig.FastGroup)
}
}
} else {
for _, routeInfo := range internal.ROUTES {
setRouter(fastHandler, routeInfo.Path, routeInfo.Method, r, appConfig.FastGroup)
}
}
}
}
func setRouter(handler gin.HandlerFunc, path string, method string, r *gin.Engine, prefix string) {
notFoundHandler := func(c *gin.Context) {
c.JSON(404, gin.H{"message": "404"})
http.NotFoundHandler()
}
if len(strings.TrimSpace(prefix)) > 0 && prefix != "/" && !strings.HasPrefix(path, prefix) {
path = prefix + path
}
switch method {
case "GET":
r.GET(path, handler)
break
case "POST":
r.POST(path, handler)
break
case "PUT":
r.PUT(path, handler)
break
case "DELETE":
r.DELETE(path, handler)
break
case "PATCH":
r.PATCH(path, handler)
break
case "OPTIONS":
r.OPTIONS(path, handler)
break
default:
r.NoRoute(notFoundHandler)
break
}
}
func isArray(value any) bool {
return reflect.TypeOf(value).Kind() == reflect.Array
}
func isEnabledFastApi() bool {
if reflect.TypeOf(appConfig.Fast).Kind() == reflect.Bool {
return true
}
if reflect.TypeOf(appConfig.Fast).Kind() == reflect.Array {
// 将对象转换为数组
arr := reflect.ValueOf(appConfig.Fast).Interface().([]string)
if len(arr) > 0 {
return true
}
}
return false
}
func isFastApi(url string) bool {
if isEnabledFastApi() {
if strings.HasPrefix(url, fmt.Sprintf("%s/", appConfig.FastGroup)) {
return true
}
}
return false
}
func fillHandlesByModels(appConf *config.AppConfig, modelMapper map[string]interface{}, handleMap types.HandleMap) {
for _, route := range appConf.Routes {
if len(strings.TrimSpace(route.Url)) == 0 {
continue
}
var uris = strings.Split(route.Url, "/")
if len(uris) < 2 {
continue
}
var table = jsonUtil.Lcfirst(jsonUtil.Case2Camel(uris[len(uris)-2]))
var action = jsonUtil.Ucfirst(jsonUtil.Case2Camel(uris[len(uris)-1]))
if _, exists := modelMapper[table]; !exists {
continue
}
handleMap[route.Url] = func(ctx *gin.Context, params map[string]interface{}, db *gorm.DB, opts *types.Options) error {
model := reflect.ValueOf(modelMapper[table])
method := model.MethodByName(action)
var count = model.Type().NumField()
rules := make(map[string]interface{})
data := make(map[string]interface{})
if len(route.Validation) > 0 {
for key, rule := range route.Validation {
fieldName := jsonUtil.Case2Camel(key)
rules[fieldName] = rule
if val, ok := params[fieldName]; ok && val != nil {
data[fieldName] = val
} else {
data[fieldName] = nil
}
}
} else {
for i := 0; i < count; i++ {
field := model.Type().Field(i)
fieldName := field.Name
tag := field.Tag.Get("validate")
if tag != "" { // 如果标签存在且不为空
rules[fieldName] = tag
if val, ok := params[fieldName]; ok && val != nil {
data[fieldName] = val
} else {
data[fieldName] = nil
}
}
}
}
err := validate(ctx, rules, data)
if err != nil {
return err
}
// 确保是指针类型的方法
internal.InvokeMethod(ctx, method, params, db, opts)
return nil
}
}
}
func CloseDatabase(db *gorm.DB) {
sqlDB, err := db.DB()
if err != nil {
loggerUtil.Error(err.Error())
}
//db.Commit()
defer sqlDB.Close()
}
func validate(ctx *gin.Context, rules map[string]interface{}, data map[string]interface{}) error {
if len(rules) > 0 && len(data) == len(rules) {
validate := validator.New()
errsMap := validate.ValidateMap(data, rules)
if len(errsMap) > 0 {
var errs string
for key, err := range errsMap {
data[key] = fmt.Sprintf("%s: %v;", key, err)
errs += data[key].(string) + "\n"
}
err := errors.New(errs)
ctx.AbortWithStatusJSON(http.StatusRequestedRangeNotSatisfiable, gin.H{
"code": -100,
"message": errs,
"data": data,
})
return err
}
}
return nil
}
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
Go
1
https://gitee.com/ccait-dev/fast-api.git
git@gitee.com:ccait-dev/fast-api.git
ccait-dev
fast-api
fast-api
v1.0.58

搜索帮助