feat: http di

This commit is contained in:
ivamp 2024-06-16 00:45:32 +08:00
parent 61c6bd1d10
commit 8ea6ead6cc
7 changed files with 153 additions and 61 deletions

View File

@ -1,10 +0,0 @@
package consts
import (
"github.com/gin-gonic/gin"
)
type Request struct {
Http *gin.Context
User *UserTokenInfo
}

View File

@ -23,4 +23,8 @@ type UserTokenInfo struct {
Groups []string `json:"groups"`
}
type User struct {
Token UserTokenInfo
}
const UserTokenInfoKey = "user.jwt"

View File

@ -1,14 +1,22 @@
package user
import (
"fmt"
"framework_v2/internal/consts"
"github.com/gin-gonic/gin"
"net/http"
)
func CurrentUser(req *consts.Request) {
req.Http.JSON(http.StatusOK, gin.H{
"req": req.User.Sub,
})
//func CurrentUser(req *consts.Request) {
// req.Http.JSON(http.StatusOK, gin.H{
// "req": req.User.Sub,
// })
//
//}
func CurrentUser(c *gin.Context, user *consts.User) {
fmt.Println("CurrentUser", user)
c.JSON(200, gin.H{
"IP": c.ClientIP(),
"User": user.Token.Sub,
})
}

View File

@ -5,5 +5,5 @@ import (
)
func InitApiRoutes() {
HandleRoute(GET, "/", user.CurrentUser, MiddlewareJWTAuth, MiddlewareJSONResponse)
GET("/", MiddlewareJSONResponse, user.CurrentUser)
}

View File

@ -1,10 +1,14 @@
package providers
import (
"fmt"
"framework_v2/internal/access"
"framework_v2/internal/consts"
"framework_v2/internal/helper"
ginzap "github.com/gin-contrib/zap"
"github.com/gin-gonic/gin"
"net/http"
"reflect"
"time"
)
@ -18,43 +22,101 @@ func InitGin() {
//access.Router.Use(ginzap.RecoveryWithZap(access.Logger, true))
}
type httpMethod int
//func HandleRoute(method httpMethod, relativePath string, controller HandlerFunc, middlewares ...gin.HandlerFunc) {
// access.Router.Handle(method.String(), relativePath, func(c *gin.Context) {
// for _, middleware := range middlewares {
// middleware(c)
// }
//
// if !c.IsAborted() {
// handleWithMetadata(c, controller)
// }
// })
//}
var httpMethodStr = []string{"GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"}
//func handleWithMetadata(c *gin.Context, controller HandlerFunc) {
// var metadata = &consts.Request{
// Http: c,
// User: GetAuthFromCtx(c),
// }
//
// controller(metadata)
//}
func (h httpMethod) String() string {
return httpMethodStr[h-1]
}
const (
GET httpMethod = iota + 1
POST
PUT
DELETE
PATCH
HEAD
OPTIONS
)
type HandlerFunc func(metadata *consts.Request)
func HandleRoute(method httpMethod, relativePath string, controller HandlerFunc, middlewares ...gin.HandlerFunc) {
access.Router.Handle(method.String(), relativePath, func(c *gin.Context) {
for _, middleware := range middlewares {
middleware(c)
}
if !c.IsAborted() {
handleWithMetadata(c, controller)
}
func GET(relativePath string, handlers ...interface{}) {
access.Router.GET(relativePath, func(c *gin.Context) {
doHandler(c, handlers...)
})
}
func handleWithMetadata(c *gin.Context, controller HandlerFunc) {
var metadata = &consts.Request{
Http: c,
User: GetAuthFromCtx(c),
func POST(relativePath string, handlers ...interface{}) {
access.Router.POST(relativePath, func(c *gin.Context) {
doHandler(c, handlers...)
})
}
func PUT(relativePath string, handlers ...interface{}) {
access.Router.PUT(relativePath, func(c *gin.Context) {
doHandler(c, handlers...)
})
}
func PATCH(relativePath string, handlers ...interface{}) {
access.Router.PATCH(relativePath, func(c *gin.Context) {
doHandler(c, handlers...)
})
}
func DELETE(relativePath string, handlers ...interface{}) {
access.Router.DELETE(relativePath, func(c *gin.Context) {
doHandler(c, handlers...)
})
}
func doHandler(c *gin.Context, handlers ...interface{}) {
for _, handler := range handlers {
if c.IsAborted() {
// 是否已经响应
if c.Writer.Written() {
return
} else {
helper.ResponseError(c, http.StatusBadRequest, ErrEmptyResponse)
}
controller(metadata)
return
}
wrapHandler(c, handler)
}
}
func wrapHandler(c *gin.Context, f interface{}) {
fnValue := reflect.ValueOf(f)
fnType := fnValue.Type()
var args []reflect.Value
for i := 0; i < fnType.NumIn(); i++ {
argType := fnType.In(i)
var argValue reflect.Value
switch argType {
case reflect.TypeOf((*gin.Context)(nil)):
argValue = reflect.ValueOf(c)
case reflect.TypeOf((*consts.User)(nil)):
userInfo := DIJWTAuth(c)
if userInfo == nil {
helper.ResponseError(c, http.StatusUnauthorized, ErrNotValidToken)
return
}
argValue = reflect.ValueOf(userInfo)
default:
helper.ResponseError(c, http.StatusBadRequest, fmt.Errorf("invalid argument type: %s", argType.String()))
return
}
args = append(args, argValue)
}
fnValue.Call(args)
}

View File

@ -14,58 +14,58 @@ var (
ErrNotValidToken = errors.New("无效的 JWT 令牌。")
ErrJWTFormatError = errors.New("JWT 格式错误。")
ErrNotBearerType = errors.New("不是 Bearer 类型。")
ErrEmptyResponse = errors.New("我们的服务器返回了空请求,可能某些环节出了问题。")
)
const AnonymousUser = "anonymous"
func MiddlewareJWTAuth(c *gin.Context) {
// DIJWTAuth 用于注入到方法签名中。我觉得下面的代码以后可以优化。
func DIJWTAuth(c *gin.Context) *consts.User {
var sub = AnonymousUser
var jwtIdToken = &consts.UserTokenInfo{}
var jwtIdToken = &consts.User{}
if Config.DebugMode.Enable {
jwtIdToken.Sub = sub
jwtIdToken.Token.Sub = sub
} else {
// get authorization header
authorization := c.Request.Header.Get("Authorization")
if authorization == "" {
helper.ResponseError(c, http.StatusUnauthorized, ErrJWTFormatError)
return
return nil
}
authSplit := strings.Split(authorization, " ")
if len(authSplit) != 2 {
helper.ResponseError(c, http.StatusUnauthorized, ErrJWTFormatError)
return
return nil
}
if authSplit[0] != "Bearer" {
helper.ResponseError(c, http.StatusUnauthorized, ErrNotBearerType)
return
return nil
}
token, err := ParseJWT(authSplit[1])
if err != nil {
helper.ResponseError(c, http.StatusUnauthorized, ErrJWTFormatError)
return
return nil
}
sub, err = token.Claims.GetSubject()
if err != nil {
helper.ResponseError(c, http.StatusUnauthorized, ErrNotValidToken)
return
return nil
}
err = mapstructure.Decode(token.Claims, &jwtIdToken)
err = mapstructure.Decode(token.Claims, &jwtIdToken.Token)
if err != nil {
Logger.Error("Failed to map token claims to JwtIDToken struct.\nError: " + err.Error())
helper.ResponseError(c, http.StatusUnauthorized, ErrNotValidToken)
return
return nil
}
}
c.Set(consts.UserTokenInfoKey, jwtIdToken)
return
return jwtIdToken
}
func MiddlewareJSONResponse(c *gin.Context) {

28
main_test.go Normal file
View File

@ -0,0 +1,28 @@
package main
import (
"github.com/gin-gonic/gin"
"testing"
)
type MyInterface interface{}
type MyStruct struct{}
func BenchmarkTypeAssertionSuccess(b *testing.B) {
var x MyInterface = &MyStruct{}
for n := 0; n < b.N; n++ {
if _, ok := x.(*MyStruct); !ok {
b.Fatal("Type assertion failed")
}
}
}
func BenchmarkTypeAssertionFailure(b *testing.B) {
var x MyInterface = &MyStruct{}
for n := 0; n < b.N; n++ {
if _, ok := x.(*gin.Context); ok {
b.Fatal("Type assertion should fail")
}
}
}