1
0
mirror of https://github.com/labstack/echo.git synced 2025-01-26 03:20:08 +02:00

Improve filesystem support.

This commit is contained in:
toimtoimtoim 2022-01-15 11:04:07 +02:00 committed by Martti T
parent af2a49dbbc
commit b830c4ef95
6 changed files with 68 additions and 32 deletions

View File

@ -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

View File

@ -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)
})
}
}

View File

@ -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
}

View File

@ -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",
},
}

View File

@ -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),
)
}

View File

@ -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",
},
}