I'm setting up an SPA application using Vue Router with the history mode, so I'd like to redirect all non-static-file requests to the static index.html file served by app.HandleDir("/", "./frontend/dist").
I've tried the following:
app.Get("/*", func(ctx iris.Context) {
ctx.View("index.html")
})
But this overrides the static file routes and other API routes, so what's the way to handle all other routes?
Is there a concept of route priorities? If so, we would just register that with a lower priority.
app.Any("/{asdf:path}", func(ctx iris.Context) {
ctx.View("index.html")
})
app.HandleDir("/", iris.Dir(Conf.Dir.Assets), iris.DirOptions{
ShowList: false,
})
Doesn't this pattern work for you?
https://github.com/kataras/iris/blob/master/_examples/routing/dynamic-path/root-wildcard/main.go#L21
@pipe1 wildcard does not override static routes or other parameterized routes in Iris (Iris has the most powerful Routing in Go language), the problem in your code is that you use the /* which is not an Iris pattern, you should use the {:path} parameter type instead, e.g. app.Get("/{p:path}", handler), to get the "p" use: ctx.Params().Get("p"). As @AlbinoGeek correctly noted, there is an example which covers exactly cases like vue router and SPA.
That sounds like it'd work, however when I do this
app.HandleDir("/", "./frontend/dist")
app.Get("/{p:path}", func(ctx iris.Context) {
log.Infof(ctx.Path())
ctx.View("index.html")
})
All requests get handled by the app.Get handler, and thus no assets are loaded. When I reverse the order of those two handlers, no routes are handled by my handler.
Hello @pipe1,
The app.HandleDir registers a app.Get and app.Head of "/{p:path}" under the hoods, this is why you can't register both. You can always use the app.SetRegisterRule(iris.RouteOverlap) which will make both of the routes to be registered and if the first fails then it fallbacks to the second one. However the best option for that type of cases, if you need all routing to be handled by your front-end, including the HTTP Errors is the following:
func main() {
app := iris.New()
app.RegisterView(iris.HTML("./views", ".html"))
app.OnAnyErrorCode(index)
app.HandleDir("/", "./frontend/dist")
app.Get("/", index)
app.Listen(":8080")
}
func index(ctx iris.Context) {
ctx.View("index.html") // or ctx.ServeFile("./frontned/index.html")
}
The file-server/single-page-application example may need an update to include a real-world Vue JS Router too. I am on this.
That works, thank you!
Hi @kataras, I would like to ask what if our SPA works under different router party (subdomain)? What is the best way to make it work? I solved it some how but I'm not sure it's best way to do it or not. For example my app has couple of different subdomains and one of them (app.) runs only SPA. Here is my code to solve it;
matchCommonAssets := regexp.MustCompile(`.*\..+$`)
app.RegisterView(iris.HTML("./public/app", ".html"))
moduleParty := app.Party("app.")
moduleParty.Use(middlewares.CheckTokenValidation)
moduleParty.Get("/", func(ctx iris.Context) {
ctx.View("index.html")
})
moduleParty.Get("/{p:path}", func(ctx iris.Context) {
if matchCommonAssets.MatchString(ctx.Params().Get("p")) {
ctx.ServeFile(fmt.Sprintf("./public/app/%s", ctx.Params().Get("p")), false)
return
}
ctx.View("index.html")
})
I have to add if condition to handler to serve static files otherwise SPA not works properly (especially when you refresh page on routed url). What do you think about my approach? Is it correct way to do it or do you have any other suggestion?
@ozguncagri it should work on any type of Party, including Subdomains. Did you test it? It seems to work here.
@kataras parties doesn't have OnAnyErrorCode function so I can not limit error code control inside of my party. Because it's app wide function, it affects all my parties and this is not the behavior that I want to use on my app. Is there any way to manage error code control on specific party?
Edit: I think I can use this kind of approach on my app wide error control on different parties;
app.OnAnyErrorCode(
func(ctx iris.Context) {
switch ctx.Subdomain() {
case "api":
// manage error
case "app":
// manage error
case "auth":
// manage error
default:
// manage error
}
},
)
@ozguncagri that's fine too but if you use the @master version the OnAnyErrorCode/OnErrorCode should work per-party (and per subdomain). Actually I've just tested it and it seem to work, here is an example:
package main
import "github.com/kataras/iris/v12"
func main() {
app := iris.New()
apiSD := app.Subdomain("api")
apiSD.OnErrorCode(iris.StatusNotFound, func(ctx iris.Context) {
ctx.Writef("Not found from api: %s", ctx.Subdomain())
})
apiSD.OnErrorCode(iris.StatusBadRequest, func(ctx iris.Context) {
ctx.Writef("BadRequest from api: %s", ctx.Subdomain())
})
apiSD.Get("/", fireBadRequest)
appSD := app.Subdomain("app")
appSD.OnErrorCode(iris.StatusNotFound, func(ctx iris.Context) {
ctx.Writef("Not found from app: %s", ctx.Subdomain())
})
appSD.OnErrorCode(iris.StatusBadRequest, func(ctx iris.Context) {
ctx.Writef("BadRequest from app: %s", ctx.Subdomain())
})
appSD.Get("/", fireBadRequest)
authSD := app.Subdomain("auth")
authSD.OnErrorCode(iris.StatusNotFound, func(ctx iris.Context) {
ctx.Writef("Not found from auth: %s", ctx.Subdomain())
})
authSD.OnErrorCode(iris.StatusBadRequest, func(ctx iris.Context) {
ctx.Writef("BadRequest from auth: %s", ctx.Subdomain())
})
authSD.Get("/", fireBadRequest)
// http://api.testdomain.com
// http://api.testdomain.com/dsa42
// http://app.testdomain.com
// http://app.testdomain.com/dsa42
// http://auth.testdomain.com
// http://auth.testdomain.com/dsa42
app.Listen(":80")
}
func fireBadRequest(ctx iris.Context) {
ctx.StopWithStatus(iris.StatusBadRequest)
}
/*
127.0.0.1 api.testdomain.com
127.0.0.1 app.testdomain.com
127.0.0.1 auth.testdomain.com
*/
@kataras oh; thanks for noticing me to upgrade iris package. I just upgraded dependency and everything looks fine for now. And thanks for example btw.
You are welcome @ozguncagri, we have a cool gif animation on README on how to update and use Iris too 馃憤馃徑
@pipe01, @ozguncagri The iris.DirOptions struct (optional 3rd parameter of the HandleDir method) now contains a SPA bool field. Make sure you update to @master.
Example Code:
app.HandleDir("/", iris.Dir("./public"), iris.DirOptions{
IndexName: "index.html",
SPA: true,
})
That's fantastic, thank you!