mirror of
https://github.com/kataras/iris.git
synced 2025-01-23 10:41:03 +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
|
||||
|
||||
import "github.com/kataras/iris/v12"
|
||||
|
||||
const addr = "mydomain.com:80"
|
||||
import (
|
||||
"github.com/kataras/iris/v12"
|
||||
"github.com/kataras/iris/v12/middleware/rewrite"
|
||||
)
|
||||
|
||||
func main() {
|
||||
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/users -> http://www.mydomain.com/users
|
||||
// http://mydomain.com/users/login -> http://www.mydomain.com/users/login
|
||||
app.Listen(addr)
|
||||
// http://mydomain.com/user -> http://www.mydomain.com/user
|
||||
// http://mydomain.com/user/login -> http://www.mydomain.com/user/login
|
||||
app.Listen(":80")
|
||||
}
|
||||
|
||||
func newApp() *iris.Application {
|
||||
app := iris.New()
|
||||
app.Get("/", func(ctx iris.Context) {
|
||||
ctx.Writef("This will never be executed.")
|
||||
})
|
||||
app.Logger().SetLevel("debug")
|
||||
|
||||
www := app.Subdomain("www") // <- same as app.Party("www.")
|
||||
www.Get("/", index)
|
||||
static := app.Subdomain("static")
|
||||
static.Get("/", staticIndex)
|
||||
|
||||
// www is an `iris.Party`, use it like you already know, like grouping routes.
|
||||
www.PartyFunc("/users", func(p iris.Party) { // <- same as www.Party("/users").Get(...)
|
||||
p.Get("/", usersIndex)
|
||||
p.Get("/login", getLogin)
|
||||
})
|
||||
app.Get("/", index)
|
||||
userRouter := app.Party("/user")
|
||||
userRouter.Get("/", userGet)
|
||||
userRouter.Get("/login", userGetLogin)
|
||||
|
||||
// redirects mydomain.com/%anypath% to www.mydomain.com/%anypath%.
|
||||
// First argument is the 'from' and second is the 'to/target'.
|
||||
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)
|
||||
// redirects := rewrite.Load("redirects.yml")
|
||||
// ^ see _examples/routing/rewrite example for that.
|
||||
//
|
||||
// If you need to redirect any subdomain to 'www' then:
|
||||
// app.SubdomainRedirect(app.WildcardSubdomain(), www)
|
||||
// If you need to redirect from a subdomain to the root domain then:
|
||||
// app.SubdomainRedirect(app.Subdomain("mysubdomain"), app)
|
||||
//
|
||||
// Note that app.Party("mysubdomain.") and app.Subdomain("mysubdomain")
|
||||
// is the same exactly thing, the difference is that the second can omit the last dot('.').
|
||||
// Now let's do that by code.
|
||||
rewriteEngine, _ := rewrite.New(rewrite.Options{
|
||||
PrimarySubdomain: "www",
|
||||
})
|
||||
// Enable this line for debugging:
|
||||
// rewriteEngine.SetLogger(app.Logger())
|
||||
app.WrapRouter(rewriteEngine.Rewrite)
|
||||
|
||||
return app
|
||||
}
|
||||
|
||||
func staticIndex(ctx iris.Context) {
|
||||
ctx.Writef("This is the static.mydomain.com index.")
|
||||
}
|
||||
|
||||
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) {
|
||||
ctx.Writef("This is the www.mydomain.com/users endpoint.")
|
||||
func userGet(ctx iris.Context) {
|
||||
// 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) {
|
||||
ctx.Writef("This is the www.mydomain.com/users/login endpoint.")
|
||||
func userGetLogin(ctx iris.Context) {
|
||||
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.
|
||||
// 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
|
||||
// existing routes and the future routes that may being registered.
|
||||
//
|
||||
|
|
|
@ -8,6 +8,7 @@ package router_test
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"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().
|
||||
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/core/router"
|
||||
|
||||
"github.com/kataras/golog"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
|
@ -69,6 +70,7 @@ type Engine struct {
|
|||
redirects []*redirectMatch
|
||||
options Options
|
||||
|
||||
logger *golog.Logger
|
||||
domainValidator func(string) bool
|
||||
}
|
||||
|
||||
|
@ -117,6 +119,35 @@ func New(opts Options) (*Engine, error) {
|
|||
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.
|
||||
// For a global alternative, if you want to wrap the entire Iris Application
|
||||
// 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 != "" {
|
||||
hostport := context.GetHost(r)
|
||||
root := context.GetDomain(hostport)
|
||||
|
||||
e.initDebugf("Begin request: full host: %s and root domain: %s", hostport, root)
|
||||
// Note:
|
||||
// localhost and 127.0.0.1 are not supported for subdomain rewrite, by purpose,
|
||||
// use a virtual host instead.
|
||||
|
@ -150,10 +183,14 @@ func (e *Engine) Rewrite(w http.ResponseWriter, r *http.Request, routeHandler ht
|
|||
root += getPort(hostport)
|
||||
subdomain := strings.TrimSuffix(hostport, root)
|
||||
|
||||
e.debugf("Domain is not a loopback, requested subdomain: %s\n", subdomain)
|
||||
|
||||
if subdomain == "" {
|
||||
// we are in root domain, full redirect to its primary subdomain.
|
||||
r.Host = primarySubdomain + root
|
||||
r.URL.Host = primarySubdomain + root
|
||||
newHost := 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)
|
||||
return
|
||||
}
|
||||
|
@ -164,13 +201,15 @@ func (e *Engine) Rewrite(w http.ResponseWriter, r *http.Request, routeHandler ht
|
|||
// to bypass the subdomain router (`routeHandler`)
|
||||
// do not return, redirects should be respected.
|
||||
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.
|
||||
r.Host = rootHost
|
||||
r.URL.Host = rootHost
|
||||
}
|
||||
|
||||
// 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 == src {
|
||||
e.debugf("WARNING: source and target URLs match: %s\n", src)
|
||||
routeHandler(w, r)
|
||||
return
|
||||
}
|
||||
|
@ -194,6 +234,7 @@ func (e *Engine) Rewrite(w http.ResponseWriter, r *http.Request, routeHandler ht
|
|||
return
|
||||
}
|
||||
|
||||
e.debugf("No redirect: handle request: %s as: %s\n", r.RequestURI, u)
|
||||
r.URL = u
|
||||
routeHandler(w, r)
|
||||
return
|
||||
|
@ -202,8 +243,10 @@ func (e *Engine) Rewrite(w http.ResponseWriter, r *http.Request, routeHandler ht
|
|||
if !rd.isRelativePattern {
|
||||
// this performs better, no need to check query or host,
|
||||
// the uri already built.
|
||||
e.debugf("Full redirect: from: %s to: %s\n", src, target)
|
||||
redirectAbs(w, r, target, rd.code)
|
||||
} else {
|
||||
e.debugf("Path redirect: from: %s to: %s\n", src, target)
|
||||
http.Redirect(w, r, target, rd.code)
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user