将SvelteKit嵌入到Golang二进制文件中

huangapple go评论83阅读模式
英文:

Embedd sveltekit in golang binary

问题

我正在尝试使用embedd来包含SvelteKit网站,并使用Chi作为我的路由器。但是我无法使其工作。我得到以下选项之一。据我了解,embedd的all:选项确保包括以_为前缀的文件。我还尝试了在主要的V1版本中的StripPrefix方法中进行了一些变化:/uibuild/uibuild/等等...

有人可以给我一些指导吗?

示例仓库

  1. 目录列表,对我来说是uibuild
  2. /处是空白页面,但在Chrome控制台上嵌套文件会出现404错误
  3. 在主页/上出现404错误。

Svelte配置:

import preprocess from "svelte-preprocess";
import adapter from "@sveltejs/adapter-static";

/** @type {import('svelte-svelte').Config} */
const config = {
  kit: {
    adapter: adapter({
      pages: "./../server/uibuild",
      assets: "./../server/uibuild",
      fallback: "index.html",
    }),
  },

  preprocess: [
    preprocess({
      postcss: true,
    }),
  ],
};

export default config;

Main.go V1:

这会导致错误3。

package main

import (
	"embed"
	"log"
	"net/http"

	chi "github.com/go-chi/chi/v5"
)

//go:embed all:uibuild
var svelteStatic embed.FS

func main() {

	r := chi.NewRouter()

	r.Handle("/", http.StripPrefix("/uibuild", http.FileServer(http.FS(svelteStatic))))

	log.Fatal(http.ListenAndServe(":8082", r))
}

Main.go V2:

这会导致错误2。

static, err := fs.Sub(svelteStatic, "uibuild")
	if err != nil {
		panic(err)
	}

r := chi.NewRouter()
r.Handle("/", http.FileServer(http.FS(static)))

log.Fatal(http.ListenAndServe(":8082", r))

文件结构:

.
├── go.mod
├── go.sum
├── main.go
└── uibuild
    ├── _app
    │   ├── immutable
    │   │   ├── assets
    │   │   │   ├── 0.d7cb9c3b.css
    │   │   │   └── _layout.d7cb9c3b.css
    │   │   ├── chunks
    │   │   │   ├── index.6dba6488.js
    │   │   │   └── singletons.b716dd01.js
    │   │   ├── entry
    │   │   │   ├── app.c5e2a2d5.js
    │   │   │   └── start.58733315.js
    │   │   └── nodes
    │   │       ├── 0.ba05e72f.js
    │   │       ├── 1.f4999e32.js
    │   │       └── 2.ad52e74a.js
    │   └── version.json
    ├── favicon.png
    └── index.html
英文:

I'm trying to serve a single binary with embedd to include the SvelteKit website. I use Chi as my router. But I cannot get it to work. I'm getting one of these options below. As I understand the embedd all: option makes sure that the files prefixed with _ are included. I also have tried variations in the StripPrefix method in main V1: /uibuild/ or uibuild/ etc...

Can someone shine some light on it?

Sample repo

  1. List of directory(s), in my case `uibuild`
  2. Blank page at "/" but in the chrome console 404's on nested files
  3. 404 on the main page "/".

​

Svelte config:

import preprocess from "svelte-preprocess";
import adapter from "@sveltejs/adapter-static";

/** @type {import('@sveltejs/kit').Config} */
const config = {
  kit: {
    adapter: adapter({
      pages: "./../server/uibuild",
      assets: "./../server/uibuild",
      fallback: "index.html",
    }),
  },

  preprocess: [
    preprocess({
      postcss: true,
    }),
  ],
};

export default config;

​

Main.go V1:

This gives error 3.

package main

import (
	"embed"
	"log"
	"net/http"

	chi "github.com/go-chi/chi/v5"
)

//go:embed all:uibuild
var svelteStatic embed.FS

func main() {

	r := chi.NewRouter()

	r.Handle("/", http.StripPrefix("/uibuild", http.FileServer(http.FS(svelteStatic))))

	log.Fatal(http.ListenAndServe(":8082", r))
}

Main.go V2:

This will give error 2.

static, err := fs.Sub(svelteStatic, "uibuild")
	if err != nil {
		panic(err)
	}

r := chi.NewRouter()
r.Handle("/", http.FileServer(http.FS(static)))

log.Fatal(http.ListenAndServe(":8082", r))

File structure:

.
├── go.mod
├── go.sum
├── main.go
└── uibuild
    ├── _app
    │   ├── immutable
    │   │   ├── assets
    │   │   │   ├── 0.d7cb9c3b.css
    │   │   │   └── _layout.d7cb9c3b.css
    │   │   ├── chunks
    │   │   │   ├── index.6dba6488.js
    │   │   │   └── singletons.b716dd01.js
    │   │   ├── entry
    │   │   │   ├── app.c5e2a2d5.js
    │   │   │   └── start.58733315.js
    │   │   └── nodes
    │   │       ├── 0.ba05e72f.js
    │   │       ├── 1.f4999e32.js
    │   │       └── 2.ad52e74a.js
    │   └── version.json
    ├── favicon.png
    └── index.html

答案1

得分: 1

令人沮丧的是,只需添加一个字符,你的"Main.go V2"就能正常工作。你正在使用以下代码:

r.Handle("/", http.FileServer(http.FS(static)))

根据文档:

func (mx *Mux) Handle(pattern string, handler http.Handler)

