├── .travis.yml ├── README.md ├── controller.go ├── hodenHelper.go ├── hopen.go └── utils └── render.go /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | sudo: false 3 | 4 | go: 5 | - 1.4 6 | - 1.5 7 | - 1.6 8 | - tip -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # hopen 2 | ==== 3 | ![hopen](https://travis-ci.org/who246/hopen.svg?branch=master) 4 | Golang web极速开发框架 Rapid development framework 5 | #how to use 6 | 7 | ##main 8 | 9 | ```Go 10 | func init(){ 11 | //hopen.AddRouter("/test/:id([0-9]+)/sss",&testController.TestController{},"get:tohtml"); 12 | //hopen.AddAutoRouter("/test/:id([0-9]+)/tohtml",&testController.TestController{}); 13 | hopen.AddPrefixAutoRouter("/test",&testController.TestController{}); 14 | } 15 | func main(){ 16 | hopen.Run() 17 | } 18 | ``` 19 | 20 | ##controller 21 | ```Go 22 | type TestController struct { 23 | hopen.Controller 24 | } 25 | 26 | func (t *TestController) Sayhello() { 27 | print(t.R.Form.Get("id")) 28 | } 29 | func (t *TestController) ToJson() { 30 | m := make(map[string]string) 31 | m["show_branch"] = "false" 32 | m["t0"] = "true" 33 | m["t1"] = "true" 34 | t.SetValue("data", m) 35 | t.SetValue("msg", "测试") 36 | t.SetValue("status", "测试") 37 | t.RenderJson() 38 | } 39 | 40 | type Servers struct { 41 | XMLName xml.Name `xml:"server"` 42 | ServerName string `xml:"serverName"` 43 | ServerIP string `xml:"serverIP"` 44 | } 45 | 46 | func (t *TestController) ToXml() { 47 | v := &Servers{ServerName:"2",ServerIP:"3"} 48 | t.RenderXml(v) 49 | } 50 | func (t *TestController) ToHtml() { 51 | id ,_ := t.GetI("id",-1); 52 | t.SetValue("msg", "id is " + strconv.FormatInt(int64(id), 10)) 53 | t.Render("tmpl/welcome.html") 54 | } 55 | func (t *TestController) RedirectTo() { 56 | t.Redirect("tojson") 57 | } 58 | ``` 59 | -------------------------------------------------------------------------------- /controller.go: -------------------------------------------------------------------------------- 1 | package hopen 2 | 3 | import ( 4 | "net/http" 5 | "github.com/who246/hopen/utils" 6 | "strconv" 7 | "text/template" 8 | ) 9 | 10 | type controllerInterface interface { 11 | Init(w http.ResponseWriter, r *http.Request) 12 | } 13 | type Controller struct { 14 | W http.ResponseWriter 15 | R *http.Request 16 | Data map[string]interface{} 17 | } 18 | 19 | func (c *Controller) Init(w http.ResponseWriter, r *http.Request) { 20 | c.W = w 21 | c.R = r 22 | c.Data = make(map[string]interface{}) 23 | } 24 | 25 | func (c *Controller) SetValue(key string, obj interface{}) { 26 | c.Data[key] = obj 27 | } 28 | func (c *Controller) GetS(key string) string{ 29 | return c.R.Form.Get(key) 30 | } 31 | 32 | func (c *Controller) GetI(key string,def int) (int,error){ 33 | if v := c.R.Form.Get(key); v != ""{ 34 | return strconv.Atoi(v) 35 | } 36 | return def,nil 37 | } 38 | func (c *Controller) GetI8(key string,def int8) (int8,error){ 39 | if v := c.R.Form.Get(key); v != ""{ 40 | i64,err := strconv.ParseInt(v,10,8) 41 | i8 := int8(i64) 42 | return i8,err 43 | } 44 | return def,nil 45 | } 46 | func (c *Controller) GetI32(key string,def int32) (int32,error){ 47 | if v := c.R.Form.Get(key); v != ""{ 48 | i64,err := strconv.ParseInt(v,10,32) 49 | i32 := int32(i64) 50 | return i32,err 51 | } 52 | return def,nil 53 | } 54 | func (c *Controller) GetI64(key string,def int64) (int64,error){ 55 | if v := c.R.Form.Get(key); v != ""{ 56 | return strconv.ParseInt(v,10,64) 57 | } 58 | return def,nil 59 | } 60 | func (c *Controller) GetB(key string, def bool) (bool, error) { 61 | if strv := c.R.Form.Get(key); strv != "" { 62 | return strconv.ParseBool(strv) 63 | } else { 64 | return def, nil 65 | } 66 | } 67 | 68 | func (c *Controller) GetF(key string, def float64) (float64, error) { 69 | if strv := c.R.Form.Get(key); strv != "" { 70 | return strconv.ParseFloat(strv, 64) 71 | } else { 72 | return def, nil 73 | } 74 | } 75 | func (c *Controller) RenderJson() error { 76 | return utils.RenderJson(c.W,c.Data) 77 | } 78 | func (c *Controller) RenderXml(obj interface{}) error { 79 | 80 | return utils.RenderXml(c.W,obj) 81 | } 82 | func (c *Controller) Render(path string) error { 83 | return utils.Render(c.W,path,c.Data) 84 | } 85 | func (c *Controller) HTMLEscapeString(data string) string { 86 | return template.HTMLEscapeString(data) 87 | } 88 | func (c *Controller) Redirect(url string) { 89 | c.W.Header().Add("Location", url) 90 | c.W.WriteHeader(302) 91 | } -------------------------------------------------------------------------------- /hodenHelper.go: -------------------------------------------------------------------------------- 1 | package hopen 2 | 3 | import ( 4 | "reflect" 5 | "regexp" 6 | "strings" 7 | ) 8 | 9 | type routerRegistorInfo struct { 10 | regex *regexp.Regexp 11 | params map[int]string 12 | controllerType reflect.Type 13 | methods []*method 14 | urlRouter bool 15 | controller controllerInterface 16 | } 17 | 18 | type RouterRegistor struct { 19 | info []*routerRegistorInfo 20 | } 21 | type method struct { 22 | methdName string 23 | methdType string 24 | } 25 | 26 | func (this *RouterRegistor) Registor(pattern string, c controllerInterface, methdNames string, urlRouter bool) { 27 | parts := strings.Split(pattern, "/") 28 | j := 0 29 | params := make(map[int]string) 30 | for i, part := range parts { 31 | if strings.HasPrefix(part, ":") { 32 | expr := "([^/]+)" 33 | if index := strings.Index(part, "("); index != -1 { 34 | expr = part[index:] 35 | part = part[1:index] 36 | } 37 | params[j] = part 38 | parts[i] = expr 39 | j++ 40 | } 41 | } 42 | methods := []*method{} 43 | if !urlRouter { 44 | methodStr := strings.Split(methdNames, ",") 45 | for _, str := range methodStr { 46 | ms := strings.Split(str, ":") 47 | m := &method{methdType: ms[0], methdName: ms[1]} 48 | methods = append(methods, m) 49 | } 50 | } 51 | pattern = strings.Join(parts, "/") 52 | regex, regexErr := regexp.Compile(pattern) 53 | if regexErr != nil { 54 | return 55 | } 56 | 57 | t := reflect.Indirect(reflect.ValueOf(c)).Type() 58 | route := &routerRegistorInfo{} 59 | route.regex = regex 60 | route.controllerType = t 61 | route.params = params 62 | route.methods = methods 63 | route.urlRouter = urlRouter 64 | route.controller = c 65 | this.info = append(this.info, route) 66 | } 67 | -------------------------------------------------------------------------------- /hopen.go: -------------------------------------------------------------------------------- 1 | package hopen 2 | 3 | import ( 4 | "errors" 5 | "net/http" 6 | "net/url" 7 | "reflect" 8 | "strings" 9 | ) 10 | 11 | var ( 12 | router *RouterRegistor 13 | staticDirs map[string]string 14 | ) 15 | 16 | func init() { 17 | router = &RouterRegistor{} 18 | staticDirs = make(map[string]string) 19 | //设置默认静态目录 20 | SetStaticDir("/static", "static") 21 | } 22 | func SetStaticDir(url,dir string){ 23 | staticDirs[url] = dir 24 | } 25 | func Run() { 26 | RunWithPort(":9090") 27 | } 28 | func RunWithPort(port string) { 29 | http.HandleFunc("/", httpMethod) //设置访问的路由 30 | err := http.ListenAndServe(port, nil) //设置监听的端口 31 | if err != nil { 32 | panic(err) 33 | } 34 | } 35 | func httpMethod(w http.ResponseWriter, r *http.Request) { 36 | for prefix, staticDir := range staticDirs { 37 | if strings.HasPrefix(r.URL.Path, prefix) { 38 | file := staticDir + r.URL.Path[len(prefix):] 39 | println(file) 40 | http.ServeFile(w, r, file) 41 | return 42 | } 43 | } 44 | requestPath := r.URL.Path 45 | isFind := false; 46 | for _, route := range router.info { 47 | if !route.regex.MatchString(requestPath) { 48 | continue 49 | } 50 | matches := route.regex.FindStringSubmatch(requestPath) 51 | if matches == nil || len(matches[0]) != len(requestPath) { 52 | continue 53 | } 54 | //params := make(map[string]string) 55 | if len(route.params) > 0 { 56 | values := r.URL.Query() 57 | for i, match := range matches[1:] { 58 | values.Add(route.params[i], match) 59 | //params[route.params[i]] = match 60 | } 61 | r.URL.RawQuery = url.Values(values).Encode() // + "&" + r.URL.RawQuery 62 | } 63 | r.ParseForm() 64 | //初始化 65 | vc := reflect.New(route.controllerType) 66 | init := vc.MethodByName("Init") 67 | in := make([]reflect.Value, 2) 68 | in[0] = reflect.ValueOf(w) 69 | in[1] = reflect.ValueOf(r) 70 | init.Call(in) 71 | var methdName string 72 | //获取调用的方法 73 | if !route.urlRouter { 74 | for _, method := range route.methods { 75 | var err error 76 | if (strings.ToUpper(method.methdType) == r.Method || method.methdType == "*") && strings.ToLower(method.methdName) != "init" { 77 | err, methdName = findMethod(route.controller, method.methdName) 78 | if err != nil{ 79 | continue 80 | } 81 | } 82 | 83 | } 84 | if methdName == "" { 85 | http.NotFound(w, r) 86 | return 87 | } 88 | } else { 89 | var err error 90 | parts := strings.Split(requestPath, "/") 91 | orMethodName := parts[len(parts)-1] 92 | if orMethodName == "" || strings.ToLower(orMethodName) == "init" { 93 | http.NotFound(w, r) 94 | break 95 | } 96 | err, methdName = findMethod(route.controller, orMethodName) 97 | if err != nil { 98 | http.NotFound(w, r) 99 | break 100 | } 101 | } 102 | methd := vc.MethodByName(methdName) 103 | methd.Call([]reflect.Value{}) 104 | isFind = true 105 | break 106 | } 107 | if !isFind { 108 | http.NotFound(w, r) 109 | } 110 | } 111 | func AddRouter(pattern string, c controllerInterface, methdName string) { 112 | router.Registor(pattern, c, methdName, false) 113 | } 114 | 115 | //example. hopen.AddAutoRouter("/test/sayhello",&testController.TestController{}); 116 | // if url is /test/sayhello Call Sayhello method 117 | func AddAutoRouter(pattern string, c controllerInterface) { 118 | parts := strings.Split(pattern, "/") 119 | orMethodName := parts[len(parts)-1] 120 | router.Registor(pattern, c, "*:"+orMethodName, false) 121 | } 122 | 123 | //example. hopen.AddPrefixAutoRouter("/test",&testController.TestController{}); 124 | // if url is /test/sayhello Call Sayhello method 125 | func AddPrefixAutoRouter(pattern string, c controllerInterface) { 126 | pattern = pattern + "/([^/]+)" 127 | router.Registor(pattern, c, "", true) 128 | } 129 | func findMethod(c controllerInterface, methodName string) (error, string) { 130 | v := reflect.ValueOf(c) 131 | vt := v.Type() 132 | methodName = strings.ToLower(methodName) 133 | for i := 0; i < vt.NumMethod(); i++ { 134 | if strings.ToLower(vt.Method(i).Name) == methodName { 135 | return nil, vt.Method(i).Name 136 | } 137 | } 138 | return errors.New("method not find"), "" 139 | } 140 | -------------------------------------------------------------------------------- /utils/render.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "encoding/json" 5 | "encoding/xml" 6 | "html/template" 7 | "io/ioutil" 8 | "net/http" 9 | ) 10 | 11 | var renderCache map[string]*template.Template 12 | 13 | func init() { 14 | renderCache = make(map[string]*template.Template) 15 | } 16 | func RenderJson(w http.ResponseWriter, data interface{}) error { 17 | w.Header().Add("Content-Type", "application/json; charset=utf-8") 18 | content, err := json.Marshal(data) 19 | if err != nil { 20 | http.Error(w, err.Error(), http.StatusInternalServerError) 21 | return err 22 | } 23 | _, err = w.Write(content) 24 | return err 25 | } 26 | 27 | func RenderXml(w http.ResponseWriter, obj interface{}) error { 28 | w.Header().Add("Content-Type", "application/xml; charset=utf-8") 29 | content, err := xml.Marshal(obj) 30 | if err != nil { 31 | http.Error(w, err.Error(), http.StatusInternalServerError) 32 | return err 33 | } 34 | _, err = w.Write(content) 35 | return err 36 | } 37 | 38 | func Render(w http.ResponseWriter, path string, data interface{}) error { 39 | var err error 40 | t, ok := renderCache[path] 41 | if ok { 42 | err = t.Execute(w, data) 43 | return err 44 | } 45 | t = template.New(path) 46 | b, err := ioutil.ReadFile(path) 47 | s := string(b) 48 | _, err = t.Parse(s) 49 | 50 | if err != nil { 51 | return err 52 | } 53 | err = t.Execute(w, data) 54 | if err == nil { 55 | renderCache[path] = t 56 | } 57 | return err 58 | } 59 | --------------------------------------------------------------------------------