github.com/sohaha/zlsgo@v1.7.13-0.20240501141223-10dd1a906f76/znet/bind_router.go (about)

     1  package znet
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  	"strings"
     7  
     8  	"github.com/sohaha/zlsgo/zreflect"
     9  	"github.com/sohaha/zlsgo/zstring"
    10  	"github.com/sohaha/zlsgo/zutil"
    11  )
    12  
    13  // BindStruct Bind Struct
    14  func (e *Engine) BindStruct(prefix string, s interface{}, handle ...Handler) error {
    15  	g := e.Group(prefix)
    16  	if len(handle) > 0 {
    17  		for _, v := range handle {
    18  			g.Use(v)
    19  		}
    20  	}
    21  	of := reflect.ValueOf(s)
    22  	if !of.IsValid() {
    23  		return nil
    24  	}
    25  	initFn := of.MethodByName("Init")
    26  	if initFn.IsValid() {
    27  		before, ok := initFn.Interface().(func(e *Engine))
    28  		if ok {
    29  			before(g)
    30  		} else {
    31  			if before, ok := initFn.Interface().(func(e *Engine) error); !ok {
    32  				return fmt.Errorf("func: [%s] is not an effective routing method", "Init")
    33  			} else {
    34  				if err := before(g); err != nil {
    35  					return err
    36  				}
    37  			}
    38  		}
    39  
    40  	}
    41  	typeOf := reflect.Indirect(of).Type()
    42  	return zutil.TryCatch(func() error {
    43  		return zreflect.ForEachMethod(of, func(i int, m reflect.Method, value reflect.Value) error {
    44  			if m.Name == "Init" {
    45  				return nil
    46  			}
    47  			path, method, key := "", "", ""
    48  			methods := `ANY|GET|POST|PUT|DELETE|PATCH|HEAD|OPTIONS`
    49  			regex := `^(?i)(` + methods + `)(.*)$`
    50  			info, err := zstring.RegexExtract(regex, m.Name)
    51  			infoLen := len(info)
    52  			if err != nil || infoLen != 3 {
    53  				indexs := zstring.RegexFind(`(?i)(`+methods+`)`, m.Name, 1)
    54  				if len(indexs) == 0 {
    55  					if e.IsDebug() && m.Name != "Init" {
    56  						e.Log.Warnf("matching rule error: %s%s\n", m.Name, m.Func.String())
    57  					}
    58  					return nil
    59  				}
    60  
    61  				index := indexs[0]
    62  				method = strings.ToUpper(m.Name[index[0]:index[1]])
    63  				path = m.Name[index[1]:]
    64  				key = strings.ToLower(m.Name[:index[0]])
    65  			} else {
    66  				path = info[2]
    67  				method = strings.ToUpper(info[1])
    68  			}
    69  
    70  			fn := value.Interface()
    71  			handleName := strings.Join([]string{typeOf.PkgPath(), typeOf.Name(), m.Name}, ".")
    72  			if e.BindStructCase != nil {
    73  				path = e.BindStructCase(path)
    74  			} else if e.BindStructDelimiter != "" {
    75  				path = zstring.CamelCaseToSnakeCase(path, e.BindStructDelimiter)
    76  			}
    77  			if path == "" {
    78  				path = "/"
    79  			}
    80  			if key != "" {
    81  				if strings.HasSuffix(path, "/") {
    82  					path += ":" + key
    83  				} else {
    84  					path += "/:" + key
    85  				}
    86  			} else if path != "/" && e.BindStructSuffix != "" {
    87  				path = path + e.BindStructSuffix
    88  			}
    89  			if path == "/" {
    90  				path = ""
    91  			}
    92  
    93  			var (
    94  				p  string
    95  				l  int
    96  				ok bool
    97  			)
    98  
    99  			if method == "ANY" {
   100  				p, l, ok = g.handleAny(path, Utils.ParseHandlerFunc(fn), nil, nil)
   101  			} else {
   102  				p, l, ok = g.addHandle(method, path, Utils.ParseHandlerFunc(fn), nil, nil)
   103  			}
   104  
   105  			if ok && e.IsDebug() {
   106  				f := fmt.Sprintf("%%s %%-40s -> %s (%d handlers)", handleName, l)
   107  				if e.webMode == testCode {
   108  					f = "%s %-40s"
   109  				}
   110  				e.Log.Debug(routeLog(e.Log, f, method, p))
   111  			}
   112  			return nil
   113  		})
   114  	})
   115  }