The code first.
package main
import (
"log"
"io/fs"
"embed"
"net/http"
"github.com/gorilla/mux"
)
//go:embed static/*
var static embed.FS
func main() {
router := mux.NewRouter()
fSys, err := fs.Sub(static, "static")
if err != nil {
panic(err)
}
staticServer := http.FileServer(http.FS(fSys))
router.Handle("/", staticServer)
if err := http.ListenAndServe(":3001", router); err != nil {
log.Fatalf("server could not run")
}
}
So I have this application and two others app2, app3 on ports 3002 and 3003 accordingly.
I need to proxy those apps using apache2 server.
Urls should be http:///app1 http:///app2 http:///app3
If I write:
ProxyPass /app1 http://localhost:3001/
ProxyPassReverse /app1 http://localhost:3001/
so index.html file loads fine but css style not loaded.
html and css files are on the same "virtual" disk. How can I fix css loading?
├── go.mod
├── go.sum
├── main.go
└── static
├── index.html
└── style.css
index.html looks like
<link rel="stylesheet" href="./style.css">
<h1>Hello</h1>
Error: mesage in browser console:
The resource from “http://localhost:3001/style.css” was blocked due to MIME type (“text/plain”) mismatch (X-Content-Type-Options: nosniff)
Related
my golang code arch is as bellow:
├── embeded.go
├── go.mod
├── json
│ └── file
└── main.go
and this is my embede.go code:
package main
import "embed"
//go:embed json/*
var templatesFS embed.FS
func TemplatesFS() embed.FS {
return templatesFS
}
now in my main.go I can't access file in json directory:
package main
import (
"fmt"
"log"
"os"
"text/template"
)
func main() {
tmpl := template.Must(
template.New("json/file").
ParseFS(TemplatesFS(), "json/file"))
if err := tmpl.Execute(os.Stdout, "config"); err != nil {
log.Fatal(err)
}
}
when I run above code I got error template: json/file: "json/file" is an incomplete or empty template
but I can access the file like this:
file, err := TemplatesFS().ReadFile("json/file")
so why I can't access it in templte.execute ?
How Can I Solve It ?
The template parser successfully read a template from the embedded file system.
The Execute method reports that the template tmpl is incomplete. The variable tmpl is set to the template created by the call to New. The template is incomplete because the application did not parse a template with the template's name, json/file.
ParseFS names templates using the file's base name. Fix by using using the file's base name in the call to New.
tmpl := template.Must(
template.New("file").ParseFS(TemplatesFS(), "json/file"))
I have the following directory structure:
web/
dist/
index.html
main.go
the content of main.go:
package main
import (
"embed"
"io/fs"
"net/http"
)
//go:embed web/dist
var webfs embed.FS
func main() {
mux := http.NewServeMux()
sub, err := fs.Sub(webfs, "web/dist")
if err != nil {
panic(err)
}
mux.Handle("/", http.FileServer(http.FS(sub)))
http.ListenAndServe(":2222", mux)
}
When the code is run, I could get the content of index.html through http://127.0.0.1:2222. However, when I changed the line to:
mux.Handle("/admin/", http.FileServer(http.FS(sub)))
I get 404 when accessing http://127.0.0.1:2222/admin/.
You will need to strip the /admin/ off the request path (otherwise the file server will be looking in web/dist/admin). e.g.
mux.Handle("/admin/", http.StripPrefix("/admin/", http.FileServer(http.FS(sub))))
See the docs for StripPrefix for more info.
Go 1.16 is out and I want to use the new embed features. I can get it to work if everything is in the main package. But it's not clear how to handle accessing resources from subfolders/packages. Trying to do it with embed.FS support.
e.g. I have a main.go and also an HTTP handler in a handlers package/folder
If I put the handler in the main, it works. If I put it in the handlers package, it can't find the templates. I get:
handlers/indexHandler.go:11:12: pattern templates: no matching files found exit status 1
Similarly, I can get it to serve an image from the static folder if I serve it from /. But I can't serve both a handler from / and the static/images from /. If I put images on /static/ it can't find the images.
I think it has to do with relative paths. But I can't find the right combination through trial and error... Could it be too early to rely on these features?
Previously I was using go-rice and I did not have these problems. But I would like to use the std library as much as possible.
main.go:
package main
import (...)
//go:embed static
var static embed.FS
func main() {
fsRoot, _ := fs.Sub(static, "static")
fsStatic := http.FileServer(http.FS(fsRoot))
http.Handle("/", fsStatic)
http.HandleFunc("/index", Index)
http.ListenAndServe(":8080", nil)
}
handlers/indexHandler.go:
package handlers
import (...)
//go:embed templates
var templates embed.FS
// Index handler
func Index(w http.ResponseWriter, r *http.Request) {
tmpl := template.New("")
var err error
if tmpl, err = tmpl.ParseFS(templates, "simple.gohtml"); err != nil {
fmt.Println(err)
}
if err = tmpl.ExecuteTemplate(w, "simple", nil); err != nil {
log.Print(err)
}
}
Structure is as follows...
.
├── go.mod
├── go.sum
├── handlers
│ └── indexHandler.go
├── main.go
├── static
│ ├── css
│ │ └── layout.css
│ └── images
│ └── logo.png
└── templates
└── simple.gohtml
I finally figured it out...
You can keep the templates folder in the main folder and embed them from there. Then you need to inject the FS variable into the other handler package. It's always easy after you figure it out.
e.g.
package main
//go:embed templates/*
var templateFs embed.FS
func main() {
handlers.TemplateFs = templateFs
...
package handlers
var TemplateFs embed.FS
func handlerIndex() {
...
tmpl, err = tmpl.ParseFS(TemplateFs, "templates/layout.gohtml",...
...
Currently in Go 1.17 , embed is not supporting subfolders/packages, see https://github.com/golang/go/issues/41191#issuecomment-686616556
Go 1.16 added the new embed package. I would like to use this package to embed a directory and serve it over HTTP. Consider the following setup:
myproject/
|-- main.go
|-- static/
| |-- index.html
| |-- styles.css
| |-- scripts.js
package main
import (
"embed"
"log"
"net/http"
)
//go:embed static
var staticFS embed.FS
func main() {
http.Handle("/", http.FileServer(http.FS(staticFS)))
log.Fatal(http.ListenAndServe(":8080", nil))
}
With this setup, my expectation is that I can point my browser to localhost:8080 and have it load index.html. What I am observing instead, is that I need to point my browser to localhost:8080/static to have it load index.html.
How can an embedded filesystem be served from the root path of the URL?
When declaring a variable of type embed.FS, that variable represents a filesystem that already contains a root directory. All resources from the //go:embed directive are copied into this root directory of the filesystem. That means that the staticFS variable does not refer to the static folder that was being embedded directly, but to the root directory that contains the static folder. With that in mind, to achieve the desired result of being able to access the static folder from localhost:8080, Go's fs.Sub can be used to pass a filesystem to the server where the static folder is the root:
package main
import (
"embed"
"io/fs"
"log"
"net/http"
)
//go:embed static
var embeddedFS embed.FS
func main() {
serverRoot, err := fs.Sub(embeddedFS, "static")
if err != nil {
log.Fatal(err)
}
http.Handle("/", http.FileServer(http.FS(serverRoot)))
log.Fatal(http.ListenAndServe(":8080", nil))
}
Alternatively to fs.Sub you can declare embedding inside the static directory.
package static
//go:embed *.html *.css *.js
var FS embed.FS
And then import it.
package main
import (
"embed"
"log"
"net/http"
"import/path/to/static"
)
func main() {
http.Handle("/", http.FileServer(http.FS(static.FS)))
log.Fatal(http.ListenAndServe(":8080", nil))
}
The downside of adding embedding into static directory is that *.go file may be also added as embedded unless strict file masks are used (e.g. //go:embed *.html *js instead of //go:embed *).
Edit: Removed additionally proposed http.StripPrefix because it does not actually help with issue.
I'm trying to test an implementation of a simple web server in Go using the Gin.
The service has a single endpoint rendering HTML.
server.go
// Serve triggers the server initialization
func Serve(addr string) {
if err := serverEngine().Run(addr); err != nil {
log.Fatalf("could not serve on %s: %s", addr, err)
}
}
func serverEngine() *gin.Engine {
eng := gin.Default()
// Register resource handlers
eng.LoadHTMLGlob("tmpl/*")
eng.GET("/", indexHandler)
return eng
}
func indexHandler(c *gin.Context) {
c.HTML(http.StatusOK, "index.tmpl", gin.H{
"title": "foo",
})
}
server_test.go
func TestServe(t *testing.T) {
timeOut := time.Duration(3) * time.Second
server := httptest.NewServer(serverEngine())
defer server.Close()
// fixes weird double ':' problem
port := server.URL[len(server.URL)-5:]
_, err := net.DialTimeout("tcp", "localhost:"+port, timeOut)
if err != nil {
t.Errorf("failed to dial server: %s", err)
}
}
When I run the code everything works fine. But when running the unit tests it panics with the following message:
--- FAIL: TestServe (0.00s)
panic: html/template: pattern matches no files: `tmpl/*` [recovered]
panic: html/template: pattern matches no files: `tmpl/*`
Project structure:
.
├── main.go
└── server
├── server.go
├── server_test.go
└── tmpl
└── index.tmpl
How can I ensure that the go test know about the template location at runtime so I can perform this test?
EDIT:
After comment interaction, updating answer match directory structure.
gin-test/
├── main.go
└── server
├── server.go
├── server_test.go
└── tmpl
└── index.tmpl
And your server.go file on function serverEngine() has-
eng.LoadHTMLGlob("tmpl/*")
First you're in root directory: i.e. gin-test, run-
# Output:
jeeva#mb-pro:~/go-home/src/gin-test$ go test ./...
? gin-test [no test files]
ok gin-test/server 0.046s
Second you're in gin-test/server directory/package:
jeeva#mb-pro:~/go-home/src/gin-test/server$ go test
#Output:
It would produce the same output as below answer.
My best guess is 'problem could be your directory structure and files' and bit of test case code improvements.
I have just tried your code at my local with following directory structure:
$GOPATH/src/gin-test/
├── server.go
├── server_test.go
└── tmpl
└── index.tmpl
Then, I have updated your TestServe method to following:
Note: your test case works too, I just updated to demo the GET request and response.
func TestServe(t *testing.T) {
server := httptest.NewServer(serverEngine())
defer server.Close()
fmt.Println("Server URL:", server.URL)
resp, err := http.Get(server.URL)
body, _ := ioutil.ReadAll(resp.Body)
fmt.Println("Error:", err)
fmt.Println("Response:", string(body))
}
Unit test output:
jeeva#mb-pro:~/go-home/src/gin-test$ go test
[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
- using env: export GIN_MODE=release
- using code: gin.SetMode(gin.ReleaseMode)
[GIN-debug] Loaded HTML Templates (1):
- index.tmpl
[GIN-debug] GET / --> gin-test.indexHandler (3 handlers)
Server URL: http://127.0.0.1:56989
[GIN] 2017/07/09 - 16:31:16 | 200 | 432.107µs | 127.0.0.1 | GET /
Error: <nil>
Response: <b>I'm here</b>
PASS
ok gin-test 0.104s
jeeva#mb-pro:~/go-home/src/gin-test$
Happy coding!