I wrote a very basic web application, following this tutorial. I want to add css rules in an external stylesheet, but it's not working - when the HTML templates are rendered, something goes wrong and the CSS is completely ignored. If I put the rules in tags, it works, but I don't want to have to deal with that.
In a Go web application, how do I render an external CSS stylesheet?
Add a handler to handle serving static files from a specified directory.
eg. create {server.go directory}/resources/ and use
http.Handle("/resources/", http.StripPrefix("/resources/", http.FileServer(http.Dir("resources"))))
along with /resources/somethingsomething.css
The reason for using StripPrefix is that you can change the served directory as you please, but keep the reference in HTML the same.
eg. to serve from /home/www/
http.Handle("/resources/", http.StripPrefix("/resources/", http.FileServer(http.Dir("/home/www/"))))
Note that this will leave the resources directory listing open.
If you want to prevent that, there is a good snippet on the go snippet blog:
http://gosnip.posterous.com/disable-directory-listing-with-httpfileserver
Edit: Posterous is now gone, so I just pulled the code off of the golang mailing list and will post it here.
type justFilesFilesystem struct {
fs http.FileSystem
}
func (fs justFilesFilesystem) Open(name string) (http.File, error) {
f, err := fs.fs.Open(name)
if err != nil {
return nil, err
}
return neuteredReaddirFile{f}, nil
}
type neuteredReaddirFile struct {
http.File
}
func (f neuteredReaddirFile) Readdir(count int) ([]os.FileInfo, error) {
return nil, nil
}
Original post discussing it: https://groups.google.com/forum/#!topic/golang-nuts/bStLPdIVM6w
And use it like this in place of the line above:
fs := justFilesFilesystem{http.Dir("resources/")}
http.Handle("/resources/", http.StripPrefix("/resources/", http.FileServer(fs)))
Related
TL;DR
I'm wondering how to organise function maps for go templates that are dependant on the data being obtained in the specific endpoint being called.
Issue
I have a main frontend endpoint (for a CMS) which will grab the slug of the page, and go and obtain the post data it from the model. I also have a templates package that takes in models, the post data and gin context.
GoView is currently being used. But I am setting HTMLRender to equal a new ginview evertime the page is hit presumably this isn't great for performance and I'm wondering if there is a neater way of going about it.
// Serve the front end website
func (c *FrontendController) Serve(g *gin.Context) {
path := g.Request.URL.Path
post, err := c.models.Posts.GetBySlug(path)
if err != nil {
NoPageFound(g)
return
}
tf := templates.NewFunctions(g, c.models, &post)
c.server.HTMLRender = ginview.New(goview.Config{
Root: paths.Theme(),
Extension: config.Template["file_extension"],
Master: "/layouts/main",
Partials: []string{},
DisableCache: true,
Funcs: tf.GetFunctions(),
})
r := ResourceData{
Post: post,
}
g.HTML(200, config.Template["template_dir"] + "/" + post.PageTemplate, r)
}
Templates
Some template functions include getting the field & author of the post, and seeing if the user is logged in, for example.
func (t *TemplateFunctions) isAuth() bool {
...
}
There are also template functions that aren't tied to gin.context such as getting the assets path.
func (t *TemplateFunctions) assetsPath() string {
...
}
Questions
Is there a way to merge the data dependant functions with global ones?
Is there a way to set the template functions without redeclaring the HTMLRender every time a page is hit? Or is it necessary?
I am trying to achieve something similar to the Go Playground's share button.
When a button called share is clicked, the current HTML response is saved into a file. That also includes everything a user can see.
What I can do so far
- I can successfully save an HTML page into a file.
- I can get and save a whole page from a URL using HTTP Get.
Caveats
- I can't save a current webpage I am working on.
This is my code so far for getting remote URLs:
func HTTPGet(url string, timeout time.Duration) (content []byte, err error) {
request, err := http.NewRequest("POST", url, nil)
if err != nil {
return
}
ctx, cancel_func := context.WithTimeout(context.Background(), timeout)
request = request.WithContext(ctx)
response, err := http.DefaultClient.Do(request)
if err != nil {
return
}
defer response.Body.Close()
if response.StatusCode != 200 {
cancel_func()
return nil, fmt.Errorf("INVALID RESPONSE; status: %s", response.Status)
}
return ioutil.ReadAll(response.Body)
}
Hopefully this will not involve messing with the DOM manually.
There is a few ways this can be done, with some cooperation from the client.
If you want to save the current web page as it is displayed to the client (with the current form values, etc.), you have to do that with Javascript on the client side. You can get the document, serialize it in some form and send it to the server to save it.
Alternative is to generate the HTML page on the server side again, but that will not contain data user entered, or any dynamically generated HTML on the client side.
Another way is to use Javascript on the client side to collect page state including form values and data representing any dynamically generated content, send it to the server to save, and reconstruct the page as needed.
I am currently working on a Go web server utilizing the builtin templates.
The current issue I am having is that when I run the web server, it is serving the correct files but it does not load any media on the site.(Such as pictures and fonts) When I run the .html file as it is all media is loaded so I know it has something to do with my back end. Here is my code :
var templates = template.Must(template.ParseGlob("static/*.html"))
...
func index(w http.ResponseWriter, r *http.Request) {
currentTime := time.Now().Local()
toSend := payload{
Date: currentTime.Format("01-02-2006"),
Status: "Active",
}
t, err := template.ParseFiles("static/index.html")
if err != nil {
log.Fatalf("Error parsing template: %v", err)
}
t.Execute(w, toSend)
}
...
And here is my file path:
app
|-main.go
|-static(contains static files)
|-media(contains all media)
|-index.html
This serves the templates perfectly fine with all the required data yet without any media. All help is appreciated thank you!
Answered by #Adrian in the comments, I simply wasn't loading the media assets via the go server for the html to use.
http.Handle("/media/",http.StripPrefix("/media/",http.FileServer(http.Dir("static/media"))))
Was all I needed
I'm using gorilla serve mux to serve static html files.
r := mux.NewRouter()
r.PathPrefix("/").Handler(http.FileServer(http.Dir("./public"))).Methods("GET")
I do have a Index.html file inside the public folder as well as other html files.
When browsing the site I get all the content of the folder instead of the default Index.html.
I came from C# and I know that IIS takes Index.html as default but it is possible to select any page as a default.
I wanted to know if there's a proper way to select a default page to serve in Gorilla mux without creating a custom handler/wrapper.
Maybe using a custom http.HandlerFunc would be easier:
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
// Here you can check if path is empty, you can render index.html
http.ServeFile(w, r, r.URL.Path)
})
You do have to make a custom handler because you want a custom behavior. Here, I just wrapped the http.FileServer handler.
Try this one:
package main
import (
"log"
"net/http"
"github.com/gorilla/mux"
)
func main() {
handler := mux.NewRouter()
fs := http.FileServer(http.Dir("./public"))
handler.PathPrefix("/").Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/" {
//your default page
r.URL.Path = "/my_default_page.html"
}
fs.ServeHTTP(w, r)
})).Methods("GET")
log.Fatal(http.ListenAndServe(":8080", handler))
}
So, from the code, if the visited path is the root (/) then you rewrite the r.URL.Path to your default page, in this case my_default_page.html.
After grabthefish mentioned it i decided to check the actual code of gorilla serve mux.
This code is taken from net/http package which Gorilla mux is based on.
func serveFile(w ResponseWriter, r *Request, fs FileSystem, name string,
redirect bool) {
const indexPage = "/index.html"
// redirect .../index.html to .../
// can't use Redirect() because that would make the path absolute,
// which would be a problem running under StripPrefix
if strings.HasSuffix(r.URL.Path, indexPage) {
localRedirect(w, r, "./")
return
}
the code request the index file to be index.html in lower case so renaming my index file solved it.
thank you grabthefish!
I'm new to developing web applications in Go.
I'm looking for the best way to integrate a MySQL database into my web application.
I was thinking of doing something like this:
type Context struct {
Database *sql.DB
}
// Some database methods like Close() and Query() for Context struct here
In the main function for my web application I would have something like this:
db := sql.Open(...)
ctx := Context{db}
I would then pass then pass my Context structure into various handlers that require a database connection. Would this be a good design decision or is there a better way to integrate a SQL database into my web application?
I typically do something like this:
package main
func main(){
db,err := sql.Open(...)
if err != nil {
log.Fatal(err)
}
defer db.Close()
http.HandleFunc("/feed", server.FeedHandler(db))
http.HandleFunc("/gui", server.GuiHandler(db))
...
log.Fatal(http.ListenAndServe(":8000", nil))
}
Where server is a separate package where I define, implement and test all my http handlers.
This is basically what you were thinking of but skipping the step of wrapping the db in a struct which isn't necessary. I would not recommend making the db a global variable. Having global dependencies will completely destroy any possibility of testing your http handlers in a reliable fashion.
Dependency injecting the db, as in the example above, costs you two extra characters to type every time you call the function but it allows you to test your http handlers easily using the go-sqlmock package which you definitely want to do as your web app grows.
package server
func TestFeedHandler(t *testing.T){
mockdb, err := sqlmock.New()
if err != nil {
t.Errorf("An error '%s' was not expected when opening a stub database connection", err)
}
columns := []string{"id", "name"}
sqlmock.ExpectQuery("SELECT id,name from persons;").
WillReturnRows(sqlmock.NewRows(columns).
AddRow(1234, "bob")
handler := FeedHandler(mockdb)
// test that handler returns expected result
}