Serving static files
Now let’s improve the look and feel of the home page by adding some static CSS and image files to our project, along with a tiny bit of JavaScript to highlight the active navigation item.
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
Go’s 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 GET
requests which begin with "/static/"
are handled using this, like so:
Route pattern | Handler | Action |
---|---|---|
GET / | home | Display the home page |
GET /snippet/view/{id} | snippetView | Display a specific snippet |
GET /snippet/create | snippetCreate | Display a form for creating a new snippet |
POST /snippet/create | snippetCreatePost | Save a new snippet |
GET /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 for a file, it will remove the leading slash from the request 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.
Open your 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.tmpl
file to make use of the static files:
Make sure you save the changes, then restart the server and visit http://localhost:4000
. Your home page should now look like this:
Additional information
File server features and functions
Go’s http.FileServer
handler 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.
and..
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.png
file, like so:The
Last-Modified
andIf-Modified-Since
headers are transparently supported. If a file hasn’t changed since the user last requested it, thenhttp.FileServer
will send a304 Not Modified
status code instead of the file itself. This helps reduce latency and processing overhead for both the client and server.The
Content-Type
is automatically set from the file extension using themime.TypeByExtension()
function. You can add your own custom extensions and content types using themime.AddExtensionType()
function if necessary.
Performance
In this chapter we set up the file server so that it serves files out of the ./ui/static
directory on your hard disk.
But it’s important to note that http.FileServer
probably won’t be reading these files from disk once the application is up-and-running. 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.