github.com/jackgris/buffalo@v0.10.3-0.20171206174715-b1dbfd2402de/router.go (about) 1 package buffalo 2 3 import ( 4 "fmt" 5 "net/http" 6 "path" 7 "reflect" 8 "sort" 9 "strings" 10 11 "github.com/markbates/inflect" 12 ) 13 14 // GET maps an HTTP "GET" request to the path and the specified handler. 15 func (a *App) GET(p string, h Handler) *RouteInfo { 16 return a.addRoute("GET", p, h) 17 } 18 19 // POST maps an HTTP "POST" request to the path and the specified handler. 20 func (a *App) POST(p string, h Handler) *RouteInfo { 21 return a.addRoute("POST", p, h) 22 } 23 24 // PUT maps an HTTP "PUT" request to the path and the specified handler. 25 func (a *App) PUT(p string, h Handler) *RouteInfo { 26 return a.addRoute("PUT", p, h) 27 } 28 29 // DELETE maps an HTTP "DELETE" request to the path and the specified handler. 30 func (a *App) DELETE(p string, h Handler) *RouteInfo { 31 return a.addRoute("DELETE", p, h) 32 } 33 34 // HEAD maps an HTTP "HEAD" request to the path and the specified handler. 35 func (a *App) HEAD(p string, h Handler) *RouteInfo { 36 return a.addRoute("HEAD", p, h) 37 } 38 39 // OPTIONS maps an HTTP "OPTIONS" request to the path and the specified handler. 40 func (a *App) OPTIONS(p string, h Handler) *RouteInfo { 41 return a.addRoute("OPTIONS", p, h) 42 } 43 44 // PATCH maps an HTTP "PATCH" request to the path and the specified handler. 45 func (a *App) PATCH(p string, h Handler) *RouteInfo { 46 return a.addRoute("PATCH", p, h) 47 } 48 49 // Redirect from one URL to another URL. Only works for "GET" requests. 50 func (a *App) Redirect(status int, from, to string) *RouteInfo { 51 return a.GET(from, func(c Context) error { 52 return c.Redirect(status, to) 53 }) 54 } 55 56 // Mount mounts a http.Handler (or Buffalo app) and passes through all requests to it. 57 /* 58 func muxer() http.Handler { 59 f := func(res http.ResponseWriter, req *http.Request) { 60 fmt.Fprintf(res, "%s - %s", req.Method, req.URL.String()) 61 } 62 mux := mux.NewRouter() 63 mux.HandleFunc("/foo", f).Methods("GET") 64 mux.HandleFunc("/bar", f).Methods("POST") 65 mux.HandleFunc("/baz/baz", f).Methods("DELETE") 66 return mux 67 } 68 69 a.Mount("/admin", muxer()) 70 71 $ curl -X DELETE http://localhost:3000/admin/baz/baz 72 */ 73 func (a *App) Mount(p string, h http.Handler) { 74 prefix := path.Join(a.Prefix, p) 75 path := path.Join(p, "{path:.+}") 76 a.ANY(path, WrapHandler(http.StripPrefix(prefix, h))) 77 } 78 79 // ServeFiles maps an path to a directory on disk to serve static files. 80 // Useful for JavaScript, images, CSS, etc... 81 /* 82 a.ServeFiles("/assets", http.Dir("path/to/assets")) 83 */ 84 func (a *App) ServeFiles(p string, root http.FileSystem) { 85 path := path.Join(a.Prefix, p) 86 a.router.PathPrefix(path).Handler(http.StripPrefix(path, http.FileServer(root))) 87 } 88 89 // Resource maps an implementation of the Resource interface 90 // to the appropriate RESTful mappings. Resource returns the *App 91 // associated with this group of mappings so you can set middleware, etc... 92 // on that group, just as if you had used the a.Group functionality. 93 /* 94 a.Resource("/users", &UsersResource{}) 95 96 // Is equal to this: 97 98 ur := &UsersResource{} 99 g := a.Group("/users") 100 g.GET("/", ur.List) // GET /users => ur.List 101 g.GET("/new", ur.New) // GET /users/new => ur.New 102 g.GET("/{user_id}", ur.Show) // GET /users/{user_id} => ur.Show 103 g.GET("/{user_id}/edit", ur.Edit) // GET /users/{user_id}/edit => ur.Edit 104 g.POST("/", ur.Create) // POST /users => ur.Create 105 g.PUT("/{user_id}", ur.Update) PUT /users/{user_id} => ur.Update 106 g.DELETE("/{user_id}", ur.Destroy) DELETE /users/{user_id} => ur.Destroy 107 */ 108 func (a *App) Resource(p string, r Resource) *App { 109 g := a.Group(p) 110 p = "/" 111 112 rv := reflect.ValueOf(r) 113 if rv.Kind() == reflect.Ptr { 114 rv = rv.Elem() 115 } 116 117 rt := rv.Type() 118 rname := fmt.Sprintf("%s.%s", rt.PkgPath(), rt.Name()) + ".%s" 119 120 name := strings.Replace(rt.Name(), "Resource", "", 1) 121 paramName := inflect.Singularize(inflect.Underscore(name)) 122 123 spath := path.Join(p, fmt.Sprintf("{%s_id}", paramName)) 124 setFuncKey(r.List, fmt.Sprintf(rname, "List")) 125 g.GET(p, r.List) 126 setFuncKey(r.New, fmt.Sprintf(rname, "New")) 127 g.GET(path.Join(p, "new"), r.New) 128 setFuncKey(r.Show, fmt.Sprintf(rname, "Show")) 129 g.GET(path.Join(spath), r.Show) 130 setFuncKey(r.Edit, fmt.Sprintf(rname, "Edit")) 131 g.GET(path.Join(spath, "edit"), r.Edit) 132 setFuncKey(r.Create, fmt.Sprintf(rname, "Create")) 133 g.POST(p, r.Create) 134 setFuncKey(r.Update, fmt.Sprintf(rname, "Update")) 135 g.PUT(path.Join(spath), r.Update) 136 setFuncKey(r.Destroy, fmt.Sprintf(rname, "Destroy")) 137 g.DELETE(path.Join(spath), r.Destroy) 138 g.Prefix = path.Join(g.Prefix, spath) 139 return g 140 } 141 142 // ANY accepts a request across any HTTP method for the specified path 143 // and routes it to the specified Handler. 144 func (a *App) ANY(p string, h Handler) { 145 a.GET(p, h) 146 a.POST(p, h) 147 a.PUT(p, h) 148 a.PATCH(p, h) 149 a.HEAD(p, h) 150 a.OPTIONS(p, h) 151 a.DELETE(p, h) 152 } 153 154 // Group creates a new `*App` that inherits from it's parent `*App`. 155 // This is useful for creating groups of end-points that need to share 156 // common functionality, like middleware. 157 /* 158 g := a.Group("/api/v1") 159 g.Use(AuthorizeAPIMiddleware) 160 g.GET("/users, APIUsersHandler) 161 g.GET("/users/:user_id, APIUserShowHandler) 162 */ 163 func (a *App) Group(groupPath string) *App { 164 g := New(a.Options) 165 g.Prefix = path.Join(a.Prefix, groupPath) 166 g.Name = g.Prefix 167 168 g.router = a.router 169 g.Middleware = a.Middleware.clone() 170 g.ErrorHandlers = a.ErrorHandlers 171 g.root = a 172 if a.root != nil { 173 g.root = a.root 174 } 175 a.children = append(a.children, g) 176 return g 177 } 178 179 func (a *App) addRoute(method string, url string, h Handler) *RouteInfo { 180 a.moot.Lock() 181 defer a.moot.Unlock() 182 183 url = path.Join(a.Prefix, url) 184 name := a.buildRouteName(url) 185 186 hs := funcKey(h) 187 r := &RouteInfo{ 188 Method: method, 189 Path: url, 190 HandlerName: hs, 191 Handler: h, 192 App: a, 193 Aliases: []string{}, 194 } 195 196 r.MuxRoute = a.router.Handle(url, r).Methods(method) 197 r.Name(name) 198 199 routes := a.Routes() 200 routes = append(routes, r) 201 sort.Sort(routes) 202 203 if a.root != nil { 204 a.root.routes = routes 205 } else { 206 a.routes = routes 207 } 208 209 return r 210 } 211 212 //buildRouteName builds a route based on the path passed. 213 func (a *App) buildRouteName(p string) string { 214 if p == "/" || p == "" { 215 return "root" 216 } 217 218 resultParts := []string{} 219 parts := strings.Split(p, "/") 220 221 for index, part := range parts { 222 223 if strings.Contains(part, "{") || part == "" { 224 continue 225 } 226 227 shouldSingularize := (len(parts) > index+1) && strings.Contains(parts[index+1], "{") 228 if shouldSingularize { 229 part = inflect.Singularize(part) 230 } 231 232 if parts[index] == "new" || parts[index] == "edit" { 233 resultParts = append([]string{part}, resultParts...) 234 continue 235 } 236 237 if index > 0 && strings.Contains(parts[index-1], "}") { 238 resultParts = append(resultParts, part) 239 continue 240 } 241 242 resultParts = append(resultParts, part) 243 } 244 245 if len(resultParts) == 0 { 246 return "unnamed" 247 } 248 249 underscore := strings.TrimSpace(strings.Join(resultParts, "_")) 250 return inflect.CamelizeDownFirst(underscore) 251 }