From 638caf72f0a85c9b29dc4d4e993d2f80e01491b7 Mon Sep 17 00:00:00 2001 From: stffabi Date: Sat, 29 Oct 2022 23:15:15 +0200 Subject: [PATCH] [assetserver] Introduce middleware and extract options (#2016) * [assetserver] Add support for HTTP Middlewares * [dev] Disable frontend DevServer if no Assets has been defined and inform user * [dev] Consistent WebSocket behaviour in dev and prod mode for assets handler and middleware In prod mode we can't support WebSockets so make sure the assets handler and middleware never see WebSockets in dev mode. * [templates] Migrate to new AssetServer option * [docs] Add assetserver.Options to the reference --- .../generate/template/base/main.go.tmpl | 5 +- .../templates/generate/plain/main.go.tmpl | 5 +- .../templates/templates/vue/main.tmpl.go | 11 +-- v2/internal/app/app_dev.go | 18 ++++- .../frontend/assetserver/assethandler.go | 18 +++-- .../frontend/assetserver/assetserver.go | 32 ++++++--- ...rver_browser_dev.go => assetserver_dev.go} | 8 ++- v2/internal/frontend/assetserver/common.go | 17 +++++ .../frontend/desktop/darwin/frontend.go | 2 +- .../frontend/desktop/linux/frontend.go | 2 +- .../frontend/desktop/windows/frontend.go | 7 +- v2/internal/frontend/devserver/devserver.go | 14 +++- v2/internal/frontend/devserver/external.go | 31 ++++---- .../staticanalysis/test/standard/main.go | 11 +-- v2/pkg/options/assetserver/middleware.go | 20 ++++++ v2/pkg/options/assetserver/options.go | 35 +++++++++ v2/pkg/options/options.go | 11 ++- v2/pkg/templates/base/main.go.tmpl | 11 +-- v2/pkg/templates/generate/plain/main.go.tmpl | 5 +- .../templates/templates/lit-ts/main.go.tmpl | 11 +-- v2/pkg/templates/templates/lit/main.go.tmpl | 11 +-- v2/pkg/templates/templates/plain/main.go.tmpl | 11 +-- .../templates/preact-ts/main.go.tmpl | 11 +-- .../templates/templates/preact/main.go.tmpl | 11 +-- .../templates/templates/react-ts/main.go.tmpl | 11 +-- v2/pkg/templates/templates/react/main.go.tmpl | 11 +-- .../templates/svelte-ts/main.go.tmpl | 11 +-- .../templates/templates/svelte/main.go.tmpl | 11 +-- .../templates/vanilla-ts/main.go.tmpl | 11 +-- .../templates/templates/vanilla/main.go.tmpl | 11 +-- .../templates/templates/vue-ts/main.go.tmpl | 11 +-- v2/pkg/templates/templates/vue/main.go.tmpl | 11 +-- website/docs/guides/dynamic-assets.mdx | 13 ++-- website/docs/guides/frameless.mdx | 13 ++-- website/docs/guides/migrating.mdx | 8 ++- website/docs/howdoesitwork.mdx | 36 ++++++---- website/docs/reference/options.mdx | 71 ++++++++++++++++--- 37 files changed, 397 insertions(+), 150 deletions(-) rename v2/internal/frontend/assetserver/{assetserver_browser_dev.go => assetserver_dev.go} (56%) create mode 100644 v2/pkg/options/assetserver/middleware.go create mode 100644 v2/pkg/options/assetserver/options.go diff --git a/v2/cmd/wails/internal/commands/generate/template/base/main.go.tmpl b/v2/cmd/wails/internal/commands/generate/template/base/main.go.tmpl index 268a44348..d8e902027 100644 --- a/v2/cmd/wails/internal/commands/generate/template/base/main.go.tmpl +++ b/v2/cmd/wails/internal/commands/generate/template/base/main.go.tmpl @@ -7,6 +7,7 @@ import ( "github.com/wailsapp/wails/v2" "github.com/wailsapp/wails/v2/pkg/logger" "github.com/wailsapp/wails/v2/pkg/options" + "github.com/wailsapp/wails/v2/pkg/options/assetserver" "github.com/wailsapp/wails/v2/pkg/options/mac" "github.com/wailsapp/wails/v2/pkg/options/windows" ) @@ -36,7 +37,9 @@ func main() { StartHidden: false, HideWindowOnClose: false, BackgroundColour: &options.RGBA{R: 255, G: 255, B: 255, A: 255}, - Assets: assets, + AssetServer: &assetserver.Options{ + Assets: assets, + }, Menu: nil, Logger: nil, LogLevel: logger.DEBUG, diff --git a/v2/cmd/wails/internal/commands/initialise/templates/generate/plain/main.go.tmpl b/v2/cmd/wails/internal/commands/initialise/templates/generate/plain/main.go.tmpl index 8f37a961a..433cfee4e 100644 --- a/v2/cmd/wails/internal/commands/initialise/templates/generate/plain/main.go.tmpl +++ b/v2/cmd/wails/internal/commands/initialise/templates/generate/plain/main.go.tmpl @@ -9,6 +9,7 @@ import ( "github.com/wailsapp/wails/v2" "github.com/wailsapp/wails/v2/pkg/logger" "github.com/wailsapp/wails/v2/pkg/options" + "github.com/wailsapp/wails/v2/pkg/options/assetserver" "github.com/wailsapp/wails/v2/pkg/options/windows" ) @@ -37,7 +38,9 @@ func main() { StartHidden: false, HideWindowOnClose: false, BackgroundColour: &options.RGBA{R: 27, G: 38, B: 54, A: 1}, - Assets: assets, + AssetServer: &assetserver.Options{ + Assets: assets, + }, Menu: nil, Logger: nil, LogLevel: logger.DEBUG, diff --git a/v2/cmd/wails/internal/commands/initialise/templates/templates/vue/main.tmpl.go b/v2/cmd/wails/internal/commands/initialise/templates/templates/vue/main.tmpl.go index e0f197dee..e24782be3 100644 --- a/v2/cmd/wails/internal/commands/initialise/templates/templates/vue/main.tmpl.go +++ b/v2/cmd/wails/internal/commands/initialise/templates/templates/vue/main.tmpl.go @@ -5,6 +5,7 @@ import ( "github.com/wailsapp/wails/v2" "github.com/wailsapp/wails/v2/pkg/options" + "github.com/wailsapp/wails/v2/pkg/options/assetserver" ) //go:embed all:frontend/dist @@ -16,10 +17,12 @@ func main() { // Create application with options err := wails.Run(&options.App{ - Title: "{{.ProjectName}}", - Width: 1024, - Height: 768, - Assets: assets, + Title: "{{.ProjectName}}", + Width: 1024, + Height: 768, + AssetServer: &assetserver.Options{ + Assets: assets, + }, BackgroundColour: &options.RGBA{R: 27, G: 38, B: 54, A: 1}, OnStartup: app.startup, Bind: []interface{}{ diff --git a/v2/internal/app/app_dev.go b/v2/internal/app/app_dev.go index d807ed9a3..35c02678f 100644 --- a/v2/internal/app/app_dev.go +++ b/v2/internal/app/app_dev.go @@ -13,6 +13,7 @@ import ( "path/filepath" "github.com/wailsapp/wails/v2/internal/binding" + "github.com/wailsapp/wails/v2/internal/frontend/assetserver" "github.com/wailsapp/wails/v2/internal/frontend/desktop" "github.com/wailsapp/wails/v2/internal/frontend/devserver" "github.com/wailsapp/wails/v2/internal/frontend/dispatcher" @@ -90,6 +91,14 @@ func CreateApp(appoptions *options.App) (*App, error) { } } + assetConfig := assetserver.BuildAssetServerConfig(appoptions) + + if assetConfig.Assets == nil && frontendDevServerURL != "" { + myLogger.Warning("No AssetServer.Assets has been defined but a frontend DevServer, the frontend DevServer will not be used.") + frontendDevServerURL = "" + assetdir = "" + } + if frontendDevServerURL != "" { if devServer == "" { return nil, fmt.Errorf("Unable to use FrontendDevServerUrl without a DevServer address") @@ -107,7 +116,7 @@ func CreateApp(appoptions *options.App) (*App, error) { } else { if assetdir == "" { // If no assetdir has been defined, let's try to infer it from the project root and the asset FS. - assetdir, err = tryInferAssetDirFromFS(appoptions.Assets) + assetdir, err = tryInferAssetDirFromFS(assetConfig.Assets) if err != nil { return nil, err } @@ -121,12 +130,17 @@ func CreateApp(appoptions *options.App) (*App, error) { } myLogger.Info("Serving assets from disk: %s", absdir) - appoptions.Assets = os.DirFS(absdir) + assetConfig.Assets = os.DirFS(absdir) ctx = context.WithValue(ctx, "assetdir", assetdir) } } + // Migrate deprecated options to the new AssetServer option + appoptions.Assets = nil + appoptions.AssetsHandler = nil + appoptions.AssetServer = &assetConfig + if devServer != "" { ctx = context.WithValue(ctx, "devserver", devServer) } diff --git a/v2/internal/frontend/assetserver/assethandler.go b/v2/internal/frontend/assetserver/assethandler.go index ca73be4d2..09353aa3f 100644 --- a/v2/internal/frontend/assetserver/assethandler.go +++ b/v2/internal/frontend/assetserver/assethandler.go @@ -14,6 +14,7 @@ import ( "github.com/wailsapp/wails/v2/internal/fs" "github.com/wailsapp/wails/v2/internal/logger" + "github.com/wailsapp/wails/v2/pkg/options/assetserver" ) //go:embed defaultindex.html @@ -32,7 +33,13 @@ type assetHandler struct { retryMissingFiles bool } -func NewAssetHandler(ctx context.Context, vfs iofs.FS, assetsHandler http.Handler) (http.Handler, error) { +func NewAssetHandler(ctx context.Context, options assetserver.Options) (http.Handler, error) { + var log *logger.Logger + if _logger := ctx.Value("logger"); _logger != nil { + log = _logger.(*logger.Logger) + } + + vfs := options.Assets if vfs != nil { if _, err := vfs.Open("."); err != nil { return nil, err @@ -49,13 +56,14 @@ func NewAssetHandler(ctx context.Context, vfs iofs.FS, assetsHandler http.Handle } } - result := &assetHandler{ + var result http.Handler = &assetHandler{ fs: vfs, - handler: assetsHandler, + handler: options.Handler, + logger: log, } - if _logger := ctx.Value("logger"); _logger != nil { - result.logger = _logger.(*logger.Logger) + if middleware := options.Middleware; middleware != nil { + result = middleware(result) } return result, nil diff --git a/v2/internal/frontend/assetserver/assetserver.go b/v2/internal/frontend/assetserver/assetserver.go index 5fff282f2..fd66d46bd 100644 --- a/v2/internal/frontend/assetserver/assetserver.go +++ b/v2/internal/frontend/assetserver/assetserver.go @@ -4,15 +4,16 @@ import ( "bytes" "context" "fmt" - iofs "io/fs" "net/http" "net/http/httptest" "strconv" + "golang.org/x/net/html" + "github.com/wailsapp/wails/v2/internal/frontend/runtime" "github.com/wailsapp/wails/v2/internal/logger" - - "golang.org/x/net/html" + "github.com/wailsapp/wails/v2/pkg/options" + "github.com/wailsapp/wails/v2/pkg/options/assetserver" ) const ( @@ -22,6 +23,7 @@ const ( type AssetServer struct { handler http.Handler + wsHandler http.Handler runtimeJS []byte ipcJS func(*http.Request) []byte @@ -31,8 +33,12 @@ type AssetServer struct { appendSpinnerToBody bool } -func NewAssetServer(ctx context.Context, vfs iofs.FS, assetsHandler http.Handler, bindingsJSON string) (*AssetServer, error) { - handler, err := NewAssetHandler(ctx, vfs, assetsHandler) +func NewAssetServerMainPage(ctx context.Context, bindingsJSON string, options *options.App) (*AssetServer, error) { + return NewAssetServer(ctx, bindingsJSON, BuildAssetServerConfig(options)) +} + +func NewAssetServer(ctx context.Context, bindingsJSON string, options assetserver.Options) (*AssetServer, error) { + handler, err := NewAssetHandler(ctx, options) if err != nil { return nil, err } @@ -66,17 +72,21 @@ func NewAssetServerWithHandler(ctx context.Context, handler http.Handler, bindin } func (d *AssetServer) ServeHTTP(rw http.ResponseWriter, req *http.Request) { + if isWebSocket(req) { + // Forward WebSockets to the distinct websocket handler if it exists + if wsHandler := d.wsHandler; wsHandler != nil { + wsHandler.ServeHTTP(rw, req) + } else { + rw.WriteHeader(http.StatusNotImplemented) + } + return + } + header := rw.Header() if d.servingFromDisk { header.Add(HeaderCacheControl, "no-cache") } - if isWebSocket(req) { - // WebSockets can always directly be forwarded to the handler - d.handler.ServeHTTP(rw, req) - return - } - path := req.URL.Path switch path { case "", "/", "/index.html": diff --git a/v2/internal/frontend/assetserver/assetserver_browser_dev.go b/v2/internal/frontend/assetserver/assetserver_dev.go similarity index 56% rename from v2/internal/frontend/assetserver/assetserver_browser_dev.go rename to v2/internal/frontend/assetserver/assetserver_dev.go index 6ced11787..5d83cf5fe 100644 --- a/v2/internal/frontend/assetserver/assetserver_browser_dev.go +++ b/v2/internal/frontend/assetserver/assetserver_dev.go @@ -12,15 +12,17 @@ import ( ) /* -The assetserver for dev serves assets from disk. -It injects a websocket based IPC script into `index.html`. +The assetserver for the dev mode. +Depending on the UserAgent it injects a websocket based IPC script into `index.html` or the default desktop IPC. The +default desktop IPC is injected when the webview accesses the devserver. */ -func NewBrowserAssetServer(ctx context.Context, handler http.Handler, bindingsJSON string) (*AssetServer, error) { +func NewDevAssetServer(ctx context.Context, handler http.Handler, wsHandler http.Handler, bindingsJSON string) (*AssetServer, error) { result, err := NewAssetServerWithHandler(ctx, handler, bindingsJSON) if err != nil { return nil, err } + result.wsHandler = wsHandler result.appendSpinnerToBody = true result.ipcJS = func(req *http.Request) []byte { if strings.Contains(req.UserAgent(), WailsUserAgentValue) { diff --git a/v2/internal/frontend/assetserver/common.go b/v2/internal/frontend/assetserver/common.go index b787cc6bb..ffe80577a 100644 --- a/v2/internal/frontend/assetserver/common.go +++ b/v2/internal/frontend/assetserver/common.go @@ -8,9 +8,26 @@ import ( "net/http" "strings" + "github.com/wailsapp/wails/v2/pkg/options" + "github.com/wailsapp/wails/v2/pkg/options/assetserver" "golang.org/x/net/html" ) +func BuildAssetServerConfig(options *options.App) assetserver.Options { + if opts := options.AssetServer; opts != nil { + if options.Assets != nil || options.AssetsHandler != nil { + panic("It's not possible to use the deprecated Assets and AssetsHandler options and the new AssetServer option at the same time. Please migrate all your Assets options to the AssetServer option.") + } + + return *opts + } + + return assetserver.Options{ + Assets: options.Assets, + Handler: options.AssetsHandler, + } +} + const ( HeaderHost = "Host" HeaderContentType = "Content-Type" diff --git a/v2/internal/frontend/desktop/darwin/frontend.go b/v2/internal/frontend/desktop/darwin/frontend.go index ca9975749..a1eff8e8d 100644 --- a/v2/internal/frontend/desktop/darwin/frontend.go +++ b/v2/internal/frontend/desktop/darwin/frontend.go @@ -90,7 +90,7 @@ func NewFrontend(ctx context.Context, appoptions *options.App, myLogger *logger. } else { appBindings.DB().UpdateObfuscatedCallMap() } - assets, err := assetserver.NewAssetServer(ctx, appoptions.Assets, appoptions.AssetsHandler, bindings) + assets, err := assetserver.NewAssetServerMainPage(ctx, bindings, appoptions) if err != nil { log.Fatal(err) } diff --git a/v2/internal/frontend/desktop/linux/frontend.go b/v2/internal/frontend/desktop/linux/frontend.go index 6457bc984..bb934d343 100644 --- a/v2/internal/frontend/desktop/linux/frontend.go +++ b/v2/internal/frontend/desktop/linux/frontend.go @@ -94,7 +94,7 @@ func NewFrontend(ctx context.Context, appoptions *options.App, myLogger *logger. } else { appBindings.DB().UpdateObfuscatedCallMap() } - assets, err := assetserver.NewAssetServer(ctx, appoptions.Assets, appoptions.AssetsHandler, bindings) + assets, err := assetserver.NewAssetServerMainPage(ctx, bindings, appoptions) if err != nil { log.Fatal(err) } diff --git a/v2/internal/frontend/desktop/windows/frontend.go b/v2/internal/frontend/desktop/windows/frontend.go index 7135f5d03..f5eb3b92d 100644 --- a/v2/internal/frontend/desktop/windows/frontend.go +++ b/v2/internal/frontend/desktop/windows/frontend.go @@ -102,7 +102,7 @@ func NewFrontend(ctx context.Context, appoptions *options.App, myLogger *logger. appBindings.DB().UpdateObfuscatedCallMap() } - assets, err := assetserver.NewAssetServer(ctx, appoptions.Assets, appoptions.AssetsHandler, bindings) + assets, err := assetserver.NewAssetServerMainPage(ctx, bindings, appoptions) if err != nil { log.Fatal(err) } @@ -435,9 +435,8 @@ func (f *Frontend) setupChromium() { log.Fatal(err) } - - if opts := f.frontendOptions.Windows; opts != nil { - if opts.ZoomFactor > 0.0 { + if opts := f.frontendOptions.Windows; opts != nil { + if opts.ZoomFactor > 0.0 { chromium.PutZoomFactor(opts.ZoomFactor) } err = settings.PutIsZoomControlEnabled(opts.IsZoomControlEnabled) diff --git a/v2/internal/frontend/devserver/devserver.go b/v2/internal/frontend/devserver/devserver.go index 3cfebd587..6dc18b8e0 100644 --- a/v2/internal/frontend/devserver/devserver.go +++ b/v2/internal/frontend/devserver/devserver.go @@ -12,6 +12,7 @@ import ( "log" "net" "net/http" + "net/http/httputil" "net/url" "strings" "sync" @@ -53,7 +54,10 @@ func (d *DevWebServer) Run(ctx context.Context) error { d.server.GET("/wails/reload", d.handleReload) d.server.GET("/wails/ipc", d.handleIPCWebSocket) + assetServerConfig := assetserver.BuildAssetServerConfig(d.appoptions) + var assetHandler http.Handler + var wsHandler http.Handler _fronendDevServerURL, _ := ctx.Value("frontenddevserverurl").(string) if _fronendDevServerURL == "" { assetdir, _ := ctx.Value("assetdir").(string) @@ -62,7 +66,7 @@ func (d *DevWebServer) Run(ctx context.Context) error { }) var err error - assetHandler, err = assetserver.NewAssetHandler(ctx, d.appoptions.Assets, d.appoptions.AssetsHandler) + assetHandler, err = assetserver.NewAssetHandler(ctx, assetServerConfig) if err != nil { log.Fatal(err) } @@ -81,7 +85,11 @@ func (d *DevWebServer) Run(ctx context.Context) error { d.logger.Error("Timeout waiting for frontend DevServer") } - assetHandler = newExternalDevServerAssetHandler(d.logger, externalURL, d.appoptions.AssetsHandler) + assetHandler = newExternalDevServerAssetHandler(d.logger, externalURL, assetServerConfig) + // WebSockets aren't currently supported in prod mode, so a WebSocket connection is the result of the + // FrontendDevServer e.g. Vite to support auto reloads. + // Therefore we direct WebSockets directly to the FrontendDevServer instead of returning a NotImplementedStatus. + wsHandler = httputil.NewSingleHostReverseProxy(externalURL) } // Setup internal dev server @@ -90,7 +98,7 @@ func (d *DevWebServer) Run(ctx context.Context) error { log.Fatal(err) } - assetServer, err := assetserver.NewBrowserAssetServer(ctx, assetHandler, bindingsJSON) + assetServer, err := assetserver.NewDevAssetServer(ctx, assetHandler, wsHandler, bindingsJSON) if err != nil { log.Fatal(err) } diff --git a/v2/internal/frontend/devserver/external.go b/v2/internal/frontend/devserver/external.go index a07b1c0d6..fd717e723 100644 --- a/v2/internal/frontend/devserver/external.go +++ b/v2/internal/frontend/devserver/external.go @@ -10,11 +10,21 @@ import ( "net/http/httputil" "net/url" - "github.com/labstack/echo/v4" "github.com/wailsapp/wails/v2/internal/logger" + "github.com/wailsapp/wails/v2/pkg/options/assetserver" ) -func newExternalDevServerAssetHandler(logger *logger.Logger, url *url.URL, handler http.Handler) http.Handler { +func newExternalDevServerAssetHandler(logger *logger.Logger, url *url.URL, options assetserver.Options) http.Handler { + handler := newExternalAssetsHandler(logger, url, options.Handler) + + if middleware := options.Middleware; middleware != nil { + handler = middleware(handler) + } + + return handler +} + +func newExternalAssetsHandler(logger *logger.Logger, url *url.URL, handler http.Handler) http.Handler { errSkipProxy := fmt.Errorf("skip proxying") proxy := httputil.NewSingleHostReverseProxy(url) @@ -56,23 +66,18 @@ func newExternalDevServerAssetHandler(logger *logger.Logger, url *url.URL, handl } } - e := echo.New() - e.Any("/*", - func(c echo.Context) error { - req := c.Request() - rw := c.Response() - if c.IsWebSocket() || req.Method == http.MethodGet { + return http.HandlerFunc( + func(rw http.ResponseWriter, req *http.Request) { + if req.Method == http.MethodGet { proxy.ServeHTTP(rw, req) - return nil + return } if handler != nil { handler.ServeHTTP(rw, req) - return nil + return } - return c.NoContent(http.StatusMethodNotAllowed) + rw.WriteHeader(http.StatusMethodNotAllowed) }) - - return e } diff --git a/v2/internal/staticanalysis/test/standard/main.go b/v2/internal/staticanalysis/test/standard/main.go index 93cbc7ea3..3f735d640 100644 --- a/v2/internal/staticanalysis/test/standard/main.go +++ b/v2/internal/staticanalysis/test/standard/main.go @@ -5,6 +5,7 @@ import ( "github.com/wailsapp/wails/v2" "github.com/wailsapp/wails/v2/pkg/options" + "github.com/wailsapp/wails/v2/pkg/options/assetserver" ) //go:embed all:frontend/dist @@ -16,10 +17,12 @@ func main() { // Create application with options err := wails.Run(&options.App{ - Title: "staticanalysis", - Width: 1024, - Height: 768, - Assets: assets, + Title: "staticanalysis", + Width: 1024, + Height: 768, + AssetServer: &assetserver.Options{ + Assets: assets, + }, BackgroundColour: &options.RGBA{R: 27, G: 38, B: 54, A: 1}, OnStartup: app.startup, Bind: []interface{}{ diff --git a/v2/pkg/options/assetserver/middleware.go b/v2/pkg/options/assetserver/middleware.go new file mode 100644 index 000000000..b3826ab7d --- /dev/null +++ b/v2/pkg/options/assetserver/middleware.go @@ -0,0 +1,20 @@ +package assetserver + +import ( + "net/http" +) + +// Middleware defines a HTTP middleware that can be applied to the AssetServer. +// The handler passed as next is the next handler in the chain. One can decide to call the next handler +// or implement a specialized handling. +type Middleware func(next http.Handler) http.Handler + +// ChainMiddleware allows chaining multiple middlewares to one middleware. +func ChainMiddleware(middleware ...Middleware) Middleware { + return func(h http.Handler) http.Handler { + for i := len(middleware) - 1; i >= 0; i-- { + h = middleware[i](h) + } + return h + } +} diff --git a/v2/pkg/options/assetserver/options.go b/v2/pkg/options/assetserver/options.go new file mode 100644 index 000000000..0be9a1d3e --- /dev/null +++ b/v2/pkg/options/assetserver/options.go @@ -0,0 +1,35 @@ +package assetserver + +import ( + "io/fs" + "net/http" +) + +// Options defines the configuration of the AssetServer. +type Options struct { + // Assets defines the static assets to be used. A GET request is first tried to be served from this Assets. If the Assets returns + // `os.ErrNotExist` for that file, the request handling will fallback to the Handler and tries to serve the GET + // request from it. + // + // If set to nil, all GET requests will be forwarded to Handler. + Assets fs.FS + + // Handler will be called for every GET request that can't be served from Assets, due to `os.ErrNotExist`. Furthermore all + // non GET requests will always be served from this Handler. + // + // If not defined, the result is the following in cases where the Handler would have been called: + // GET request: `http.StatusNotFound` + // Other request: `http.StatusMethodNotAllowed` + Handler http.Handler + + // Middleware is a HTTP Middleware which allows to hook into the AssetServer request chain. It allows to skip the default + // request handler dynamically, e.g. implement specialized Routing etc. + // The Middleware is called to build a new `http.Handler` used by the AssetSever and it also receives the default + // handler used by the AssetServer as an argument. + // + // If not defined, the default AssetServer request chain is executed. + // + // Multiple Middlewares can be chained together with: + // ChainMiddleware(middleware ...Middleware) Middleware + Middleware Middleware +} diff --git a/v2/pkg/options/options.go b/v2/pkg/options/options.go index 25f619b6b..e6ded464a 100644 --- a/v2/pkg/options/options.go +++ b/v2/pkg/options/options.go @@ -8,6 +8,7 @@ import ( "net/http" "runtime" + "github.com/wailsapp/wails/v2/pkg/options/assetserver" "github.com/wailsapp/wails/v2/pkg/options/linux" "github.com/wailsapp/wails/v2/pkg/options/mac" "github.com/wailsapp/wails/v2/pkg/options/windows" @@ -47,9 +48,13 @@ type App struct { AlwaysOnTop bool // BackgroundColour is the background colour of the window // You can use the options.NewRGB and options.NewRGBA functions to create a new colour - BackgroundColour *RGBA - Assets fs.FS - AssetsHandler http.Handler + BackgroundColour *RGBA + // Deprecated: Use AssetServer.Assets instead. + Assets fs.FS + // Deprecated: Use AssetServer.Handler instead. + AssetsHandler http.Handler + // AssetServer configures the Assets for the application + AssetServer *assetserver.Options Menu *menu.Menu Logger logger.Logger `json:"-"` LogLevel logger.LogLevel diff --git a/v2/pkg/templates/base/main.go.tmpl b/v2/pkg/templates/base/main.go.tmpl index a8324d1b8..571cf6b10 100644 --- a/v2/pkg/templates/base/main.go.tmpl +++ b/v2/pkg/templates/base/main.go.tmpl @@ -5,6 +5,7 @@ import ( "github.com/wailsapp/wails/v2" "github.com/wailsapp/wails/v2/pkg/options" +"github.com/wailsapp/wails/v2/pkg/options/assetserver" ) //go:embed frontend/dist @@ -16,10 +17,12 @@ app := NewApp() // Create application with options err := wails.Run(&options.App{ -Title: "{{.ProjectName}}", -Width: 1024, -Height: 768, -Assets: assets, +Title: "{{.ProjectName}}", +Width: 1024, +Height: 768, +AssetServer: &assetserver.Options{ + Assets: assets, +}, BackgroundColour: &options.RGBA{R: 27, G: 38, B: 54, A: 1}, OnStartup: app.startup, Bind: []interface{}{ diff --git a/v2/pkg/templates/generate/plain/main.go.tmpl b/v2/pkg/templates/generate/plain/main.go.tmpl index d13de5a38..9f3e2fffe 100644 --- a/v2/pkg/templates/generate/plain/main.go.tmpl +++ b/v2/pkg/templates/generate/plain/main.go.tmpl @@ -9,6 +9,7 @@ import ( "github.com/wailsapp/wails/v2" "github.com/wailsapp/wails/v2/pkg/logger" "github.com/wailsapp/wails/v2/pkg/options" +"github.com/wailsapp/wails/v2/pkg/options/assetserver" "github.com/wailsapp/wails/v2/pkg/options/windows" ) @@ -37,7 +38,9 @@ Frameless: false, StartHidden: false, HideWindowOnClose: false, BackgroundColour: &options.RGBA{R: 27, G: 38, B: 54, A: 1}, -Assets: assets, +AssetServer: &assetserver.Options{ + Assets: assets, +}, Menu: nil, Logger: nil, LogLevel: logger.DEBUG, diff --git a/v2/pkg/templates/templates/lit-ts/main.go.tmpl b/v2/pkg/templates/templates/lit-ts/main.go.tmpl index e0f197dee..e24782be3 100644 --- a/v2/pkg/templates/templates/lit-ts/main.go.tmpl +++ b/v2/pkg/templates/templates/lit-ts/main.go.tmpl @@ -5,6 +5,7 @@ import ( "github.com/wailsapp/wails/v2" "github.com/wailsapp/wails/v2/pkg/options" + "github.com/wailsapp/wails/v2/pkg/options/assetserver" ) //go:embed all:frontend/dist @@ -16,10 +17,12 @@ func main() { // Create application with options err := wails.Run(&options.App{ - Title: "{{.ProjectName}}", - Width: 1024, - Height: 768, - Assets: assets, + Title: "{{.ProjectName}}", + Width: 1024, + Height: 768, + AssetServer: &assetserver.Options{ + Assets: assets, + }, BackgroundColour: &options.RGBA{R: 27, G: 38, B: 54, A: 1}, OnStartup: app.startup, Bind: []interface{}{ diff --git a/v2/pkg/templates/templates/lit/main.go.tmpl b/v2/pkg/templates/templates/lit/main.go.tmpl index e0f197dee..e24782be3 100644 --- a/v2/pkg/templates/templates/lit/main.go.tmpl +++ b/v2/pkg/templates/templates/lit/main.go.tmpl @@ -5,6 +5,7 @@ import ( "github.com/wailsapp/wails/v2" "github.com/wailsapp/wails/v2/pkg/options" + "github.com/wailsapp/wails/v2/pkg/options/assetserver" ) //go:embed all:frontend/dist @@ -16,10 +17,12 @@ func main() { // Create application with options err := wails.Run(&options.App{ - Title: "{{.ProjectName}}", - Width: 1024, - Height: 768, - Assets: assets, + Title: "{{.ProjectName}}", + Width: 1024, + Height: 768, + AssetServer: &assetserver.Options{ + Assets: assets, + }, BackgroundColour: &options.RGBA{R: 27, G: 38, B: 54, A: 1}, OnStartup: app.startup, Bind: []interface{}{ diff --git a/v2/pkg/templates/templates/plain/main.go.tmpl b/v2/pkg/templates/templates/plain/main.go.tmpl index 09d0b8ea3..847803db6 100644 --- a/v2/pkg/templates/templates/plain/main.go.tmpl +++ b/v2/pkg/templates/templates/plain/main.go.tmpl @@ -5,6 +5,7 @@ import ( "github.com/wailsapp/wails/v2" "github.com/wailsapp/wails/v2/pkg/options" + "github.com/wailsapp/wails/v2/pkg/options/assetserver" ) //go:embed all:frontend/src @@ -16,10 +17,12 @@ func main() { // Create application with options err := wails.Run(&options.App{ - Title: "{{.ProjectName}}", - Width: 1024, - Height: 768, - Assets: assets, + Title: "{{.ProjectName}}", + Width: 1024, + Height: 768, + AssetServer: &assetserver.Options{ + Assets: assets, + }, BackgroundColour: &options.RGBA{R: 27, G: 38, B: 54, A: 1}, OnStartup: app.startup, Bind: []interface{}{ diff --git a/v2/pkg/templates/templates/preact-ts/main.go.tmpl b/v2/pkg/templates/templates/preact-ts/main.go.tmpl index e0f197dee..e24782be3 100644 --- a/v2/pkg/templates/templates/preact-ts/main.go.tmpl +++ b/v2/pkg/templates/templates/preact-ts/main.go.tmpl @@ -5,6 +5,7 @@ import ( "github.com/wailsapp/wails/v2" "github.com/wailsapp/wails/v2/pkg/options" + "github.com/wailsapp/wails/v2/pkg/options/assetserver" ) //go:embed all:frontend/dist @@ -16,10 +17,12 @@ func main() { // Create application with options err := wails.Run(&options.App{ - Title: "{{.ProjectName}}", - Width: 1024, - Height: 768, - Assets: assets, + Title: "{{.ProjectName}}", + Width: 1024, + Height: 768, + AssetServer: &assetserver.Options{ + Assets: assets, + }, BackgroundColour: &options.RGBA{R: 27, G: 38, B: 54, A: 1}, OnStartup: app.startup, Bind: []interface{}{ diff --git a/v2/pkg/templates/templates/preact/main.go.tmpl b/v2/pkg/templates/templates/preact/main.go.tmpl index e0f197dee..e24782be3 100644 --- a/v2/pkg/templates/templates/preact/main.go.tmpl +++ b/v2/pkg/templates/templates/preact/main.go.tmpl @@ -5,6 +5,7 @@ import ( "github.com/wailsapp/wails/v2" "github.com/wailsapp/wails/v2/pkg/options" + "github.com/wailsapp/wails/v2/pkg/options/assetserver" ) //go:embed all:frontend/dist @@ -16,10 +17,12 @@ func main() { // Create application with options err := wails.Run(&options.App{ - Title: "{{.ProjectName}}", - Width: 1024, - Height: 768, - Assets: assets, + Title: "{{.ProjectName}}", + Width: 1024, + Height: 768, + AssetServer: &assetserver.Options{ + Assets: assets, + }, BackgroundColour: &options.RGBA{R: 27, G: 38, B: 54, A: 1}, OnStartup: app.startup, Bind: []interface{}{ diff --git a/v2/pkg/templates/templates/react-ts/main.go.tmpl b/v2/pkg/templates/templates/react-ts/main.go.tmpl index e0f197dee..e24782be3 100644 --- a/v2/pkg/templates/templates/react-ts/main.go.tmpl +++ b/v2/pkg/templates/templates/react-ts/main.go.tmpl @@ -5,6 +5,7 @@ import ( "github.com/wailsapp/wails/v2" "github.com/wailsapp/wails/v2/pkg/options" + "github.com/wailsapp/wails/v2/pkg/options/assetserver" ) //go:embed all:frontend/dist @@ -16,10 +17,12 @@ func main() { // Create application with options err := wails.Run(&options.App{ - Title: "{{.ProjectName}}", - Width: 1024, - Height: 768, - Assets: assets, + Title: "{{.ProjectName}}", + Width: 1024, + Height: 768, + AssetServer: &assetserver.Options{ + Assets: assets, + }, BackgroundColour: &options.RGBA{R: 27, G: 38, B: 54, A: 1}, OnStartup: app.startup, Bind: []interface{}{ diff --git a/v2/pkg/templates/templates/react/main.go.tmpl b/v2/pkg/templates/templates/react/main.go.tmpl index e0f197dee..e24782be3 100644 --- a/v2/pkg/templates/templates/react/main.go.tmpl +++ b/v2/pkg/templates/templates/react/main.go.tmpl @@ -5,6 +5,7 @@ import ( "github.com/wailsapp/wails/v2" "github.com/wailsapp/wails/v2/pkg/options" + "github.com/wailsapp/wails/v2/pkg/options/assetserver" ) //go:embed all:frontend/dist @@ -16,10 +17,12 @@ func main() { // Create application with options err := wails.Run(&options.App{ - Title: "{{.ProjectName}}", - Width: 1024, - Height: 768, - Assets: assets, + Title: "{{.ProjectName}}", + Width: 1024, + Height: 768, + AssetServer: &assetserver.Options{ + Assets: assets, + }, BackgroundColour: &options.RGBA{R: 27, G: 38, B: 54, A: 1}, OnStartup: app.startup, Bind: []interface{}{ diff --git a/v2/pkg/templates/templates/svelte-ts/main.go.tmpl b/v2/pkg/templates/templates/svelte-ts/main.go.tmpl index e0f197dee..e24782be3 100644 --- a/v2/pkg/templates/templates/svelte-ts/main.go.tmpl +++ b/v2/pkg/templates/templates/svelte-ts/main.go.tmpl @@ -5,6 +5,7 @@ import ( "github.com/wailsapp/wails/v2" "github.com/wailsapp/wails/v2/pkg/options" + "github.com/wailsapp/wails/v2/pkg/options/assetserver" ) //go:embed all:frontend/dist @@ -16,10 +17,12 @@ func main() { // Create application with options err := wails.Run(&options.App{ - Title: "{{.ProjectName}}", - Width: 1024, - Height: 768, - Assets: assets, + Title: "{{.ProjectName}}", + Width: 1024, + Height: 768, + AssetServer: &assetserver.Options{ + Assets: assets, + }, BackgroundColour: &options.RGBA{R: 27, G: 38, B: 54, A: 1}, OnStartup: app.startup, Bind: []interface{}{ diff --git a/v2/pkg/templates/templates/svelte/main.go.tmpl b/v2/pkg/templates/templates/svelte/main.go.tmpl index e0f197dee..e24782be3 100644 --- a/v2/pkg/templates/templates/svelte/main.go.tmpl +++ b/v2/pkg/templates/templates/svelte/main.go.tmpl @@ -5,6 +5,7 @@ import ( "github.com/wailsapp/wails/v2" "github.com/wailsapp/wails/v2/pkg/options" + "github.com/wailsapp/wails/v2/pkg/options/assetserver" ) //go:embed all:frontend/dist @@ -16,10 +17,12 @@ func main() { // Create application with options err := wails.Run(&options.App{ - Title: "{{.ProjectName}}", - Width: 1024, - Height: 768, - Assets: assets, + Title: "{{.ProjectName}}", + Width: 1024, + Height: 768, + AssetServer: &assetserver.Options{ + Assets: assets, + }, BackgroundColour: &options.RGBA{R: 27, G: 38, B: 54, A: 1}, OnStartup: app.startup, Bind: []interface{}{ diff --git a/v2/pkg/templates/templates/vanilla-ts/main.go.tmpl b/v2/pkg/templates/templates/vanilla-ts/main.go.tmpl index e0f197dee..e24782be3 100644 --- a/v2/pkg/templates/templates/vanilla-ts/main.go.tmpl +++ b/v2/pkg/templates/templates/vanilla-ts/main.go.tmpl @@ -5,6 +5,7 @@ import ( "github.com/wailsapp/wails/v2" "github.com/wailsapp/wails/v2/pkg/options" + "github.com/wailsapp/wails/v2/pkg/options/assetserver" ) //go:embed all:frontend/dist @@ -16,10 +17,12 @@ func main() { // Create application with options err := wails.Run(&options.App{ - Title: "{{.ProjectName}}", - Width: 1024, - Height: 768, - Assets: assets, + Title: "{{.ProjectName}}", + Width: 1024, + Height: 768, + AssetServer: &assetserver.Options{ + Assets: assets, + }, BackgroundColour: &options.RGBA{R: 27, G: 38, B: 54, A: 1}, OnStartup: app.startup, Bind: []interface{}{ diff --git a/v2/pkg/templates/templates/vanilla/main.go.tmpl b/v2/pkg/templates/templates/vanilla/main.go.tmpl index e0f197dee..e24782be3 100644 --- a/v2/pkg/templates/templates/vanilla/main.go.tmpl +++ b/v2/pkg/templates/templates/vanilla/main.go.tmpl @@ -5,6 +5,7 @@ import ( "github.com/wailsapp/wails/v2" "github.com/wailsapp/wails/v2/pkg/options" + "github.com/wailsapp/wails/v2/pkg/options/assetserver" ) //go:embed all:frontend/dist @@ -16,10 +17,12 @@ func main() { // Create application with options err := wails.Run(&options.App{ - Title: "{{.ProjectName}}", - Width: 1024, - Height: 768, - Assets: assets, + Title: "{{.ProjectName}}", + Width: 1024, + Height: 768, + AssetServer: &assetserver.Options{ + Assets: assets, + }, BackgroundColour: &options.RGBA{R: 27, G: 38, B: 54, A: 1}, OnStartup: app.startup, Bind: []interface{}{ diff --git a/v2/pkg/templates/templates/vue-ts/main.go.tmpl b/v2/pkg/templates/templates/vue-ts/main.go.tmpl index e0f197dee..e24782be3 100644 --- a/v2/pkg/templates/templates/vue-ts/main.go.tmpl +++ b/v2/pkg/templates/templates/vue-ts/main.go.tmpl @@ -5,6 +5,7 @@ import ( "github.com/wailsapp/wails/v2" "github.com/wailsapp/wails/v2/pkg/options" + "github.com/wailsapp/wails/v2/pkg/options/assetserver" ) //go:embed all:frontend/dist @@ -16,10 +17,12 @@ func main() { // Create application with options err := wails.Run(&options.App{ - Title: "{{.ProjectName}}", - Width: 1024, - Height: 768, - Assets: assets, + Title: "{{.ProjectName}}", + Width: 1024, + Height: 768, + AssetServer: &assetserver.Options{ + Assets: assets, + }, BackgroundColour: &options.RGBA{R: 27, G: 38, B: 54, A: 1}, OnStartup: app.startup, Bind: []interface{}{ diff --git a/v2/pkg/templates/templates/vue/main.go.tmpl b/v2/pkg/templates/templates/vue/main.go.tmpl index e0f197dee..e24782be3 100644 --- a/v2/pkg/templates/templates/vue/main.go.tmpl +++ b/v2/pkg/templates/templates/vue/main.go.tmpl @@ -5,6 +5,7 @@ import ( "github.com/wailsapp/wails/v2" "github.com/wailsapp/wails/v2/pkg/options" + "github.com/wailsapp/wails/v2/pkg/options/assetserver" ) //go:embed all:frontend/dist @@ -16,10 +17,12 @@ func main() { // Create application with options err := wails.Run(&options.App{ - Title: "{{.ProjectName}}", - Width: 1024, - Height: 768, - Assets: assets, + Title: "{{.ProjectName}}", + Width: 1024, + Height: 768, + AssetServer: &assetserver.Options{ + Assets: assets, + }, BackgroundColour: &options.RGBA{R: 27, G: 38, B: 54, A: 1}, OnStartup: app.startup, Bind: []interface{}{ diff --git a/website/docs/guides/dynamic-assets.mdx b/website/docs/guides/dynamic-assets.mdx index 07202e0a4..806accf18 100644 --- a/website/docs/guides/dynamic-assets.mdx +++ b/website/docs/guides/dynamic-assets.mdx @@ -19,6 +19,7 @@ import ( "fmt" "github.com/wailsapp/wails/v2" "github.com/wailsapp/wails/v2/pkg/options" + "github.com/wailsapp/wails/v2/pkg/options/assetserver" "net/http" "os" "strings" @@ -54,13 +55,15 @@ func main() { // Create application with options err := wails.Run(&options.App{ - Title: "helloworld", - Width: 1024, - Height: 768, - Assets: assets, + Title: "helloworld", + Width: 1024, + Height: 768, + AssetServer: &assetserver.Options{ + Assets: assets, + Handler: NewFileLoader(), + }, BackgroundColour: &options.RGBA{R: 27, G: 38, B: 54, A: 255}, OnStartup: app.startup, - AssetsHandler: NewFileLoader(), Bind: []interface{}{ app, }, diff --git a/website/docs/guides/frameless.mdx b/website/docs/guides/frameless.mdx index 4d5798474..07d8d2d25 100644 --- a/website/docs/guides/frameless.mdx +++ b/website/docs/guides/frameless.mdx @@ -38,6 +38,7 @@ import ( "github.com/wailsapp/wails/v2" "github.com/wailsapp/wails/v2/pkg/options" + "github.com/wailsapp/wails/v2/pkg/options/assetserver" ) //go:embed all:frontend/dist @@ -49,15 +50,17 @@ func main() { // Create application with options err := wails.Run(&options.App{ - Title: "alwaysontop", - Width: 1024, - Height: 768, - Assets: assets, + Title: "alwaysontop", + Width: 1024, + Height: 768, + AssetServer: &assetserver.Options{ + Assets: assets, + }, Frameless: true, CSSDragProperty: "widows", CSSDragValue: "1", Bind: []interface{}{ - app, + app, }, }) diff --git a/website/docs/guides/migrating.mdx b/website/docs/guides/migrating.mdx index 1fcdcc958..86549707d 100644 --- a/website/docs/guides/migrating.mdx +++ b/website/docs/guides/migrating.mdx @@ -31,7 +31,9 @@ In v2, there is just a single method, `wails.Run()`, that accepts [application o Title: "MyApp", Width: 800, Height: 600, - Assets: assets, + AssetServer: &assetserver.Options{ + Assets: assets, + }, Bind: []interface{}{ basic, }, @@ -165,7 +167,9 @@ var assets embed.FS func main() { err := wails.Run(&options.App{ /* Other Options */ - Assets: assets, + AssetServer: &assetserver.Options{ + Assets: assets, + }, }) } ``` diff --git a/website/docs/howdoesitwork.mdx b/website/docs/howdoesitwork.mdx index 872e847f3..fb23b3dfc 100644 --- a/website/docs/howdoesitwork.mdx +++ b/website/docs/howdoesitwork.mdx @@ -33,6 +33,7 @@ import ( "github.com/wailsapp/wails/v2" "github.com/wailsapp/wails/v2/pkg/options" + "github.com/wailsapp/wails/v2/pkg/options/assetserver" ) //go:embed all:frontend/dist @@ -43,12 +44,14 @@ func main() { app := &App{} err := wails.Run(&options.App{ - Title: "Basic Demo", - Width: 1024, - Height: 768, - Assets: &assets, - OnStartup: app.startup, - OnShutdown: app.shutdown, + Title: "Basic Demo", + Width: 1024, + Height: 768, + AssetServer: &assetserver.Options{ + Assets: assets, + }, + OnStartup: app.startup, + OnShutdown: app.shutdown, Bind: []interface{}{ app, }, @@ -147,6 +150,7 @@ import ( "github.com/wailsapp/wails/v2" "github.com/wailsapp/wails/v2/pkg/options" + "github.com/wailsapp/wails/v2/pkg/options/assetserver" ) //go:embed all:frontend/dist @@ -157,10 +161,12 @@ func main() { app := &App{} err := wails.Run(&options.App{ - Title: "Basic Demo", - Width: 1024, - Height: 768, - Assets: &assets, + Title: "Basic Demo", + Width: 1024, + Height: 768, + AssetServer: &assetserver.Options{ + Assets: assets, + }, Bind: []interface{}{ app, }, @@ -185,10 +191,12 @@ You may bind as many structs as you like. Just make sure you create an instance ```go {8-10} //... err := wails.Run(&options.App{ - Title: "Basic Demo", - Width: 1024, - Height: 768, - Assets: &assets, + Title: "Basic Demo", + Width: 1024, + Height: 768, + AssetServer: &assetserver.Options{ + Assets: assets, + }, Bind: []interface{}{ app, &mystruct1{}, diff --git a/website/docs/reference/options.mdx b/website/docs/reference/options.mdx index 4f2b1227a..baeb92876 100644 --- a/website/docs/reference/options.mdx +++ b/website/docs/reference/options.mdx @@ -10,7 +10,13 @@ The `Options.App` struct contains the application configuration. It is passed to the `wails.Run()` method: ```go title="Example" -import "github.com/wailsapp/wails/v2/pkg/options" +import ( + "github.com/wailsapp/wails/v2/pkg/options" + "github.com/wailsapp/wails/v2/pkg/options/assetserver" + "github.com/wailsapp/wails/v2/pkg/options/linux" + "github.com/wailsapp/wails/v2/pkg/options/mac" + "github.com/wailsapp/wails/v2/pkg/options/windows" +) func main() { @@ -29,8 +35,11 @@ func main() { HideWindowOnClose: false, BackgroundColour: &options.RGBA{R: 0, G: 0, B: 0, A: 255}, AlwaysOnTop: false, - Assets: assets, - AssetsHandler: assetsHandler, + AssetServer: &assetserver.Options{ + Assets: assets, + Handler: assetsHandler, + Middleware: assetsMidldeware, + }, Menu: app.applicationMenu(), Logger: nil, LogLevel: logger.DEBUG, @@ -213,19 +222,20 @@ Type: `bool` ### Assets -The frontend assets to be used by the application. Requires an `index.html` file. - -Name: Assets
-Type: `embed.FS` +Deprecated: Please use Assets on [AssetServer specific options](#assetserver). ### AssetsHandler - +Deprecated: Please use AssetsHandler on [AssetServer specific options](#assetserver). -The assets handler is a generic `http.Handler` which will be called for any non GET request on the assets server -and for GET requests which can not be served from the `assets` because the file is not found. +### AssetServer -| Value | Win | Mac | Lin | +This defines AssetServer specific options. It allows to customize the AssetServer with static assets, serving assets +dynamically with an `http.Handler` or hook into the request chain with an `assetserver.Middleware`. + +Not all features of an `http.Request` are currently supported, please see the following feature matrix: + +| Feature | Win | Mac | Lin | | ----------------------- | --- | --- | --- | | GET | ✅ | ✅ | ✅ | | POST | ✅ | ✅ | ❌ | @@ -239,16 +249,55 @@ and for GET requests which can not be served from the `assets` because the file | Response Headers | ✅ | ✅ | ❌ | | Response Body | ✅ | ✅ | ✅ | | Response Body Streaming | ❌ | ❌ | ✅ | +| WebSockets | ❌ | ❌ | ❌ | NOTE: Linux is currently very limited due to targeting a WebKit2GTK Version < 2.36.0. In the future some features will be supported by the introduction of WebKit2GTK 2.36.0+ support. +Name: AssetServer
+Type: `*assetserver.Options` + +#### Assets + +The static frontend assets to be used by the application. + +A GET request is first tried to be served from this `fs.FS`. If the `fs.FS` returns `os.ErrNotExist` for that file, +the request handling will fallback to the [Handler](#handler) and tries to serve the GET request from it. + +If set to nil, all GET requests will be forwarded to [Handler](#handler). + +Name: Assets
+Type: `fs.FS` + +#### Handler + +The assets handler is a generic `http.Handler` for fallback handling of assets that can't be found. + +The handler will be called for every GET request that can't be served from [Assets](#assets), due to `os.ErrNotExist`. +Furthermore all non GET requests will always be served from this Handler. + +If not defined, the result is the following in cases where the Handler would have been called: +- GET request: `http.StatusNotFound` +- Other request: `http.StatusMethodNotAllowed` + NOTE: When used in combination with a Frontend DevServer there might be limitations, eg. Vite serves the index.html on every path, that does not contain a file extension. Name: AssetsHandler
Type: `http.Handler` +#### Middleware + +Middleware is a HTTP Middleware which allows to hook into the AssetServer request chain. It allows to skip the default +request handler dynamically, e.g. implement specialized Routing etc. +The Middleware is called to build a new `http.Handler` used by the AssetSever and it also receives the default +handler used by the AssetServer as an argument. + +If not defined, the default AssetServer request chain is executed. + +Name: Middleware
+Type: `assetserver.Middleware` + ### Menu The menu to be used by the application. More details about Menus in the [Menu Reference](../reference/runtime/menu.mdx).