Comments (36)
👍 thank you for making this so easy.
For anyone else that wants to set up a static server and api on the same url here is what I ended up with:
r := gin.Default()
r.Use(static.Serve("/", static.LocalFile("./public", true)))
api := r.Group("/api")
{
api.GET("/events", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "pong",
})
})
}
r.Run()
// file system
public/
index.html
css.css
main.go
from gin.
I'm using this, working great! Please note that you'll have to import github.com/gin-gonic/contrib/static
r := gin.Default()
r.Use(static.Serve("/", static.LocalFile("html", false)))
from gin.
This drove me crazy. For anyone else who comes here seeking the same answer: you can use the built-in router.Static()
method (not the contrib package), but the argument for the root path of the static directory needs to be either an absolute path, or a relative path that's relative to the current working directory, not the binary and not the Go source files. Drove me nuts.
from gin.
If you just want to send a HTML file when NoRoute. (It's normally in SPA)
You don't need the static package.
For example, you want to send ${workspaceRoot}/static/index.html for default file.
router.NoRoute(func(c *gin.Context) {
c.File("workspaceRoot/static/index.html")
})
May be you're looking for it?
from gin.
Absolute path:
router.StaticFile("/", "/home/terry/project/src/static/index.html")
Relative path:
router.StaticFile("/", "./src/static/index.html")
In the example above, the relative path depends on your current working directory being /home/terry/project
, even though your compiled executable may exist in /home/terry/project/bin
, so you'd have to execute the following:
$ cd /home/terry/project
$ ./bin/my-server
from gin.
I just created an static middleware:
https://github.com/gin-gonic/contrib/tree/master/static
Use it like martini:
r := gin.Default()
r.Use(static.Serve("/public")) // static files have higher priority over dynamic routes
r.NotFound(static.Serve("/public")) // when no route is found, serving static files is tried.
from gin.
thank you for making this so easy.
For anyone else that wants to set up a static server and api on the same url here is what I ended up with:
r := gin.Default() r.Use(static.Serve("/", static.LocalFile("./public", true))) api := r.Group("/api") { api.GET("/events", func(c *gin.Context) { c.JSON(200, gin.H{ "message": "pong", }) }) } r.Run()// file system public/ index.html css.css main.go
But the above codes will not work when you want to package all static files to a single binary file:
https://github.com/gobuffalo/packr/tree/master/v2#usage-with-http
The github.com/gin-gonic/contrib/static
model does not provide such a function for taking HTTP FileSystem
parameter.
from gin.
Do not worry about using NotFound(). It is the most effective way.
Martini uses the same approach, using Use() in martini is not the most effective way, because for every single request martini have to check if the file exist! I guess you agree that it doesn't make sense! right?
The flow is:
Using Use() in martini and Gin (and probably in express) as well:
----REQUEST----> (check file for path) ---- exist ---> serve content
--- no exist ---> your normal handler
using NotFound():
----REQUEST----> your normal handler
----> not handler ---> check file and serve content
from gin.
thank you for making this so easy.
For anyone else that wants to set up a static server and api on the same url here is what I ended up with:r := gin.Default() r.Use(static.Serve("/", static.LocalFile("./public", true))) api := r.Group("/api") { api.GET("/events", func(c *gin.Context) { c.JSON(200, gin.H{ "message": "pong", }) }) } r.Run()// file system public/ index.html css.css main.go
But the above codes will not work when you want to package all static files to a single binary file:
https://github.com/gobuffalo/packr/tree/master/v2#usage-with-httpThe
github.com/gin-gonic/contrib/static
model does not provide such a function for takingHTTP FileSystem
parameter.
For packr2, it can be fixed with the following example:
r := gin.Default()
myFiles := packr.New("my files", "./www")
w.Use(StaticServe("/", myFiles))
api := r.Group("/api")
{
api.GET("/ping", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "pong",
})
})
}
r.Run()
middleware:
// StaticServe serves the data
func StaticServe(urlPrefix string, fs *packr.Box) gin.HandlerFunc {
fileserver := http.FileServer(fs)
if urlPrefix != "" {
fileserver = http.StripPrefix(urlPrefix, fileserver)
}
return func(c *gin.Context) {
if fs.Has(c.Request.URL.Path) {
fileserver.ServeHTTP(c.Writer, c.Request)
c.Abort()
}
}
}
EDIT: Forgot to mention that this does not work for folders, as in navigating to "/" will not return the index.html file, so in order to fix this:
- We add the add the local 'exists' function
func exists(fs *packr.Box, prefix string, filepath string) bool {
if p := strings.TrimPrefix(filepath, prefix); len(p) < len(filepath) {
name := path.Join("/", p)
if fs.HasDir(name) {
index := path.Join(name, "index.html")
if !fs.Has(index) {
return false
}
} else if !fs.Has(name) {
return false
}
return true
}
return false
}
- Then we replace in the middleware function:
if fs.Has(c.Request.URL.Path) {
with
if exists(fs, urlPrefix, c.Request.URL.Path) {
from gin.
Can confirm what @rickyseezy suggested works. My use case is serving the swagger ui along with my APIs. The blog post combined with the above comment helped.
from gin.
Of course this is possible in Gin. Both martini and express are doing it using middlewares.
- The built-in Static() method in Gin is useful when using groups, for example you can protect the static/ folder with basic HTTP authorization.
We could create a Static middleware. I am working on that
from gin.
Hi @manucorporat. It looks nice. I had found similar solutions using the NotFound handler too, but it's nice to see that this would be a handler coming directly out of the contrib repository. Personally I would prefer if this didn't have to be done through a NotFound handler but I understand how httprouter prevents that, it's OK :)
I will try your code, thanks a lot for the help!
from gin.
Hehe yeah, I'm not a big fan of Martini anyway.
from gin.
Again, thanks for this!
from gin.
I am going to rename NotFound()
to NoRoute()
. Just to make it clear that that handlers are not related with 404. 404 is up to you ;)
from gin.
Just a thought: it would be nice to be handle pass handlers for specific error codes, so you can provide custom 404, 500, etc. handlers for your API.
from gin.
You do not need that! You can create a middleware like this:
func Handle500(c *gin.Context) {
c.Next()
if c.Writer.Status() == 500 {
// do something
}
NoRoute() exists because it's the only way to catch a non-handled route!
like for example, for /css/image.png
there is not a defined route, so: NoRoute() is called instead!
Then you can server the image, and it would not be a 404 at all.
from gin.
If I want add more static directories, how to ?
from gin.
can you provide any example , bcz i am not getting how to setup it for all my css , js , images , html files.
from gin.
👍 i'd love an example too.
from gin.
I am also having some issue getting static html with css,js files,If possible please provide example.
from gin.
Perfect example @rogierlommers! Thanks!
from gin.
This would be awesome if in the readme. Glad I took a look here before giving up. Thanks for the middleware!
from gin.
Have created pull request to update readme: #221
from gin.
@rogierlommers perfect! Much appreciated. 😄
from gin.
I am having same issue
I have public directory in which all html, css, js reside.
Dir structure
project folder
|
-- public
|
---- index.html
main.go
Following code don't serve anything (404 error)
router := gin.Default()
router.Use(static.ServeRoot("/","public"))
router.Run(":8080")
url : http://localhost:8080 -> 404 error
url : http://localhost:8080/index.html -> 404 error
Can you please help me..
from gin.
@winduptoy can you post some code of how you used it.
from gin.
thanks @winduptoy
from gin.
thankyou~
from gin.
@manucorporat I'm looking at the static contrib library (https://github.com/gin-contrib/static) and wondering why it doesn't use NoRoute() instead of Use() since as you said above, NoRoute is the better option for this.
from gin.
import "github.com/gin-gonic/contrib/static"
...
r := gin.Default()
r.Use(static.Serve("/", static.LocalFile("/view", false)))
this is throwing the following error
cannot use static.Serve("/", static.LocalFile("/view", true)) (value of type gin.HandlerFunc) as gin.HandlerFunc value in argument to r.Use
Any idea?
from gin.
import "github.com/gin-gonic/contrib/static" ... r := gin.Default() r.Use(static.Serve("/", static.LocalFile("/view", false)))
this is throwing the following error
cannot use static.Serve("/", static.LocalFile("/view", true)) (value of type gin.HandlerFunc) as gin.HandlerFunc value in argument to r.Use
Any idea?
Fixed the error. Turns out I was using glide so there was a dependency type mismatch
from gin.
g.NoRoute(Middlewares.ReturnPublic())
middleware.go
// ReturnPublic send fontend to client when method == GET
func ReturnPublic() gin.HandlerFunc {
return func(context *gin.Context) {
method := context.Request.Method
if method == "GET" {
context.File("./public")
} else {
context.Next()
}
}
}
this works well
from gin.
My route is : /:itemid
then I want to serve my index
and faq
page on /index.html
/faq.html
which my static files are located at /static
folder?
How should I do?
from gin.
For those who are looking for an updated answer
r.StaticFS("/public", http.Dir("uploads")) r.Run(":8080")
from gin.
I made a simple update of the community's static middleware. If anyone has similar needs, maybe you can use it.
Supports both local files and Go Embed (you can create a single executable file without having to deal with file packaging)
project: https://github.com/soulteary/gin-static
download the middleware:
go get github.com/soulteary/gin-static
use local files:
package main
import (
["log"](https://pkg.go.dev/log)
static "github.com/soulteary/gin-static"
https://github.com/gin-gonic/gin
)
func main() {
r := gin.Default()
// if Allow DirectoryIndex
// r.Use(static.Serve("/", static.LocalFile("./public", true)))
// set prefix
// r.Use(static.Serve("/static", static.LocalFile("./public", true)))
r.Use(static.Serve("/", static.LocalFile("./public", false)))
r.GET("/ping", func(c *gin.Context) {
c.String(200, "test")
})
// Listen and Server in 0.0.0.0:8080
if err := r.Run(":8080"); err != nil {
log.Fatal(err)
}
}
use embed files:
package main
import (
["embed"](https://pkg.go.dev/embed)
["fmt"](https://pkg.go.dev/fmt)
["net/http"](https://pkg.go.dev/net/http)
https://github.com/gin-gonic/gin
)
//go:embed public
var EmbedFS embed.FS
func main() {
r := gin.Default()
// method 1: use as Gin Router
// trim embedfs path `public/page`, and use it as url path `/`
// r.GET("/", static.ServeEmbed("public/page", EmbedFS))
// method 2: use as middleware
// trim embedfs path `public/page`, the embedfs path start with `/`
// r.Use(static.ServeEmbed("public/page", EmbedFS))
// method 2.1: use as middleware
// trim embedfs path `public/page`, the embedfs path start with `/public/page`
// r.Use(static.ServeEmbed("", EmbedFS))
// method 3: use as manual
// trim embedfs path `public/page`, the embedfs path start with `/public/page`
// staticFiles, err := static.EmbedFolder(EmbedFS, "public/page")
// if err != nil {
// log.Fatalln("initialization of embed folder failed:", err)
// } else {
// r.Use(static.Serve("/", staticFiles))
// }
r.GET("/ping", func(c *gin.Context) {
c.String(200, "test")
})
r.NoRoute(func(c *gin.Context) {
fmt.Printf("%s doesn't exists, redirect on /\n", c.Request.URL.Path)
c.Redirect(http.StatusMovedPermanently, "/")
})
// Listen and Server in 0.0.0.0:8080
r.Run(":8080")
}
or both use local and embed file, and as those files as fallback (you can overwrite the routes):
if debugMode {
r.Use(static.Serve("/", static.LocalFile("public", false)))
} else {
r.NoRoute(
// your code ...
func(c *gin.Context) {
if c.Request.URL.Path == "/somewhere/" {
c.Data(http.StatusOK, "text/html; charset=utf-8", []byte("custom as you like"))
c.Abort()
}
},
static.ServeEmbed("public", EmbedFS),
)
// no need to block some request path before request static files
// r.NoRoute(static.ServeEmbed("public", EmbedFS))
}
from gin.
Related Issues (20)
- Gin do not support javascript ? HOT 5
- Does gin.Recovery() run before or after Group middleware? HOT 1
- How can i prevent a http connection to close HOT 3
- BasicAuth is not suitable for proxy auth
- Can't get status as expected from context.Status() HOT 1
- Gin Context implementation breaks context.Context contract HOT 4
- [Suggestions] Introducing an AI-powered robot to assist with daily issue and pull request (PR) tasks.
- Need c.JSON() response like behaviour for c.SSEvent() response for supporting first party clients like OpenAI SDK HOT 2
- custom time.Time type can not use bindQuery get a value. HOT 3
- configure server parameters HOT 1
- The binding:"required" tag does not seem to work for struct types HOT 3
- Is there a way to preserve order in JSON on response? HOT 1
- redirect is not success HOT 1
- I want to use shouldmindbodywith first and then use formafile, but it doesn't work right now HOT 3
- Extend the routing tree entry
- Transparent decompression for gzip, deflate, etc? HOT 1
- Custom time.Time type can not use bindQuery get a value 2 HOT 2
- http: response.Write on hijacked connection from github.com/gin-gonic/gin.(*responseWriter).Write (response_writer.go:83) HOT 1
- Suggestion: Move Print Debug to Run() functions? HOT 7
- AbortWithStatusJSON doesn't abort context HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from gin.