diff --git a/context_fs_go1.16.go b/context_fs_go1.16.go index 006848f78..77878c6aa 100644 --- a/context_fs_go1.16.go +++ b/context_fs_go1.16.go @@ -15,6 +15,11 @@ func (c *context) File(file string) error { return fsFile(c, file, c.echo.Filesystem) } +// FileFS serves file from given file system. +// +// When dealing with `embed.FS` use `fs := echo.MustSubFS(fs, "rootDirectory") to create sub fs which uses necessary +// prefix for directory path. This is necessary as `//go:embed assets/images` embeds files with paths +// including `assets/images` as their prefix. func (c *context) FileFS(file string, filesystem fs.FS) error { return fsFile(c, file, filesystem) } @@ -28,7 +33,7 @@ func fsFile(c Context, file string, filesystem fs.FS) error { fi, _ := f.Stat() if fi.IsDir() { - file = filepath.ToSlash(filepath.Join(file, indexPage)) + file = filepath.Join(file, indexPage) f, err = filesystem.Open(file) if err != nil { return ErrNotFound diff --git a/context_fs_go1.16_test.go b/context_fs_go1.16_test.go index f209e8a06..027d1c483 100644 --- a/context_fs_go1.16_test.go +++ b/context_fs_go1.16_test.go @@ -4,7 +4,7 @@ package echo import ( - testify "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/assert" "io/fs" "net/http" "net/http/httptest" @@ -62,18 +62,18 @@ func TestContext_File(t *testing.T) { err := handler(c) - testify.Equal(t, tc.expectStatus, rec.Code) + assert.Equal(t, tc.expectStatus, rec.Code) if tc.expectError != "" { - testify.EqualError(t, err, tc.expectError) + assert.EqualError(t, err, tc.expectError) } else { - testify.NoError(t, err) + assert.NoError(t, err) } body := rec.Body.Bytes() if len(body) > len(tc.expectStartsWith) { body = body[:len(tc.expectStartsWith)] } - testify.Equal(t, tc.expectStartsWith, body) + assert.Equal(t, tc.expectStartsWith, body) }) } } @@ -118,18 +118,18 @@ func TestContext_FileFS(t *testing.T) { err := handler(c) - testify.Equal(t, tc.expectStatus, rec.Code) + assert.Equal(t, tc.expectStatus, rec.Code) if tc.expectError != "" { - testify.EqualError(t, err, tc.expectError) + assert.EqualError(t, err, tc.expectError) } else { - testify.NoError(t, err) + assert.NoError(t, err) } body := rec.Body.Bytes() if len(body) > len(tc.expectStartsWith) { body = body[:len(tc.expectStartsWith)] } - testify.Equal(t, tc.expectStartsWith, body) + assert.Equal(t, tc.expectStartsWith, body) }) } } diff --git a/echo_fs_go1.16.go b/echo_fs_go1.16.go index 1b412bc41..435459de2 100644 --- a/echo_fs_go1.16.go +++ b/echo_fs_go1.16.go @@ -16,6 +16,10 @@ import ( type filesystem struct { // Filesystem is file system used by Static and File handlers to access files. // Defaults to os.DirFS(".") + // + // When dealing with `embed.FS` use `fs := echo.MustSubFS(fs, "rootDirectory") to create sub fs which uses necessary + // prefix for directory path. This is necessary as `//go:embed assets/images` embeds files with paths + // including `assets/images` as their prefix. Filesystem fs.FS } @@ -26,12 +30,8 @@ func createFilesystem() filesystem { } // Static registers a new route with path prefix to serve static files from the provided root directory. -func (e *Echo) Static(pathPrefix, root string) *Route { - subFs, err := subFS(e.Filesystem, root) - if err != nil { - // happens when `root` contains invalid path according to `fs.ValidPath` rules and we are unable to create FS - panic(fmt.Errorf("invalid root given to echo.Static, err %w", err)) - } +func (e *Echo) Static(pathPrefix, fsRoot string) *Route { + subFs := MustSubFS(e.Filesystem, fsRoot) return e.Add( http.MethodGet, pathPrefix+"*", @@ -40,11 +40,15 @@ func (e *Echo) Static(pathPrefix, root string) *Route { } // StaticFS registers a new route with path prefix to serve static files from the provided file system. -func (e *Echo) StaticFS(pathPrefix string, fileSystem fs.FS) *Route { +// +// When dealing with `embed.FS` use `fs := echo.MustSubFS(fs, "rootDirectory") to create sub fs which uses necessary +// prefix for directory path. This is necessary as `//go:embed assets/images` embeds files with paths +// including `assets/images` as their prefix. +func (e *Echo) StaticFS(pathPrefix string, filesystem fs.FS) *Route { return e.Add( http.MethodGet, pathPrefix+"*", - StaticDirectoryHandler(fileSystem, false), + StaticDirectoryHandler(filesystem, false), ) } @@ -125,3 +129,17 @@ func subFS(currentFs fs.FS, root string) (fs.FS, error) { } return fs.Sub(currentFs, root) } + +// MustSubFS creates sub FS from current filesystem or panic on failure. +// Panic happens when `fsRoot` contains invalid path according to `fs.ValidPath` rules. +// +// MustSubFS is helpful when dealing with `embed.FS` because for example `//go:embed assets/images` embeds files with +// paths including `assets/images` as their prefix. In that case use `fs := echo.MustSubFS(fs, "rootDirectory") to +// create sub fs which uses necessary prefix for directory path. +func MustSubFS(currentFs fs.FS, fsRoot string) fs.FS { + subFs, err := subFS(currentFs, fsRoot) + if err != nil { + panic(fmt.Errorf("can not create sub FS, invalid root given, err: %w", err)) + } + return subFs +} diff --git a/echo_fs_go1.16_test.go b/echo_fs_go1.16_test.go index 715c69ef3..07e516555 100644 --- a/echo_fs_go1.16_test.go +++ b/echo_fs_go1.16_test.go @@ -18,6 +18,7 @@ func TestEcho_StaticFS(t *testing.T) { name string givenPrefix string givenFs fs.FS + givenFsRoot string whenURL string expectStatus int expectHeaderLocation string @@ -31,6 +32,14 @@ func TestEcho_StaticFS(t *testing.T) { expectStatus: http.StatusOK, expectBodyStartsWith: string([]byte{0x89, 0x50, 0x4e, 0x47}), }, + { + name: "ok, from sub fs", + givenPrefix: "/images", + givenFs: MustSubFS(os.DirFS("./_fixture/"), "images"), + whenURL: "/images/walle.png", + expectStatus: http.StatusOK, + expectBodyStartsWith: string([]byte{0x89, 0x50, 0x4e, 0x47}), + }, { name: "No file", givenPrefix: "/images", @@ -135,7 +144,12 @@ func TestEcho_StaticFS(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { e := New() - e.StaticFS(tc.givenPrefix, tc.givenFs) + + tmpFs := tc.givenFs + if tc.givenFsRoot != "" { + tmpFs = MustSubFS(tmpFs, tc.givenFsRoot) + } + e.StaticFS(tc.givenPrefix, tmpFs) req := httptest.NewRequest(http.MethodGet, tc.whenURL, nil) rec := httptest.NewRecorder() @@ -229,12 +243,12 @@ func TestEcho_StaticPanic(t *testing.T) { { name: "panics for ../", givenRoot: "../assets", - expectError: "invalid root given to echo.Static, err sub ../assets: invalid name", + expectError: "can not create sub FS, invalid root given, err: sub ../assets: invalid name", }, { name: "panics for /", givenRoot: "/assets", - expectError: "invalid root given to echo.Static, err sub /assets: invalid name", + expectError: "can not create sub FS, invalid root given, err: sub /assets: invalid name", }, } diff --git a/group_fs_go1.16.go b/group_fs_go1.16.go index e276c80ca..2ba52b5e2 100644 --- a/group_fs_go1.16.go +++ b/group_fs_go1.16.go @@ -4,27 +4,26 @@ package echo import ( - "fmt" "io/fs" "net/http" ) // Static implements `Echo#Static()` for sub-routes within the Group. -func (g *Group) Static(pathPrefix, root string) { - subFs, err := subFS(g.echo.Filesystem, root) - if err != nil { - // happens when `root` contains invalid path according to `fs.ValidPath` rules and we are unable to create FS - panic(fmt.Errorf("invalid root given to group.Static, err %w", err)) - } +func (g *Group) Static(pathPrefix, fsRoot string) { + subFs := MustSubFS(g.echo.Filesystem, fsRoot) g.StaticFS(pathPrefix, subFs) } // StaticFS implements `Echo#StaticFS()` for sub-routes within the Group. -func (g *Group) StaticFS(pathPrefix string, fileSystem fs.FS) { +// +// When dealing with `embed.FS` use `fs := echo.MustSubFS(fs, "rootDirectory") to create sub fs which uses necessary +// prefix for directory path. This is necessary as `//go:embed assets/images` embeds files with paths +// including `assets/images` as their prefix. +func (g *Group) StaticFS(pathPrefix string, filesystem fs.FS) { g.Add( http.MethodGet, pathPrefix+"*", - StaticDirectoryHandler(fileSystem, false), + StaticDirectoryHandler(filesystem, false), ) } diff --git a/group_fs_go1.16_test.go b/group_fs_go1.16_test.go index 8fabfa1ec..d0caa33db 100644 --- a/group_fs_go1.16_test.go +++ b/group_fs_go1.16_test.go @@ -82,12 +82,12 @@ func TestGroup_StaticPanic(t *testing.T) { { name: "panics for ../", givenRoot: "../images", - expectError: "invalid root given to group.Static, err sub ../images: invalid name", + expectError: "can not create sub FS, invalid root given, err: sub ../images: invalid name", }, { name: "panics for /", givenRoot: "/images", - expectError: "invalid root given to group.Static, err sub /images: invalid name", + expectError: "can not create sub FS, invalid root given, err: sub /images: invalid name", }, }