Search code examples
gowails

Issue using wails asset handler using custom router


I am trying to use a custom multiplexer with the Wails asset handler, however when trying to fetch anything I keep getting the index.html page. I have added a print statement at the top of the ServeHTTP function of the multiplexer, but this only gets called once, when fetching favicon.ico at the start of the program.

I have the following main file:

package main

import (
    "embed"
    "fmt"

    "github.com/Nigel2392/router/v3"
    "github.com/Nigel2392/router/v3/request"
    "github.com/wailsapp/wails/v2"
    "github.com/wailsapp/wails/v2/pkg/options"
    "github.com/wailsapp/wails/v2/pkg/options/assetserver"
)

var ROUTER *router.Router = router.NewRouter(true)

func init() {
    ROUTER.Get("/about", func(r *request.Request) {
        fmt.Println("About")
        r.WriteString("About")
    })
}

//go:embed all:frontend/dist
var assets embed.FS

func main() {
    // Create an instance of the app structure
    app := NewApp()

    // Create application with options
    err := wails.Run(&options.App{
        Title:  "new",
        Width:  1024,
        Height: 768,
        AssetServer: &assetserver.Options{
            Assets:  assets,
            Handler: ROUTER,
        },
        BackgroundColour: &options.RGBA{R: 27, G: 38, B: 54, A: 1},
        OnStartup:        app.startup,
        Bind: []interface{}{
            app,
        },
    })

    if err != nil {
        println("Error:", err.Error())
    }
}

I can see the following output in the terminal:

DEB | [ExternalAssetHandler] Loading 'http://localhost:3000/favicon.ico'
DEB | [ExternalAssetHandler] Loading 'http://localhost:3000/favicon.ico' failed, using AssetHandler

# Print statement at the top of the serveHTTP function, 
# prints the requested path, and the available router paths.
Path:  /favicon.ico
GET /about ->

DEB | [ExternalAssetHandler] Loading 'http://localhost:3000/about'


To develop in the browser and call your bound Go methods from Javascript, navigate to: http://localhost:34115
DEB | [ExternalAssetHandler] Loading 'http://localhost:3000/@vite/client'
DEB | [ExternalAssetHandler] Loading 'http://localhost:3000/node_modules/vite/dist/client/env.mjs'
DEB | [ExternalAssetHandler] Loading 'http://localhost:3000/about'
DEB | [ExternalAssetHandler] Loading 'http://localhost:3000/@vite/client'
DEB | [ExternalAssetHandler] Loading 'http://localhost:3000/node_modules/vite/dist/client/env.mjs'
DEB | [ExternalAssetHandler] Loading 'http://localhost:3000/about'

When trying to fetch the about page for testing, as explained in the wails AssetServer documentation, I retrieve the index page:

let resp = await fetch("/about")
undefined
await resp.text()
'<!DOCTYPE html>\n<html lang="en">\n<head>\n  \x3Cscript type="module" src="/@vite/client">\x3C/script>\n\n    <meta charset="UTF-8"/>\n    <meta content="width=device-width, initial-scale=1.0" name="viewport"/>\n    <meta name="wails-options" content="noautoinject" />\n    \x3Cscript src="/wails/ipc.js">\x3C/script>\n    \x3Cscript src="/wails/runtime.js">\x3C/script>\n    <title>new</title>\n</head>\n<body>\n    <h1>Index!</h1>\n    <a href="/about">Go To About!</a>\n</body>\n</html>\n'

Why isn't the serveHTTP function being called?


Solution

  • The log shows that an ExternalAssetHandler is used. That implies that an external frontend dev server is used. And all the asset requests are forwarded to the external frontend dev server first. Only when the external frontend dev server responses with a 404 or 405 status code, the handler specified in assetserver.Options will be used. Nowadays, most frontdend dev server for SPA serves index.html for non-asset requests. That's why you see the behavior.

    A workaround is to configure the external frontend dev server to bypass the request. For Vite, modify the configuration file to add something like this:

    export default defineConfig({
      server: {
        proxy: {
          '/about': {
            bypass: function () {
              // Return false to produce a 404 error for the request.
              return false;
            },
          },
          // or for all the requests that start with "/api/"
          '/api/': {
            bypass: function () {
              return false;
            },
          },
        },
      },
    });
    

    But I would recommend not to do this. Because an asset handler, as its name implies, it's for serving dynamic assets. And wails has its own way for calling bound Go methods. You should give it a try.

    References: