Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Question: How can I serve content from static HTML files using URLs that have no '.html' extension #2610

Closed
3 tasks done
minherz opened this issue Mar 19, 2024 · 5 comments
Closed
3 tasks done

Comments

@minherz
Copy link

minherz commented Mar 19, 2024

Issue Description

I have a website that should show two static pages: "Home" and "Dashboard".
The pages are stored in two files under /static folder:

  • /static/index.html
  • /static/dashboards.html

When I run the server at locahost:1313 by opening http://localhost:1313/ I can see index.html.
When I open http://localhost:1313/dashboards.html I can see dashboards.html.
However, when I open http://localhost:1313/dashboards I see index.html again.

Checklist

  • Dependencies installed
  • No typos
  • Searched existing issues and docs

Expected behaviour

I would expect that the request for text/html content with URL that does not contains .html extension but matching the path otherwise, will show the static HTML file.

Actual behaviour

The server returns index.html page.

Steps to reproduce

The server is initialized using the following code:

	e := echo.New()
	e.Use(
		middleware.Logger(),
		middleware.CORSWithConfig(
			middleware.CORSConfig{
				AllowOrigins: []string{"*"},
			},
		),
		middleware.GzipWithConfig(middleware.GzipConfig{
			Level: 5,
		}),
		middleware.Secure(),
		middleware.StaticWithConfig(middleware.StaticConfig{
			Root:  "static",
			HTML5: true,
		}),
	)
	e.IPExtractor = echo.ExtractIPFromXFFHeader(
		echo.TrustLoopback(false),   // e.g. ipv4 start with 127.
		echo.TrustLinkLocal(false),  // e.g. ipv4 start with 169.254
		echo.TrustPrivateNet(false), // e.g. ipv4 start with 10. or 192.168
	)

The static content is stored in the static folder that is in the same folder as the Go file of the program.

Version/commit

Go version 1.22
Echo version 4.11.4

@aldas
Copy link
Contributor

aldas commented Mar 19, 2024

This is not how static middleware is indented to work. If you need this logic I suggest taking https://github.com/labstack/echo/blob/master/middleware/static.go that middleware code and strip it only to logic necessary for your requirements (only block related to configuration flags that you use) and add retry for .html

@aldas
Copy link
Contributor

aldas commented Mar 19, 2024

ps. if you are not building an API and only need to serve static files I think using Nginx, Apache, Caddy etc would be more suitable.

@minherz
Copy link
Author

minherz commented Mar 20, 2024

ps. if you are not building an API and only need to serve static files I think using Nginx, Apache, Caddy etc would be more suitable.

No, I have handler that serving API backend calls. And I am no claiming that the current behavior is a bug (hence the "Question:" in the title). I just want to understand how it should be implemented following the design ideas of Echo.

I tried to use Echo#Static and Echo#File that look like the methods designed to address this function. But these calls do not change the behavior.

If you need this logic I suggest taking https://github.com/labstack/echo/blob/master/middleware/static.go that middleware code and strip it only to logic necessary for your requirements (only block related to configuration flags that you use) and add retry for .html

Are you proposing to wrap or replace StaticWithConfig call with code that serves the described logic? What the above method Echo#File and Echo#Static are for? Can they be used with StaticWithConfig?

@aldas
Copy link
Contributor

aldas commented Mar 21, 2024

You might want to try this. This mw tries ".html" as fallback. it is not designed to be added to specific group or handler.

func myStaticMw(next echo.HandlerFunc) echo.HandlerFunc {
	mwFs := http.Dir("./")
	rootFolder := "_fixture"
	
	rootIndex := path.Join(rootFolder, "index.html")


	return func(c echo.Context) error {
		if c.Path() != "" {
			return next(c)
		}

		p, err := url.PathUnescape(c.Request().URL.Path)
		if err != nil {
			return err
		}
		name := path.Join(rootFolder, path.Clean("./"+p))
		file, err := mwFs.Open(name)
		if err != nil {
			if !os.IsNotExist(err) {
				return err
			}
			if !strings.HasSuffix(name, ".html") {
				file, err = mwFs.Open(name + ".html")
			}
		}
		if err != nil {
			if !os.IsNotExist(err) {
				return err
			}
			file, err = mwFs.Open(rootIndex)
			if err != nil {
				return err
			}
		}
		defer file.Close()

		info, err := file.Stat()
		if err != nil {
			return err
		}

		if info.IsDir() {
			index, err := mwFs.Open(path.Join(name, "index.html"))
			if err != nil {
				// FIXME: listing files in directory is not implemented
				return next(c)
			}

			defer index.Close()

			info, err = index.Stat()
			if err != nil {
				return err
			}

			http.ServeContent(c.Response(), c.Request(), info.Name(), info.ModTime(), index)
			return nil
		}

		http.ServeContent(c.Response(), c.Request(), info.Name(), info.ModTime(), file)
		return nil
	}
}

func main() {
	e := echo.New()

	e.Use(middleware.Logger())
	e.Use(middleware.Recover())
	e.Use(myStaticMw)

	e.GET("/api/*", func(c echo.Context) error {
		return c.JSON(http.StatusOK, []string{c.Path()})
	})

	s := http.Server{
		Addr:    ":8080",
		Handler: e,
	}

	if err := s.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
		log.Fatal(err)
	}
}

@minherz
Copy link
Author

minherz commented Mar 23, 2024

Thank you. It looks like the recommended way is to replace the currently used WithStaticConfig middleware with a customized version.

Thank you. Given this is the recommended approach, I will close the issue. Would you mind to comment here about the use cases for Echo#Static and Echo#File functions?

@minherz minherz closed this as completed Mar 23, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants