This is a creation in Article, where the information may have evolved or changed.
Go is a simple and powerful language, and after a brief experience it feels great to support the network and the command line, this article describes the general process of go implementing a static server.
Basic implementation
Recently took over the translation work of Gobyexample, which requires a local test environment after the project is reconstructed.
Because you want the URL of the page to appear as " https://gobyexample.xgwang.me/hello-world
" this end without the "/" form, the child page does not have HTML, and a picture resource therefore requires a static server.
According to the Golang Wiki, implementing this simple server requires only ... One line of code:
package mainimport "net/http"func main() { panic(http.ListenAndServe(":8080", http.FileServer(http.Dir("/usr/share/doc"))))}
After adding the log, rewrite it and put it in the tools directory of our project:
package mainimport ( "log" "net/http")func main() { // Simple static webserver: port := ":8080" log.Printf("Serving at: http://localhost%s\n", port) err := http.ListenAndServe(port, http.FileServer(http.Dir("public"))) if err != nil { log.Fatal("ListenAndServe fail:", err) }}
One more executable file. tools/serve
#!/bin/bashexec go run tools/serve.go
OK now you just need to tools/serve
start this server.
404
Everything looks normal, but if we visit a page that does not exist, 404.html will not be serve, because go provides FileServer
404 pages that we do not know we have customized.
So we need to http.FileServer
change to a custom one Handler
.
The best thing to do when writing go is that the Go official team offers very opinionated convention, such as GO-GET,GO-FMT.
In our input http.FileServer
will automatically add the imports
corresponding library, jump to the source code to see the implementation of this function:
type fileHandler struct { root FileSystem}// FileServer returns a handler that serves HTTP requests// with the contents of the file system rooted at root.//// To use the operating system's file system implementation,// use http.Dir://// http.Handle("/", http.FileServer(http.Dir("/tmp")))//// As a special case, the returned file server redirects any request// ending in "/index.html" to the same path, without the final// "index.html".func FileServer(root FileSystem) Handler { return &fileHandler{root}}func (f *fileHandler) ServeHTTP(w ResponseWriter, r *Request) { upath := r.URL.Path if !strings.HasPrefix(upath, "/") { upath = "/" + upath r.URL.Path = upath } serveFile(w, r, f.root, path.Clean(upath), true)}
So we know that the function here needs to return the handler there is a ServeHTTP
method. But here's serveFile
not a direct http.serveFile
call: go specifies that a package starts with a lowercase letter that is private and cannot be accessed by an external package.
But it doesn't matter, we can fileHandler
wrap a layer of agent on it, execute all the original contents after we have executed the logic that we judge the existence of the file fileHandler.ServeHTTP
, the modified code is as follows:
type fileHandler struct { root http.FileSystem h http.Handler}func fileServer(root http.FileSystem, h http.Handler) http.Handler { return &fileHandler{root, h}}func (f *fileHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { path := r.URL.Path if _, err := os.Stat("public/" + path); os.IsNotExist(err) { http.ServeFile(w, r, "public/404.html") return } f.h.ServeHTTP(w, r)}func main() { // Simple static webserver: port := ":8080" log.Printf("Serving at: http://localhost%s\n", port) fs := http.Dir("public") http.Handle("/", fileServer(&fs, http.FileServer(&fs))) err := http.ListenAndServe(port, nil) if err != nil { log.Fatal("ListenAndServe fail:", err) }}
FileSystem
incoming pointers are also avoided when they are passed in, and have a sense of c.
Small details
Basic functionality has been implemented, but as a command-line tool, I hope to make some improvements.
First we need to support the parameter, go to command line parameters support is very good, as long as the introduction of BUILTIN flag packet, we join
port := flag.String("port", ":8080", "localhost port to serve")path := flag.String("path", "public", "public files path")flag.Parse()
You can get *string
the command line arguments of the type, and naturally support the default values and descriptions, and test it to go run tools/serve.go -h
get:
Usage of /var/folders/sd/cwk5fwtd4ms5vflhq5_0_5rr0000gn/T/go-build178666598/command-line-arguments/_obj/exe/serve: -path string public files path (default "public") -port string localhost port to serve (default ":8080")
Before you prepare the serve file, output the formatted information with a bold look at the parameters we passed in:
log.Printf("Serving \x1b[1m%s\x1b[0m at: http://localhost\x1b[1m%s\x1b[0m\n", *path, *port)
This \x1b[0m
represents "all attributes off", which stands for " \x1b[1m
Bold on (enable foreground intensity)".
Summarize
As a static language, go has the flexibility to rival dynamic languages, has a complete and easy-to-use toolchain and a rich standard library, and is the fastest growing language in the 2017, simple and powerful.
Hope that more people can learn go together, I am perfecting go by example translation, welcome to read and contribute pr!
Read the original