diff --git a/context/context.go b/context/context.go index 51fbc1a9..1524c6bb 100644 --- a/context/context.go +++ b/context/context.go @@ -100,6 +100,7 @@ type ( // SetHeader sets the response headers first parameter is the key, second is the value SetHeader(string, string) Redirect(string, ...int) + RedirectTo(routeName string, args ...interface{}) // Errors NotFound() Panic() diff --git a/context_response.go b/context_response.go index 0887a124..59c9a09f 100644 --- a/context_response.go +++ b/context_response.go @@ -13,9 +13,9 @@ func (ctx *Context) SetHeader(k string, v string) { // Redirect redirect sends a redirect response the client // accepts 2 parameters string and an optional int // first parameter is the url to redirect -// second parameter is the http status should send, default is 302 (Temporary redirect), you can set it to 301 (Permant redirect), if that's nessecery +// second parameter is the http status should send, default is 307 (Temporary redirect), you can set it to 301 (Permant redirect), if that's nessecery func (ctx *Context) Redirect(urlToRedirect string, statusHeader ...int) { - httpStatus := 302 // temporary redirect + httpStatus := StatusTemporaryRedirect // temporary redirect if statusHeader != nil && len(statusHeader) > 0 && statusHeader[0] > 0 { httpStatus = statusHeader[0] } @@ -24,6 +24,15 @@ func (ctx *Context) Redirect(urlToRedirect string, statusHeader ...int) { ctx.StopExecution() } +// RedirectTo does the same thing as Redirect but instead of receiving a uri or path it receives a route name +func (ctx *Context) RedirectTo(routeName string, args ...interface{}) { + s, ok := ctx.station.RouteByName(routeName).Parse(args...) + if ok { + ctx.Redirect(s, StatusTemporaryRedirect) + } + +} + // Error handling // NotFound emits an error 404 to the client, using the custom http errors diff --git a/iris.go b/iris.go index 7333b844..aee43b83 100644 --- a/iris.go +++ b/iris.go @@ -247,6 +247,9 @@ func (s *Iris) PreListen(opt config.Server) *server.Server { func (s *Iris) PostListen() { //if not error opening the server, then: + // prepare the route actions, these actions needs real server's access because that it's after server's listen + s.router.optimizeLookups() + //set the rest (for Data, Text, JSON, JSONP, XML) s.rest = rest.New(s.config.Render.Rest) // set the templates diff --git a/route.go b/route.go index 8f16bca9..4951d43c 100644 --- a/route.go +++ b/route.go @@ -18,9 +18,18 @@ type ( Name(string) IRoute GetMiddleware() Middleware HasCors() bool + // internal methods + setTLS(bool) + setHost(string) + // + // used to check arguments with the route's named parameters and return the correct url // second parameter is false when the action cannot be done Parse(...interface{}) (string, bool) + + // GetURI returns the GetDomain() + Parse(...optional named parameters if route is dynamic) + // instead of Parse it just returns an empty string if path parse is failed + GetURI(...interface{}) string } // RouteNameFunc is returned to from route handle @@ -34,7 +43,10 @@ type ( // the name of the route, the default name is just the registed path. name string middleware Middleware - + // if true then https:// + isTLS bool + // the real host + host string // this is used to convert /mypath/:aparam/:something to -> /mypath/%v/%v and /mypath/* -> mypath/%v // we use %v to escape from the conversions between strings,booleans and integers. // used inside custom html template func 'url' @@ -149,6 +161,14 @@ func (r *Route) HasCors() bool { return RouteConflicts(r, "httpmethod") } +func (r *Route) setTLS(isSecure bool) { + r.isTLS = isSecure +} + +func (r *Route) setHost(s string) { + r.host = s +} + func (r *Route) Parse(args ...interface{}) (string, bool) { // check if arguments are not equal to the named parameters ( : = 1, * = all named parameters split to / ), if this happens then send not found err ///TODO: I'm thinking of making an option to disable these checks and just return a result, because they have cost when rendering an html/template, not too big compared to the render action but... we will see @@ -190,6 +210,21 @@ func (r *Route) Parse(args ...interface{}) (string, bool) { return fmt.Sprintf(r.formattedPath, args...), true } +func (r *Route) GetURI(args ...interface{}) (uri string) { + scheme := "http://" + if r.isTLS { + scheme = "https://" + } + + host := r.host + if parsedPath, ok := r.Parse(args...); ok { + uri = scheme + host + parsedPath + } + + return + +} + // RouteConflicts checks for route's middleware conflicts func RouteConflicts(r *Route, with string) bool { for _, h := range r.middleware { diff --git a/router.go b/router.go index 52929aa0..058e9d56 100644 --- a/router.go +++ b/router.go @@ -215,6 +215,23 @@ func (r *router) optimize() { r.optimized = true } +// optimizeLookups runs AFTER server's listen +func (r *router) optimizeLookups() { + // set the isTLS on all routes and the correct full domain (if it's local its empty but we don't want that) ( we don't use Domain because it's used to the tree) + listeningHost := r.station.server.Listener().Addr().String() + for idx, _ := range r.lookups { + theR := r.lookups[idx] + theR.setTLS(r.station.server.IsSecure()) + if theR.GetDomain() == "" { // means local, no subdomain + theR.setHost(listeningHost) + } else { + // it's a subdomain route + theR.setHost(theR.GetDomain() + "." + listeningHost) + } + + } +} + // notFound internal method, it justs takes the context from pool ( in order to have the custom errors available) and procedure a Not Found 404 error // this is being called when no route was found used on the ServeRequest. func (r *router) notFound(reqCtx *fasthttp.RequestCtx) {