每个路由方法都接受一个URL模式和一系列处理程序。URL模式支持命名参数(例如/users/{userID})和通配符(例如/admin/)。可以通过调用chi.URLParam(r, "userID")来在运行时获取URL参数,其中"userID"是命名参数,而chi.URLParam(r, "")是通配符参数。

所以你将"/"作为"pattern"传入;这将匹配"/",但不匹配其他任何内容;要修复这个问题,可以使用以下代码:

r.Handle("/*", http.FileServer(http.FS(static)))
// 或者
r.Mount("/", http.FileServer(http.FS(static)))

我使用其中一个svelte应用程序进行了测试,它可以正常运行。你可能还想考虑的一个改进是将任何请求不存在的文件重定向到"/"(否则,如果用户将带有路径的页面添加到书签中,加载将失败)。有关详细信息,请参见此答案

除了上述内容之外,为了演示我在评论中所说的内容,请在"ui/src/routes/+page.svelte"的末尾添加<a href="/about">About</a>,然后重新构建(同时重新构建svelte和go应用程序)。然后,你将能够导航到"about"页面(首先加载主页面,然后点击"About")。这由客户端路由器处理(因此你可能看不到任何对go服务器的请求)。有关如何在直接访问页面(例如"/about")时使其正常工作的信息,请参见链接的答案

以下是一个快速(有点巧妙)的示例,它将从嵌入的文件系统中提供所需的部分,并对所有其他请求返回主要的"index.html"(以便svelte路由器可以显示所需的页面)。

package main

import (
	"embed"
	"fmt"
	"io/fs"
	"log"
	"net/http"

	"github.com/go-chi/chi/v5"
)

//go:embed all:uibuild
var svelteStatic embed.FS

func main() {

	s, err := fs.Sub(svelteStatic, "uibuild")
	if err != nil {
		panic(err)
	}

	staticServer := http.FileServer(http.FS(s))

	r := chi.NewRouter()

	r.Handle("/", staticServer) // 不是真正需要的(因为默认会处理这个)
	r.Handle("/_app/*", staticServer)      // 需要从嵌入的文件中提供任何应用程序组件
	r.Handle("/favicon.png", staticServer) // 还要提供favicon :-)

	r.HandleFunc("/*", func(w http.ResponseWriter, r *http.Request) { // 其他所有内容都返回index
		r.URL.Path = "/" // 替换请求路径
		staticServer.ServeHTTP(w, r)
	})

	fmt.Println("Running on port: 8082")
	log.Fatal(http.ListenAndServe(":8082", r))
}
英文:

Frustratingly your "Main.go V2" works with the addition of a single character. You are using:

r.Handle(&quot;/&quot;, http.FileServer(http.FS(static)))

From the docs:

>func (mx *Mux) Handle(pattern string, handler http.Handler)

>Each routing method accepts a URL pattern and chain of handlers. The URL pattern supports named params (ie. /users/{userID}) and wildcards (ie. /admin/). URL parameters can be fetched at runtime by calling chi.URLParam(r, "userID") for named parameters and chi.URLParam(r, "") for a wildcard parameter.

So you are passing in "/" as the "pattern"; this will match / but nothing else; to fix use:

r.Handle(&quot;/*&quot;, http.FileServer(http.FS(static)))
// or
r.Mount(&quot;/&quot;, http.FileServer(http.FS(static)))

I tested this with one of my svelte apps and it ran fine. One refinement you might want to consider is redirecting any requests for files that don't exist to / (otherwise if the user bookmarks a page with a path it will fail to load). See this answer for info.

Further to the above - to demonstrate what I'm saying in the comments add &lt;a href=&quot;/about&quot;&gt;About&lt;/a&gt; to the end of ui/src/routes/+page.svelte and rebuild (both svelte, and then go apps). You will then be able to navigate to the about page (first loading the main page then click on "About"). This is handled by the client side router (so your you will probably not see any requests to the go server). See the linked answer for info on how to get this working when directly accessing a page (e.g. /about).

Here is a quick (and bit hacky) example that will serve the needed bits from the embedded file system and return the main index.html for all other requests (so the svelte router can display the needed page).

package main

import (
	&quot;embed&quot;
	&quot;fmt&quot;
	&quot;io/fs&quot;
	&quot;log&quot;
	&quot;net/http&quot;

	&quot;github.com/go-chi/chi/v5&quot;
)

//go:embed all:uibuild
var svelteStatic embed.FS

func main() {

	s, err := fs.Sub(svelteStatic, &quot;uibuild&quot;)
	if err != nil {
		panic(err)
	}

	staticServer := http.FileServer(http.FS(s))

	r := chi.NewRouter()

	r.Handle(&quot;/&quot;, staticServer) // Not really needed (as the default will pick this up)
	r.Handle(&quot;/_app/*&quot;, staticServer)      // Need to serve any app components from the embedded files
	r.Handle(&quot;/favicon.png&quot;, staticServer) // Also serve favicon :-)

	r.HandleFunc(&quot;/*&quot;, func(w http.ResponseWriter, r *http.Request) { // Everything else returns the index
		r.URL.Path = &quot;/&quot; // Replace the request path
		staticServer.ServeHTTP(w, r)
	})

	fmt.Println(&quot;Running on port: 8082&quot;)
	log.Fatal(http.ListenAndServe(&quot;:8082&quot;, r))
}

huangapple
  • 本文由 发表于 2023年6月15日 03:19:34
  • 转载请务必保留本文链接:https://go.coder-hub.com/76476901.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定