mirror of
https://github.com/kataras/iris.git
synced 2025-02-02 15:30:36 +01:00
update the subdomain redirect example using the rewrite middleware
This commit is contained in:
parent
5e82fa5b89
commit
1780d97d44
|
@ -1,73 +1,59 @@
|
||||||
// Package main shows how to register a simple 'www' subdomain,
|
|
||||||
// using the `app.WWW` method, which will register a router wrapper which will
|
|
||||||
// redirect all 'mydomain.com' requests to 'www.mydomain.com'.
|
|
||||||
// Check the 'hosts' file to see how to test the 'mydomain.com' on your local machine.
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import "github.com/kataras/iris/v12"
|
import (
|
||||||
|
"github.com/kataras/iris/v12"
|
||||||
const addr = "mydomain.com:80"
|
"github.com/kataras/iris/v12/middleware/rewrite"
|
||||||
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
app := newApp()
|
app := newApp()
|
||||||
|
|
||||||
// http(s)://mydomain.com, will be redirect to http(s)://www.mydomain.com.
|
|
||||||
// The `www` variable is the `app.Subdomain("www")`.
|
|
||||||
//
|
|
||||||
// app.WWW() wraps the router so it can redirect all incoming requests
|
|
||||||
// that comes from 'http(s)://mydomain.com/%path%' (www is missing)
|
|
||||||
// to `http(s)://www.mydomain.com/%path%`.
|
|
||||||
//
|
|
||||||
// Try:
|
|
||||||
// http://mydomain.com -> http://www.mydomain.com
|
// http://mydomain.com -> http://www.mydomain.com
|
||||||
// http://mydomain.com/users -> http://www.mydomain.com/users
|
// http://mydomain.com/user -> http://www.mydomain.com/user
|
||||||
// http://mydomain.com/users/login -> http://www.mydomain.com/users/login
|
// http://mydomain.com/user/login -> http://www.mydomain.com/user/login
|
||||||
app.Listen(addr)
|
app.Listen(":80")
|
||||||
}
|
}
|
||||||
|
|
||||||
func newApp() *iris.Application {
|
func newApp() *iris.Application {
|
||||||
app := iris.New()
|
app := iris.New()
|
||||||
app.Get("/", func(ctx iris.Context) {
|
app.Logger().SetLevel("debug")
|
||||||
ctx.Writef("This will never be executed.")
|
|
||||||
})
|
|
||||||
|
|
||||||
www := app.Subdomain("www") // <- same as app.Party("www.")
|
static := app.Subdomain("static")
|
||||||
www.Get("/", index)
|
static.Get("/", staticIndex)
|
||||||
|
|
||||||
// www is an `iris.Party`, use it like you already know, like grouping routes.
|
app.Get("/", index)
|
||||||
www.PartyFunc("/users", func(p iris.Party) { // <- same as www.Party("/users").Get(...)
|
userRouter := app.Party("/user")
|
||||||
p.Get("/", usersIndex)
|
userRouter.Get("/", userGet)
|
||||||
p.Get("/login", getLogin)
|
userRouter.Get("/login", userGetLogin)
|
||||||
})
|
|
||||||
|
|
||||||
// redirects mydomain.com/%anypath% to www.mydomain.com/%anypath%.
|
// redirects := rewrite.Load("redirects.yml")
|
||||||
// First argument is the 'from' and second is the 'to/target'.
|
// ^ see _examples/routing/rewrite example for that.
|
||||||
app.SubdomainRedirect(app, www)
|
|
||||||
|
|
||||||
// SubdomainRedirect works for multi-level subdomains as well:
|
|
||||||
// subsub := www.Subdomain("subsub") // subsub.www.mydomain.com
|
|
||||||
// subsub.Get("/", func(ctx iris.Context) { ctx.Writef("subdomain is: " + ctx.Subdomain()) })
|
|
||||||
// app.SubdomainRedirect(subsub, www)
|
|
||||||
//
|
//
|
||||||
// If you need to redirect any subdomain to 'www' then:
|
// Now let's do that by code.
|
||||||
// app.SubdomainRedirect(app.WildcardSubdomain(), www)
|
rewriteEngine, _ := rewrite.New(rewrite.Options{
|
||||||
// If you need to redirect from a subdomain to the root domain then:
|
PrimarySubdomain: "www",
|
||||||
// app.SubdomainRedirect(app.Subdomain("mysubdomain"), app)
|
})
|
||||||
//
|
// Enable this line for debugging:
|
||||||
// Note that app.Party("mysubdomain.") and app.Subdomain("mysubdomain")
|
// rewriteEngine.SetLogger(app.Logger())
|
||||||
// is the same exactly thing, the difference is that the second can omit the last dot('.').
|
app.WrapRouter(rewriteEngine.Rewrite)
|
||||||
|
|
||||||
return app
|
return app
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func staticIndex(ctx iris.Context) {
|
||||||
|
ctx.Writef("This is the static.mydomain.com index.")
|
||||||
|
}
|
||||||
|
|
||||||
func index(ctx iris.Context) {
|
func index(ctx iris.Context) {
|
||||||
ctx.Writef("This is the www.mydomain.com endpoint.")
|
ctx.Writef("This is the www.mydomain.com index.")
|
||||||
}
|
}
|
||||||
|
|
||||||
func usersIndex(ctx iris.Context) {
|
func userGet(ctx iris.Context) {
|
||||||
ctx.Writef("This is the www.mydomain.com/users endpoint.")
|
// Also, ctx.Subdomain(), ctx.SubdomainFull(), ctx.Host() and ctx.Path()
|
||||||
|
// can be helpful when working with subdomains.
|
||||||
|
ctx.Writef("This is the www.mydomain.com/user endpoint.")
|
||||||
}
|
}
|
||||||
|
|
||||||
func getLogin(ctx iris.Context) {
|
func userGetLogin(ctx iris.Context) {
|
||||||
ctx.Writef("This is the www.mydomain.com/users/login endpoint.")
|
ctx.Writef("This is the www.mydomain.com/user/login endpoint.")
|
||||||
}
|
}
|
||||||
|
|
|
@ -980,7 +980,7 @@ func (api *APIBuilder) UseOnce(handlers ...context.Handler) {
|
||||||
|
|
||||||
// UseGlobal registers handlers that should run at the very beginning.
|
// UseGlobal registers handlers that should run at the very beginning.
|
||||||
// It prepends those handler(s) to all routes,
|
// It prepends those handler(s) to all routes,
|
||||||
// including all parties, subdomains.
|
// including all parties, subdomains and errors.
|
||||||
// It doesn't care about call order, it will prepend the handlers to all
|
// It doesn't care about call order, it will prepend the handlers to all
|
||||||
// existing routes and the future routes that may being registered.
|
// existing routes and the future routes that may being registered.
|
||||||
//
|
//
|
||||||
|
|
|
@ -8,6 +8,7 @@ package router_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/http"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/kataras/iris/v12"
|
"github.com/kataras/iris/v12"
|
||||||
|
@ -285,3 +286,72 @@ func TestUseRouterSubdomains(t *testing.T) {
|
||||||
e.GET("/notfound").WithURL("http://old.example.com").Expect().Status(iris.StatusNotFound).Body().
|
e.GET("/notfound").WithURL("http://old.example.com").Expect().Status(iris.StatusNotFound).Body().
|
||||||
Equal("Not Found")
|
Equal("Not Found")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestUseWrapOrder(t *testing.T) {
|
||||||
|
var (
|
||||||
|
expectedBody = "#1 .WrapRouter\n#2 .UseRouter\n#3 .UseGlobal\n#4 .Use\n#5 Main Handler\n"
|
||||||
|
expectedNotFoundBody = "#3 .UseGlobal\n#1 .UseError\n#2 Main Error Handler\n"
|
||||||
|
makeMiddleware = func(body string) iris.Handler {
|
||||||
|
return func(ctx iris.Context) {
|
||||||
|
ctx.WriteString(body)
|
||||||
|
ctx.Next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handler = func(ctx iris.Context) {
|
||||||
|
ctx.WriteString("#5 Main Handler\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
errorHandler = func(ctx iris.Context) {
|
||||||
|
ctx.WriteString("#2 Main Error Handler\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
useHandler = makeMiddleware("#4 .Use\n")
|
||||||
|
useGlobal = makeMiddleware("#3 .UseGlobal\n")
|
||||||
|
useError = func(ctx iris.Context) {
|
||||||
|
// UseError has captured the status code, because it runs
|
||||||
|
// after the router itself but only one error handlers.
|
||||||
|
ctx.WriteString("#1 .UseError\n")
|
||||||
|
ctx.Next()
|
||||||
|
}
|
||||||
|
useRouter = func(ctx iris.Context) {
|
||||||
|
if ctx.Path() == "/" {
|
||||||
|
ctx.WriteString("#2 .UseRouter\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Next()
|
||||||
|
}
|
||||||
|
wrapRouter = func(w http.ResponseWriter, r *http.Request, router http.HandlerFunc) {
|
||||||
|
if r.URL.Path == "/" {
|
||||||
|
w.Write([]byte("#1 .WrapRouter\n"))
|
||||||
|
// Note for beginners, reading this test:
|
||||||
|
// if we write something here on a not found page,
|
||||||
|
// in the raw net/http wrapper like this one,
|
||||||
|
// then the response writer sends 200 status OK
|
||||||
|
// (on first write) and so any error handler will not be fired as expected,
|
||||||
|
// these are basic things. If you w.WriteHeader you cannot change the status code later on too.
|
||||||
|
// In Iris handlers, if you write before status code set, then it sends 200
|
||||||
|
// and it cannot change too (if you want to change that behavior you use ctx.Record()).
|
||||||
|
// However if you
|
||||||
|
// just call ctx.StatusCode without content written then you are able to change the status code
|
||||||
|
// later on.
|
||||||
|
}
|
||||||
|
|
||||||
|
router(w, r)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
app := iris.New()
|
||||||
|
app.Use(useHandler)
|
||||||
|
app.UseGlobal(useGlobal)
|
||||||
|
app.UseError(useError)
|
||||||
|
app.UseRouter(useRouter)
|
||||||
|
app.WrapRouter(wrapRouter)
|
||||||
|
|
||||||
|
app.OnErrorCode(iris.StatusNotFound, errorHandler)
|
||||||
|
app.Get("/", handler)
|
||||||
|
|
||||||
|
e := httptest.New(t, app)
|
||||||
|
e.GET("/NotFound").Expect().Status(iris.StatusNotFound).Body().Equal(expectedNotFoundBody)
|
||||||
|
e.GET("/").Expect().Status(iris.StatusOK).Body().Equal(expectedBody)
|
||||||
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ import (
|
||||||
"github.com/kataras/iris/v12/context"
|
"github.com/kataras/iris/v12/context"
|
||||||
"github.com/kataras/iris/v12/core/router"
|
"github.com/kataras/iris/v12/core/router"
|
||||||
|
|
||||||
|
"github.com/kataras/golog"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -69,6 +70,7 @@ type Engine struct {
|
||||||
redirects []*redirectMatch
|
redirects []*redirectMatch
|
||||||
options Options
|
options Options
|
||||||
|
|
||||||
|
logger *golog.Logger
|
||||||
domainValidator func(string) bool
|
domainValidator func(string) bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,6 +119,35 @@ func New(opts Options) (*Engine, error) {
|
||||||
return e, nil
|
return e, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetLogger attachs a logger to the Rewrite Engine,
|
||||||
|
// used only for debugging.
|
||||||
|
// Defaults to nil.
|
||||||
|
func (e *Engine) SetLogger(logger *golog.Logger) *Engine {
|
||||||
|
e.logger = logger.Child(e).SetChildPrefix("rewrite")
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
// init the request logging with [DBUG].
|
||||||
|
func (e *Engine) initDebugf(format string, args ...interface{}) {
|
||||||
|
if e.logger == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
e.logger.Debugf(format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
var skipDBUGSpace = strings.Repeat(" ", 7)
|
||||||
|
|
||||||
|
// continue debugging the same request with new lines and spacing,
|
||||||
|
// easier to read.
|
||||||
|
func (e *Engine) debugf(format string, args ...interface{}) {
|
||||||
|
if e.logger == nil || e.logger.Level < golog.DebugLevel {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintf(e.logger.Printer, skipDBUGSpace+format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
// Handler is an Iris Handler that can be used as a router or party or route middleware.
|
// Handler is an Iris Handler that can be used as a router or party or route middleware.
|
||||||
// For a global alternative, if you want to wrap the entire Iris Application
|
// For a global alternative, if you want to wrap the entire Iris Application
|
||||||
// use the `Wrapper` instead.
|
// use the `Wrapper` instead.
|
||||||
|
@ -141,6 +172,8 @@ func (e *Engine) Rewrite(w http.ResponseWriter, r *http.Request, routeHandler ht
|
||||||
if primarySubdomain := e.options.PrimarySubdomain; primarySubdomain != "" {
|
if primarySubdomain := e.options.PrimarySubdomain; primarySubdomain != "" {
|
||||||
hostport := context.GetHost(r)
|
hostport := context.GetHost(r)
|
||||||
root := context.GetDomain(hostport)
|
root := context.GetDomain(hostport)
|
||||||
|
|
||||||
|
e.initDebugf("Begin request: full host: %s and root domain: %s", hostport, root)
|
||||||
// Note:
|
// Note:
|
||||||
// localhost and 127.0.0.1 are not supported for subdomain rewrite, by purpose,
|
// localhost and 127.0.0.1 are not supported for subdomain rewrite, by purpose,
|
||||||
// use a virtual host instead.
|
// use a virtual host instead.
|
||||||
|
@ -150,10 +183,14 @@ func (e *Engine) Rewrite(w http.ResponseWriter, r *http.Request, routeHandler ht
|
||||||
root += getPort(hostport)
|
root += getPort(hostport)
|
||||||
subdomain := strings.TrimSuffix(hostport, root)
|
subdomain := strings.TrimSuffix(hostport, root)
|
||||||
|
|
||||||
|
e.debugf("Domain is not a loopback, requested subdomain: %s\n", subdomain)
|
||||||
|
|
||||||
if subdomain == "" {
|
if subdomain == "" {
|
||||||
// we are in root domain, full redirect to its primary subdomain.
|
// we are in root domain, full redirect to its primary subdomain.
|
||||||
r.Host = primarySubdomain + root
|
newHost := primarySubdomain + root
|
||||||
r.URL.Host = primarySubdomain + root
|
e.debugf("Redirecting from root domain to: %s\n", newHost)
|
||||||
|
r.Host = newHost
|
||||||
|
r.URL.Host = newHost
|
||||||
http.Redirect(w, r, r.URL.String(), http.StatusMovedPermanently)
|
http.Redirect(w, r, r.URL.String(), http.StatusMovedPermanently)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -164,13 +201,15 @@ func (e *Engine) Rewrite(w http.ResponseWriter, r *http.Request, routeHandler ht
|
||||||
// to bypass the subdomain router (`routeHandler`)
|
// to bypass the subdomain router (`routeHandler`)
|
||||||
// do not return, redirects should be respected.
|
// do not return, redirects should be respected.
|
||||||
rootHost := strings.TrimPrefix(hostport, subdomain)
|
rootHost := strings.TrimPrefix(hostport, subdomain)
|
||||||
|
e.debugf("Request host field was modified to: %s. Proceed without redirection\n", rootHost)
|
||||||
// modify those for the next redirects or the route handler.
|
// modify those for the next redirects or the route handler.
|
||||||
r.Host = rootHost
|
r.Host = rootHost
|
||||||
r.URL.Host = rootHost
|
r.URL.Host = rootHost
|
||||||
}
|
}
|
||||||
|
|
||||||
// maybe other subdomain or not at all, let's continue.
|
// maybe other subdomain or not at all, let's continue.
|
||||||
|
} else {
|
||||||
|
e.debugf("Primary subdomain is: %s but redirect response was not sent. Domain is a loopback?\n", primarySubdomain)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -183,6 +222,7 @@ func (e *Engine) Rewrite(w http.ResponseWriter, r *http.Request, routeHandler ht
|
||||||
|
|
||||||
if target, ok := rd.matchAndReplace(src); ok {
|
if target, ok := rd.matchAndReplace(src); ok {
|
||||||
if target == src {
|
if target == src {
|
||||||
|
e.debugf("WARNING: source and target URLs match: %s\n", src)
|
||||||
routeHandler(w, r)
|
routeHandler(w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -194,6 +234,7 @@ func (e *Engine) Rewrite(w http.ResponseWriter, r *http.Request, routeHandler ht
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
e.debugf("No redirect: handle request: %s as: %s\n", r.RequestURI, u)
|
||||||
r.URL = u
|
r.URL = u
|
||||||
routeHandler(w, r)
|
routeHandler(w, r)
|
||||||
return
|
return
|
||||||
|
@ -202,8 +243,10 @@ func (e *Engine) Rewrite(w http.ResponseWriter, r *http.Request, routeHandler ht
|
||||||
if !rd.isRelativePattern {
|
if !rd.isRelativePattern {
|
||||||
// this performs better, no need to check query or host,
|
// this performs better, no need to check query or host,
|
||||||
// the uri already built.
|
// the uri already built.
|
||||||
|
e.debugf("Full redirect: from: %s to: %s\n", src, target)
|
||||||
redirectAbs(w, r, target, rd.code)
|
redirectAbs(w, r, target, rd.code)
|
||||||
} else {
|
} else {
|
||||||
|
e.debugf("Path redirect: from: %s to: %s\n", src, target)
|
||||||
http.Redirect(w, r, target, rd.code)
|
http.Redirect(w, r, target, rd.code)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user