Serving Static Files
If you’re following along, you can grab the necessary files and extract them into the
ui/static folder that we made earlier with the following commands:
The contents of your
ui/static directory should now look like this:
The http.FileServer Handler
net/http package ships with a built-in
http.FileServer handler which you can use to serve files over HTTP from a specific directory. Let’s add a new route to our application so that all requests which begin with
"/static/" are handled using this, like so:
|ANY||/||home||Display the home page|
|ANY||/snippet?id=1||showSnippet||Display a specific snippet|
|POST||/snippet/create||createSnippet||Create a new snippet|
|ANY||/static/||http.FileServer||Serve a specific static file|
To create a new
http.FileServer handler, we need to use the
http.FileServer() function like this:
When this handler receives a request, it will remove the leading slash from the URL path and then search the
./ui/static directory for the corresponding file to send to the user.
So, for this to work correctly, we must strip the leading
"/static" from the URL path before passing it to
http.FileServer. Otherwise it will be looking for a file which doesn’t exist and the user will receive a
404 page not found response. Fortunately Go includes a
http.StripPrefix() helper specifically for this task.
main.go file and add the following code, so that the file ends up looking like this:
Once that’s complete, restart the application and open
http://localhost:4000/static/ in your browser. You should see a navigable directory listing of the
ui/static folder which looks like this:
Feel free to have a play around and browse through the directory listing to view individual files. For example, if you navigate to
http://localhost:4000/static/css/main.css you should see the CSS file appear in your browser like so:
Using the Static Files
With the file server working properly, we can now update the
ui/html/base.layout.tmpl file to make use of the static files:
Make sure you save the changes, then visit
http://localhost:4000. Your home page should now look like this:
Features and Functions
Go’s file server has a few really nice features that are worth mentioning:
It sanitizes all request paths by running them through the
path.Clean()function before searching for a file. This removes any
..elements from the URL path, which helps to stop directory traversal attacks. This feature is particularly useful if you’re using the fileserver in conjunction with a router that doesn’t automatically sanitize URL paths.
Range requests are fully supported. This is great if your application is serving large files and you want to support resumable downloads. You can see this functionality in action if you use curl to request bytes 100-199 of the
logo.pngfile, like so:
If-Modified-Sinceheaders are transparently supported. If a file hasn’t changed since the user last requested it, then
http.FileServerwill send a
304 Not Modifiedstatus code instead of the file itself. This helps reduce latency and processing overhead for both the client and server.
Content-Typeis automatically set from the file extension using the
mime.TypeByExtension()function. You can add your own custom extensions and content types using the
mime.AddExtensionType()function if necessary.
In the code above we’ve set up our file server so that it serves files out of the
./ui/static directory on your hard disk.
But it’s important to note that, once the application is up-and-running,
http.FileServer probably won’t be reading these files from disk. Both Windows and Unix-based operating systems cache recently-used files in RAM, so (for frequently-served files at least) it’s likely that
http.FileServer will be serving them from RAM rather than making the relatively slow round-trip to your hard disk.
Serving Single Files
Sometimes you might want to serve a single file from within a handler. For this there’s the
http.ServeFile() function, which you can use like so:
Disabling Directory Listings
If you want to disable directory listings there are a few different approaches you can take. The simplest way? Add a blank
index.html file to the specific directory that you want to disable listings for. This will then be served instead of the directory listing, and the user will get a
200 OK response with no body. If you want to do this for all directories under
./ui/static you can use the command:
A more complicated (but arguably better) solution is to create a custom implementation of
http.FileSystem, and have it return an
os.ErrNotExist error for any directories. A full explanation and sample code can be found in this blog post.
The Go 1.16 release introduced the new
embed package which makes it possible to embed static files into your Go program itself. We’ll explain how to use this new functionality later in Appendix 15.01: Embedding Files.