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:
parent
af2a49dbbc
commit
b830c4ef95
@ -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
|
||||
|
@ -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)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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",
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -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),
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -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",
|
||||
},
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user