github.com/System-Glitch/goyave/v3@v3.6.1-0.20210226143142-ac2fe42ee80e/route.go (about) 1 package goyave 2 3 import ( 4 "fmt" 5 "net/http" 6 "strings" 7 8 "github.com/System-Glitch/goyave/v3/validation" 9 ) 10 11 // Route stores information for matching and serving. 12 type Route struct { 13 name string 14 uri string 15 methods []string 16 parent *Router 17 handler Handler 18 validationRules *validation.Rules 19 middlewareHolder 20 parametrizeable 21 } 22 23 var _ routeMatcher = (*Route)(nil) // implements routeMatcher 24 25 // newRoute create a new route without any settings except its handler. 26 // This is used to generate a fake route for the Method Not Allowed and Not Found handlers. 27 // This route has the core middleware enabled and can be used without a parent router. 28 // Thus, custom status handlers can use language and body. 29 func newRoute(handler Handler) *Route { 30 return &Route{ 31 handler: handler, 32 middlewareHolder: middlewareHolder{ 33 middleware: []Middleware{recoveryMiddleware, parseRequestMiddleware, languageMiddleware}, 34 }, 35 } 36 } 37 38 func (r *Route) match(req *http.Request, match *routeMatch) bool { 39 if params := r.parametrizeable.regex.FindStringSubmatch(match.currentPath); params != nil { 40 if r.checkMethod(req.Method) { 41 if len(params) > 1 { 42 match.mergeParams(r.makeParameters(params)) 43 } 44 match.route = r 45 return true 46 } 47 match.err = errMatchMethodNotAllowed 48 return false 49 } 50 51 if match.err == nil { 52 // Don't override error if already set. 53 // Not nil error means it's either already errMatchNotFound 54 // or it's errMatchMethodNotAllowed, implying that a route has 55 // already been matched but with wrong method. 56 match.err = errMatchNotFound 57 } 58 return false 59 } 60 61 func (r *Route) checkMethod(method string) bool { 62 for _, m := range r.methods { 63 if m == method { 64 return true 65 } 66 } 67 return false 68 } 69 70 func (r *Route) makeParameters(match []string) map[string]string { 71 return r.parametrizeable.makeParameters(match, r.parameters) 72 } 73 74 // Name set the name of the route. 75 // Panics if a route with the same name already exists. 76 // Returns itself. 77 func (r *Route) Name(name string) *Route { 78 if r.name != "" { 79 panic(fmt.Errorf("Route name is already set")) 80 } 81 82 if _, ok := r.parent.namedRoutes[name]; ok { 83 panic(fmt.Errorf("Route %q already exists", name)) 84 } 85 86 r.name = name 87 r.parent.namedRoutes[name] = r 88 return r 89 } 90 91 // Validate adds validation rules to this route. If the user-submitted data 92 // doesn't pass validation, the user will receive an error and messages explaining 93 // what is wrong. 94 // 95 // Returns itself. 96 func (r *Route) Validate(validationRules validation.Ruler) *Route { 97 r.validationRules = validationRules.AsRules() 98 return r 99 } 100 101 // Middleware register middleware for this route only. 102 // 103 // Returns itself. 104 func (r *Route) Middleware(middleware ...Middleware) *Route { 105 r.middleware = middleware 106 return r 107 } 108 109 // BuildURL build a full URL pointing to this route. 110 // Panics if the amount of parameters doesn't match the amount of 111 // actual parameters for this route. 112 func (r *Route) BuildURL(parameters ...string) string { 113 fullURI, fullParameters := r.getFullParameters() 114 115 if len(parameters) != len(fullParameters) { 116 panic(fmt.Errorf("BuildURL: route has %d parameters, %d given", len(fullParameters), len(parameters))) 117 } 118 119 address := BaseURL() 120 121 var builder strings.Builder 122 builder.Grow(len(fullURI) + len(address)) 123 124 builder.WriteString(address) 125 126 idxs, _ := r.braceIndices(fullURI) 127 length := len(idxs) 128 end := 0 129 currentParam := 0 130 for i := 0; i < length; i += 2 { 131 raw := fullURI[end:idxs[i]] 132 end = idxs[i+1] 133 builder.WriteString(raw) 134 builder.WriteString(parameters[currentParam]) 135 currentParam++ 136 end++ // Skip closing braces 137 } 138 builder.WriteString(fullURI[end:]) 139 140 return builder.String() 141 } 142 143 // GetName get the name of this route. 144 func (r *Route) GetName() string { 145 return r.name 146 } 147 148 // GetURI get the URI of this route. 149 // The returned URI is relative to the parent router of this route, it is NOT 150 // the full path to this route. 151 // 152 // Note that this URI may contain route parameters in their définition format. 153 // Use the request's URI if you want to see the URI as it was requested by the client. 154 func (r *Route) GetURI() string { 155 return r.uri 156 } 157 158 // GetFullURI get the full URI of this route. 159 // 160 // Note that this URI may contain route parameters in their définition format. 161 // Use the request's URI if you want to see the URI as it was requested by the client. 162 func (r *Route) GetFullURI() string { 163 router := r.parent 164 segments := make([]string, 0, 3) 165 segments = append(segments, r.uri) 166 167 for router != nil { 168 segments = append(segments, router.prefix) 169 router = router.parent 170 } 171 172 // Revert segements 173 for i := len(segments)/2 - 1; i >= 0; i-- { 174 opp := len(segments) - 1 - i 175 segments[i], segments[opp] = segments[opp], segments[i] 176 } 177 178 return strings.Join(segments, "") 179 } 180 181 // GetMethods returns the methods the route matches against. 182 func (r *Route) GetMethods() []string { 183 cpy := make([]string, len(r.methods)) 184 copy(cpy, r.methods) 185 return cpy 186 } 187 188 // getFullParameters get the full uri and parameters for this route and all its parent routers. 189 func (r *Route) getFullParameters() (string, []string) { 190 router := r.parent 191 segments := make([]string, 0, 3) 192 segments = append(segments, r.uri) 193 194 parameters := make([]string, 0, len(r.parameters)) 195 for i := len(r.parameters) - 1; i >= 0; i-- { 196 parameters = append(parameters, r.parameters[i]) 197 } 198 199 for router != nil { 200 segments = append(segments, router.prefix) 201 for i := len(router.parameters) - 1; i >= 0; i-- { 202 parameters = append(parameters, router.parameters[i]) 203 } 204 router = router.parent 205 } 206 207 // Revert segements 208 for i := len(segments)/2 - 1; i >= 0; i-- { 209 opp := len(segments) - 1 - i 210 segments[i], segments[opp] = segments[opp], segments[i] 211 } 212 213 // Revert parameters 214 for i := len(parameters)/2 - 1; i >= 0; i-- { 215 opp := len(parameters) - 1 - i 216 parameters[i], parameters[opp] = parameters[opp], parameters[i] 217 } 218 219 return strings.Join(segments, ""), parameters 220 }