gin-gonic / contrib

Collection of middlewares created by the community
https://gin-gonic.github.io/gin/
MIT License
2.03k stars 276 forks source link

Catch all route with static middleware #90

Closed eduardonunesp closed 5 years ago

eduardonunesp commented 8 years ago

I have an Angular app to serve using Static middleware, and AngularJS is a SPA framework, means it needs to catch all kinds of routes. My question is: can I do something like:

r.Use(static.Serve("/*", static.LocalFile("client/build", true)))
javierprovecho commented 8 years ago

@eduardonunesp I don't get what you are trying to ask. Angular uses #/path1/path2 as routing. That is not specified by the server or in this case, gin. You just need to serve a folder (or root path) as static, and let the angular magic do the rest.

eduardonunesp commented 8 years ago

@javierprovecho the idea is when you use Angular in HTML5 mode, the app don't use the # just act like non-SPA. For instance when I was using NGINX it needed to config a sort of catch all URL.

To help clarify the idea, please take a look in this article http://www.codelord.net/2015/05/12/angularjs-how-to-setup-pushstate-with-html5mode/

djensen47 commented 7 years ago

The SPA server is a common pattern for tidy URLs. Angular, React, Aurelia, these all can use # or they can use "pretty" urls without the #, it's up to the server to return the index.html no matter what url is requested.

In node.js I have handled this by catching a 404 and returning the index.html

I'm using a similar strategy using gin:

    router.NoRoute(func(c *gin.Context) {
        c.File("./public/index.html")
    })

The actual implementation will run a regex on the Request.URL.Path and only return index.html when the regex is true. So if you ask for /unknown-image.png you'll still get a 404.

dreambo8563 commented 6 years ago

I also plan to server SPA with gin,

@djensen47 ,base on your comments, I have the code

    router.NoRoute(func(c *gin.Context) {
        dir, file := path.Split(c.Request.RequestURI)
        ext := filepath.Ext(file)
        if file == "" || ext == "" {
            c.File("./public/index.html")
        } else {
            // strings.Split(file, "?")
            c.File("./public" + path.Join(dir, file))
        }

    })

but when after adding service workers... I will get lot of error here. I thought a better SPA support for gin depends on the issue of httprouter.

and there will be lots of scenario to handle like: https://www.dreambo8563.tech/manifest.28f941dd3f089e9cfec1.js?aa (path with query) https://github.com/julienschmidt/httprouter/issues/73

djensen47 commented 6 years ago

I ended up doing this in labstack/echo instead. Echo actually has the plugins required to make this work seemlessly. We also open sourced the project and call it SPArge: https://github.com/BrewdHQ/sparge

dreambo8563 commented 6 years ago

@djensen47 yes, echo can support scenario well

appleboy commented 5 years ago

See the source code from go-ggz/ggz

https://github.com/go-ggz/ggz/blob/8e98db8d743a66bf2f3ea8dbb8c48686abc150a5/router/router.go#L79-L80

Ayanrocks commented 4 years ago

Any solution for this to serve spa with frontend routing?

Sudhanshu-bh commented 3 years ago

You can handle all the routes by including a separate line for each one of them. The below example handles 3 routes, that are, '/' '/login' and '/signup'. I am using reactjs, but i think (not sure!) this will work for other libraries/frameworks (like angular) as well. (The 'build' folder is obtained by the command 'npm run build' in react.)

router := gin.Default()

router.Use(static.Serve("/", static.LocalFile("./build", true)))
router.Use(static.Serve("/login", static.LocalFile("./build", true)))
router.Use(static.Serve("/signup", static.LocalFile("./build", true)))

router.NoRoute(func(c *gin.Context) {
    c.JSON(404, gin.H{
        "code": "PAGE_NOT_FOUND", "message": "Page not found", // or use c.File("./public/index.html")
    })
})
amboowang commented 2 years ago

so what is the final solution?

betovieirasilva commented 2 years ago

so what is the final solution?

@amboowang a simple solution is

    //configuration to SAP applications
    router.Use(static.Serve("/", static.LocalFile("./assets/build", true)))
    router.NoRoute(func(c *gin.Context) {
        if !strings.HasPrefix(c.Request.RequestURI, "/api") {
            c.File("./assets/build/index.html")
        }
        //default 404 page not found
    })
ehaughee commented 2 years ago

What is the difference between using the contrib static module and the built in router.Static? Is the contrib module required to do a root "/" static route? I get an error when trying to do so with r.Static if I have any other routes.

darul75 commented 1 year ago

Just to add to that solution which worked for me as well, in the served html file I had a reference to a js file of type

<script src="./myfile.js"></script>

that was causing me trouble while resolved with a path like /path/secondpath and looking ok with just /path

and I had completely forgotten about this head option

<base href="/">

https://stackoverflow.com/a/46273294/2205050