Как настраивать обработку файла, который не был найден при использовании статического файлового сервера go?

Поэтому я использую сервер go для обслуживания одностраничного веб-приложения.

Это работает для обслуживания всех активов на корневом маршруте. Все CSS и HTML обслуживаются правильно.

fs := http.FileServer(http.Dir("build"))
http.Handle("/", fs)

Поэтому, когда URL http://myserverurl/index.html или же http://myserverurl/styles.css, он обслуживает соответствующий файл.

Но для URL, как http://myserverurl/myCustompageкидает 404 если myCustompage это не файл в папке сборки.

Как сделать так, чтобы обслуживались все маршруты, для которых файл не существует index.html?

Это одностраничное веб-приложение, которое будет отображать соответствующий экран после подачи html и js. Но это нужно index.html обслуживаться на маршрутах, для которых нет файлов.

Как это может быть сделано?

1 ответ

Решение

Обработчик вернулся http.FileServer() не поддерживает настройку, не поддерживает предоставление пользовательской страницы 404 или действия.

Что мы можем сделать, это обернуть обработчик, возвращенный http.FileServer() и в нашем обработчике мы можем, конечно, делать все, что захотим. В нашем обработчике оболочки мы будем вызывать обработчик файлового сервера, и если это 404 не найден ответ, мы не отправим его клиенту, а заменим ответом перенаправления.

Чтобы достичь этого, в нашей оболочке мы создаем оболочку http.ResponseWriter который мы передадим обработчику, возвращенному http.FileServer() и в этом автоответчике ответов мы можем проверить код состояния, и если это 404 мы можем действовать, чтобы не отправлять ответ клиенту, а вместо этого отправлять перенаправление на /index.html,

Это пример того, как эта обертка http.ResponseWriter может выглядеть так:

type NotFoundRedirectRespWr struct {
    http.ResponseWriter // We embed http.ResponseWriter
    status              int
}

func (w *NotFoundRedirectRespWr) WriteHeader(status int) {
    w.status = status // Store the status for our own use
    if status != http.StatusNotFound {
        w.ResponseWriter.WriteHeader(status)
    }
}

func (w *NotFoundRedirectRespWr) Write(p []byte) (int, error) {
    if w.status != http.StatusNotFound {
        return w.ResponseWriter.Write(p)
    }
    return len(p), nil // Lie that we successfully written it
}

И завернуть обработчик, возвращенный http.FileServer() может выглядеть так:

func wrapHandler(h http.Handler) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        nfrw := &NotFoundRedirectRespWr{ResponseWriter: w}
        h.ServeHTTP(nfrw, r)
        if nfrw.status == 404 {
            log.Printf("Redirecting %s to index.html.", r.RequestURI)
            http.Redirect(w, r, "/index.html", http.StatusFound)
        }
    }
}

Обратите внимание, что я использовал http.StatusFound перенаправить код состояния вместо http.StatusMovedPermanently так как последний может быть кэширован браузерами, и поэтому, если файл с таким именем будет создан позже, браузер не будет запрашивать его, а отображать index.html немедленно.

А теперь используйте это, main() функция:

func main() {
    fs := wrapHandler(http.FileServer(http.Dir(".")))
    http.HandleFunc("/", fs)
    panic(http.ListenAndServe(":8080", nil))
}

Пытаясь запросить несуществующий файл, мы увидим это в журнале:

2017/11/14 14:10:21 Redirecting /a.txt3 to /index.html.
2017/11/14 14:10:21 Redirecting /favicon.ico to /index.html.

Обратите внимание, что наш пользовательский обработчик (с хорошим поведением) также перенаправил запрос на /favico.ico в index.html потому что у меня нет favico.ico файл в моей файловой системе. Вы можете добавить это как исключение, если у вас его тоже нет.

Полный пример доступен на игровой площадке Go. Вы не можете запустить его там, сохранить его в локальном рабочем пространстве Go и запустить его локально.

Также проверьте этот связанный вопрос: Журнал 404 на http.FileServer

Другие вопросы по тегам