Serving Files
Goa provides a straightforward way to serve static assets such as HTML, CSS,
JavaScript, and images through the Files function in the service DSL. This
function allows you to map HTTP paths to directories or specific files on disk,
enabling your service to deliver static content efficiently.
Using the Files Function
The Files function defines an endpoint that serves static assets via HTTP. It
behaves similarly to the standard http.ServeFile function, handling requests
to serve files or directories based on the defined path.
Syntax
Files(path, filename string, dsl ...func())
- path: The HTTP request path. It can include a wildcard (e.g.,
{*filepath}) to match variable segments of the URL. - filename: The file system path to the directory or file to serve.
- dsl: Optional DSL function to provide additional metadata such as descriptions and documentation.
Examples
Serving a Single File
To serve a single file, define the Files function with a specific path and the file’s location on disk.
var _ = Service("web", func() {
Files("/index.html", "/www/data/index.html", func() {
// All optional, but useful for OpenAPI spec
Description("Serve home page.")
Docs(func() {
Description("Additional documentation")
URL("https://goa.design")
})
})
})
In this example:
- Path:
/index.html- Requests to/index.htmlwill serve the file located at/www/data/index.html. - Filename:
/www/data/index.html- Absolute path to the file on disk. - DSL Functions: Provide a description and additional documentation for the endpoint.
Serving Static Assets with Wildcard
To serve multiple files from a directory, use a wildcard in the path.
var _ = Service("web", func() {
Files("/static/{*path}", "/www/data/static", func() {
Description("Serve static assets like CSS, JS, and images.")
})
})
In this example:
- Path:
/static/{*path}- The{*path}wildcard matches any subpath after/static/, allowing dynamic file serving. - Filename:
/www/data/static- Directory containing static assets. - Description: Provides a description for the endpoint.
Path Resolution
When using a wildcard path like /static/{*path}, Goa combines the wildcard value with the base directory to locate the file:
- The wildcard portion of the URL path is extracted
- This is appended to the base directory specified in
Filename - The resulting path is used to look up the file
For example, with the configuration:
Files("/static/{*path}", "/www/data/static")
If the URL path is /static/css/style.css, Goa will resolve to /www/data/static/css/style.css.
Handling Index Files
When serving directories, ensure that index files (e.g., index.html) are
correctly mapped. If you do not explicitly map index.html under a wildcard
path, the underlying http.ServeFile call will return a redirect to ./
instead of the index.html file.
Example
var _ = Service("bottle", func() {
Files("/static/{*path}", "/www/data/static", func() {
Description("Serve static assets for the SPA.")
})
Files("/index.html", "/www/data/index.html", func() {
Description("Serve the SPA's index.html for client-side routing.")
})
})
This configuration ensures that requests to /index.html serve the index.html
file, while requests to /static/* serve files from the static directory.
Integration with Service Implementation
When implementing static file serving in your Goa service, you have several options for managing and serving the files:
Using the File System: In your service implementation, use the file system to serve embedded files.
Using Embedded Files: The
embedpackage in Go 1.16+ allows you to embed static files directly into your binary, making deployment simpler and more reliable.
Example Service Implementation
This example demonstrates how to serve static files using the embed package.
Assuming the following design:
var _ = Service("web", func() {
Files("/static/{*path}", "static")
})
The service implementation can be:
package web
import (
"embed"
// ... other imports ...
)
//go:embed static
var StaticFS embed.FS
// ... other service code ...
Main Function Setup
In the main function, configure the HTTP server to serve static files by:
- Creating a
http.FSinstance from the embeddedStaticFSusinghttp.FS(web.StaticFS) - Passing this file system instance as the last argument to the generated
Newfunction - This allows the HTTP server to efficiently serve the embedded static files
through the endpoints defined with
Filesin the design
The file system instance provides access to the embedded files while maintaining proper file system semantics and security.
func main() {
// Other setup code...
mux := goahttp.NewMuxer()
server := genhttp.New(
endpoints,
mux,
goahttp.RequestDecoder,
goahttp.ResponseEncoder,
nil,
nil,
http.FS(web.StaticFS), // Pass the embedded file system
)
genhttp.Mount(mux, server)
// Start the server...
}
In this setup:
- go:embed: Embeds the
staticdirectory into the binary. - http.FS: Provides the embedded file system to the server for serving static files.
Summary
Using the Files function in Goa allows you to efficiently serve static content
in your services. By defining specific paths and file locations, you can manage
the delivery of static assets seamlessly. Ensure proper mapping of index files
and utilize embedded file systems for streamlined deployments.