package api import ( "context" "encoding/json" "errors" "net/http" "reflect" "strconv" "strings" ) func handleTag(s string, r *http.Request) string { parts := strings.Split(s, ":") where := "Q" key := "" if len(parts) == 1 { key = parts[0] } else { where = parts[0] key = parts[1] } switch where { case "Q": return r.URL.Query().Get(key) case "H": return r.Header.Get(key) case "P": switch key { case "*": return r.URL.Path case "last": pps := strings.Split(r.URL.Path, "/") return pps[len(pps)-1] case "len": pps := strings.Split(r.URL.Path, "/") return strconv.Itoa(len(pps)) default: pps := strings.Split(r.URL.Path, "/") n, _ := strconv.Atoi(key) if n < len(pps) { return pps[n] } return "" } } return "" } func convert(s string, tpname string) interface{} { switch tpname { case "string": return s case "int": v, _ := strconv.Atoi(s) return v case "int8": v, _ := strconv.Atoi(s) return int8(v) case "int16": v, _ := strconv.Atoi(s) return int16(v) case "int32": v, _ := strconv.Atoi(s) return int32(v) case "int64": v, _ := strconv.Atoi(s) return int64(v) case "uint": v, _ := strconv.Atoi(s) return uint(v) case "float32": v, _ := strconv.Atoi(s) return float32(v) case "float64": v, _ := strconv.Atoi(s) return float64(v) case "bool": return s == "true" || s == "1" || s == "Y" } return nil } func Map(r *http.Request, in interface{}) error { tp := reflect.TypeOf(in) vl := reflect.ValueOf(in) if tp.Kind() == reflect.Ptr { tp = tp.Elem() vl = vl.Elem() } if tp.Kind() != reflect.Struct { return errors.New("Type is not struct") } for i := 0; i < tp.NumField(); i++ { k, ok := tp.Field(i).Tag.Lookup("in") if ok { str := handleTag(k, r) v := convert(str, tp.Field(i).Type.Name()) strv := reflect.ValueOf(v) vl.Field(i).Set(strv) } } return nil } type API struct { Mux *http.ServeMux Perms map[string]string } func (a *API) GetPerm(r *http.Request) string { return a.Perms[r.Method+"_"+strings.Split(r.RequestURI, "?")[0]] } func h_Dosome(w http.ResponseWriter, r *http.Request) { ctx := r.Context() ctx = context.WithValue(r.Context(), "REQ", r) ctx = context.WithValue(ctx, "RES", w) req := &SomeReq{} err := Map(r, req) if err != nil { http.Error(w, err.Error(), 500) return } if r.Method != http.MethodGet && r.Method != http.MethodHead { err := json.NewDecoder(r.Body).Decode(req) if err != nil { http.Error(w, err.Error(), 500) return } } res, err := Dosome(ctx, req) if err != nil { http.Error(w, err.Error(), 500) return } w.Header().Add("Content-Type", "Application/json") err = json.NewEncoder(w).Encode(res) if err != nil { http.Error(w, err.Error(), 500) return } } func h_Dosome2(w http.ResponseWriter, r *http.Request) { ctx := r.Context() ctx = context.WithValue(r.Context(), "REQ", r) ctx = context.WithValue(ctx, "RES", w) req := &SomeReq2{} err := Map(r, req) if err != nil { http.Error(w, err.Error(), 500) return } if r.Method != http.MethodGet && r.Method != http.MethodHead { err := json.NewDecoder(r.Body).Decode(req) if err != nil { http.Error(w, err.Error(), 500) return } } res, err := Dosome2(ctx, req) if err != nil { http.Error(w, err.Error(), 500) return } w.Header().Add("Content-Type", "Application/json") err = json.NewEncoder(w).Encode(res) if err != nil { http.Error(w, err.Error(), 500) return } } func Init() *API { mux := &http.ServeMux{} ret := &API{ Mux: mux, Perms: make(map[string]string), } mux.HandleFunc("/some", func(w http.ResponseWriter, r *http.Request) { switch r.Method { case "POST": h_Dosome(w, r) default: http.Error(w, "Method not allowed", 500) } }) mux.HandleFunc("/some2", func(w http.ResponseWriter, r *http.Request) { switch r.Method { case "POST": h_Dosome2(w, r) default: http.Error(w, "Method not allowed", 500) } }) return ret }