honojs / hono

Web framework built on Web Standards
https://hono.dev
MIT License
20.23k stars 579 forks source link

Unable to Serve Static Files (IMPORTANT) #2565

Closed basrioglumehmet closed 6 months ago

basrioglumehmet commented 6 months ago

What version of Hono are you using?

4.2.3

What runtime/platform is your app running on?

Bun

What steps can reproduce the bug?

Start the application. Attempt to access static files using the specified route /static/*.

/* eslint-disable @typescript-eslint/no-unused-vars */
/* eslint-disable @typescript-eslint/no-explicit-any */
import { Handler, Hono } from "hono";

import "reflect-metadata";
import { controllers } from "./api/registerer";
import { MetadataKeys } from "./core/utils/metadatakeys";
import { Router } from "./core/annotations/types/Route";
import showroutes from "./core/utils/showroutes";
import { secureHeaders } from "hono/secure-headers";
import { logger } from 'hono/logger'
import { prettyJSON } from 'hono/pretty-json'
import { html } from "hono/html";
import { serveStatic } from 'hono/bun'
// ! APPLICATON BOOT CLASS
class YouTubeApplication {
  private readonly _instance: Hono;

  get instance(): Hono {
    return this._instance;
  }

  constructor() {
    this._instance = new Hono();
    this.implementSecurity();
    this.registerRouters();
  }

  private implementSecurity() {
    this._instance.use(secureHeaders());
  }

  private registerRouters() {
    this._instance.use(logger())
    this._instance.use(prettyJSON())
    this._instance.use('/static/*', serveStatic({ root: './',onNotFound(path, c) {
      console.log(path)
    }, }))

    this._instance.get('/', (c) => c.text('You can access: /static/hello.txt'))
    this._instance.onError((error,c) => {
      return c.html(html`
          <!DOCTYPE html>
          <html lang="en">
            <head>
              <meta charset="UTF-8" />
              <meta
                name="viewport"
                content="width=device-width, initial-scale=1.0"
              />
              <title>Whitelabel Error Page</title>
              <style>
                body {
                  font-family: Arial, sans-serif;
                  background-color: #f4f4f4;
                  color: #333;
                  margin: 0;
                  padding: 0;
                }
                .container {
                  display: flex;
                  justify-content: start;
                  align-items: start;
                  height: 100vh;
                }
                .content {
                  text-align: left;
                  padding:5rem;
                }
                h1 {
                  font-size: 36px;
                  color: #000; 
                }
                p {
                  font-size: 18px;
                  margin-top: 10px;
                }
              </style>
            </head>
            <body>
              <div class="container">
                <div class="content">
                  <h1>The Whitelabel Error</h1>
                  <p>There was an error processing your request.</p>
                  <p>[${c.req.raw.method}] ${c.req.raw.url}</p>
                  <p>[MESSAGE] ${(error.message as string)}</p>
                </div>
              </div>
            </body>
          </html>
        `)
    })
    controllers.forEach((controller) => {
      const controllerInstance = new (controller as { new (): any })();
      const base_path = Reflect.getMetadata(MetadataKeys.BASE_PATH, controller);
      const routers: Router[] = Reflect.getMetadata(
        MetadataKeys.ROUTERS,
        controller
      );
      this.handleSubRoutes(base_path, routers, controllerInstance);
    });
  }

  private handleSubRoutes(
    base_path: string,
    routers: Router[],
    controllerInstance: { [handleName: string]: Handler }
  ): void {
    routers.forEach((route) => {
      const routeHandler = async (c: any, next: any) => {

          const metadata = Reflect.getMetadata(
            MetadataKeys.ROUTERS,
            controllerInstance.constructor
          );
          // const routeMetadata = metadata.find(
          //   (data: { handlerName: string | symbol; }) => data.handlerName === route.handlerName
          // );

          // Call the route handler method
          const boundHandler =
            controllerInstance[String(route.handlerName)].bind(
              controllerInstance
            );
          const result = await boundHandler(c, next);
          return result;
      };
      this._instance.on(
        route.method,
        base_path.concat(route.path),
        routeHandler
      );
    });
  }
}

showroutes();
const Singleton = new YouTubeApplication().instance;
export default Singleton;

What is the expected behavior?

Static files located in the root directory should be served successfully when accessed via the /static/* route.

What do you see instead?

Instead of successfully serving static files as expected, the application fails to serve the static content. Upon attempting to access static files through the specified route /static/*, the files are not served, and the expected content is not displayed.

Additional information

Despite the inclusion of the serveStatic middleware with appropriate configuration, the static files are not served as expected. Additionally, no specific error message is provided, making it challenging to pinpoint the exact cause of the issue. Further investigation into the middleware configuration, potential conflicts, or errors during initialization may be necessary to resolve the problem with serving static files.

NicoPlyley commented 6 months ago

Where is the location of your static folder. It should be in the root not in src

I tested with this example and had no issues

import { Hono } from 'hono';
import { serveStatic } from "hono/bun";

class TestApp {
  private readonly _instance: Hono;

  get instance(): Hono {
    return this._instance;
  }

  constructor() {
    this._instance = new Hono();
    this.registerRouters();
  }

  private registerRouters() {
    this._instance.use('/static/*', serveStatic({ root: './',onNotFound(path, c) {
        console.log(path)
      }, }))

    this._instance.get('/', (c) => {
      return c.text('Hello World')
    })
  }
}

const app = new TestApp();
export default app.instance;
basrioglumehmet commented 6 months ago

Where is the location of your static folder. It should be in the root not in src

I tested with this example and had no issues

import { Hono } from 'hono';
import { serveStatic } from "hono/bun";

class TestApp {
  private readonly _instance: Hono;

  get instance(): Hono {
    return this._instance;
  }

  constructor() {
    this._instance = new Hono();
    this.registerRouters();
  }

  private registerRouters() {
    this._instance.use('/static/*', serveStatic({ root: './',onNotFound(path, c) {
        console.log(path)
      }, }))

    this._instance.get('/', (c) => {
      return c.text('Hello World')
    })
  }
}

const app = new TestApp();
export default app.instance;

404 Response

image

./ ├── favicon.ico

I did as you instructed and placed the source file in the root directory, but the result was still the same.

[!TIP] Therefore, I devised the following method, and it worked.

    //  this._instance.use('/static/*', serveStatic({ root: './',onNotFound(path, c) {
    //     console.log(path)
    //   }, }))
    this._instance.use("/favicon.ico", async (c) => {
      const file = Bun.file("./favicon.ico");
      return new Response(file.stream(), {
        headers: {
          "Content-Type": "image/x-icon",
        },
      });
    });

image

NicoPlyley commented 6 months ago

So it should be in a folder called static in the root, then it should work

basrioglumehmet commented 6 months ago

So it should be in a folder called static in the root, then it should work

yep, it works now.And, my bad. I knew I should have paid more attention. Closed

NicoPlyley commented 6 months ago

To be fair @basrioglumehmet it could be clearer in the docs

basrioglumehmet commented 6 months ago

To be fair @basrioglumehmet it could be clearer in the docs

yes, it could be more clear. Thanks for helping and explaining @NicoPlyley

NicoPlyley commented 6 months ago

Actually @basrioglumehmet the docs are completely wrong, I'll send an update now