Как настраивать обработку файла, который не был найден при использовании статического файлового сервера 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