Хороший способ отключить список каталогов с помощью http.FileServer в Go

Если вы используете http.FileServer в Go, как:

func main() {
    port := flag.String("p", "8100", "port to serve on")
    directory := flag.String("d", ".", "the directory of static file to host")
    flag.Parse()

    http.Handle("/", http.FileServer(http.Dir(*directory)))

    log.Printf("Serving %s on HTTP port: %s\n", *directory, *port)
    log.Fatal(http.ListenAndServe(":"+*port, nil))
}

Затем доступ к каталогу даст вам список файлов. Часто это отключено для веб-сервисов и вместо этого отвечает 404, и я хотел бы, чтобы такое поведение также.

http.FileServer не имеет опций для этого AFAIK, и я видел предложенный способ решить эту проблему здесь https://groups.google.com/forum/ то, что они делают, это обертывание http.FileSystem введите и реализуйте собственный метод Open. Однако это не дает 404, когда путь является каталогом, это просто пустая страница, и неясно, как изменить ее, чтобы приспособить это. Вот что они делают:

type justFilesFilesystem struct {
    fs http.FileSystem
}

func (fs justFilesFilesystem) Open(name string) (http.File, error) {
    f, err := fs.fs.Open(name)
    if err != nil {
        return nil, err
    }
    return neuteredReaddirFile{f}, nil
}

type neuteredReaddirFile struct {
    http.File
}

func (f neuteredReaddirFile) Readdir(count int) ([]os.FileInfo, error) {
    return nil, nil
}

func main() {
    fs := justFilesFilesystem{http.Dir("/tmp/")}
    http.ListenAndServe(":8080", http.FileServer(fs))
}

Примечание: если вы делаете Readdir return nil, os.ErrNotExist тогда вы получите ответ 500 с "Ошибка чтения каталога", а не 404.

Любые идеи о том, как аккуратно представить 404 и при этом сохранить функцию автоматического поиска index.html, если таковой имеется?

1 ответ

Решение

Это поведение может быть изменено, если вы заменяете не Readdir метод, но Stat,
Пожалуйста, посмотрите на рабочий код ниже. Поддерживает подачу index.html файлы, если они находятся внутри запрошенного каталога и возвращает 404 если нет index.html и это каталог.

    package main

    import (
        "io"
        "net/http"
        "os"
    )

    type justFilesFilesystem struct {
        fs               http.FileSystem
        // readDirBatchSize - configuration parameter for `Readdir` func  
        readDirBatchSize int
    }

    func (fs justFilesFilesystem) Open(name string) (http.File, error) {
        f, err := fs.fs.Open(name)
        if err != nil {
            return nil, err
        }
        return neuteredStatFile{File: f, readDirBatchSize: fs.readDirBatchSize}, nil
    }

    type neuteredStatFile struct {
        http.File
        readDirBatchSize int
    }

    func (e neuteredStatFile) Stat() (os.FileInfo, error) {
        s, err := e.File.Stat()
        if err != nil {
            return nil, err
        }
        if s.IsDir() {
        LOOP:
            for {
                fl, err := e.File.Readdir(e.readDirBatchSize)
                switch err {
                case io.EOF:
                    break LOOP
                case nil:
                    for _, f := range fl {
                        if f.Name() == "index.html" {
                            return s, err
                        }
                    }
                default:
                    return nil, err
                }
            }
            return nil, os.ErrNotExist
        }
        return s, err
    }

    func main() {
        fs := justFilesFilesystem{fs: http.Dir("/tmp/"), readDirBatchSize: 2}
        fss := http.FileServer(fs)
        http.ListenAndServe(":8080", fss)
    }
Другие вопросы по тегам