2016-09-04 2 views
2

Я использую Google App Engine для обслуживания моего (полу) статического веб-сайта, созданного с помощью Hugo. У меня есть каталог «общедоступный», где хранятся все файлы HTML и их нужно обслуживать. У меня также есть некоторые серверные скрипты для обработки контактной формы, например. Файл app.yaml выглядит так.Как обращаться с ошибкой 404 на GAE с использованием http.FileServer в Golang

// app.yaml 
runtime: go 
api_version: go1 

handlers: 
- url: /.* 
    script: _go_app 
    secure: always 

И упрощенный main.go файла выглядит следующим образом

// main.go 
package main 

import ( 
    "net/http" 
    "encoding/json" 

    "appengine" 
    "appengine/urlfetch" 
) 

func init() { 

    fileHandler := http.FileServer(http.Dir("public")) 
    http.Handle("/", fileHandler) 

    http.HandleFunc("/contactus/", HandleContactus) 
} 

Это прекрасно работает хорошо и служит HTML-файлы. Тем не менее, я рассматриваю решение для обработки случаев, когда страницы не найдены, а ответ - 404 Not Found (или любая другая ошибка сервера).

Моя мысль заключалась в создании пользовательского обработчика, который может быть передан в http.Handle("/", myCustomHandler), и будет обрабатывать ответ сервера и будет перенаправлен на пользовательский 404.html или тому подобное, если необходимо. Я новичок в Go и не могу понять, как это должно быть реализовано. Я также посмотрел на Gorilla Mux, но предпочел бы (если возможно) не использовать внешние библиотеки, чтобы это было просто.

на основе this post, я попытался следующие

package main 

import ( 
    "net/http" 
    "encoding/json" 

    "appengine" 
    "appengine/urlfetch" 
) 

func StaticSiteHandler(h http.Handler) http.Handler { 
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request){ 

    h.ServeHTTP(w, r) 
    }) 
} 


func init() { 

    fileHandler := http.FileServer(http.Dir("public")) 
    http.Handle("/", StaticSiteHandler(fileHandler)) 

    http.HandleFunc("/contactus/", HandleContactus) 
} 

Это решение работает в том смысле, что он также отвечает моим HTML-страницы, однако я до сих пор не могу понять, как обрабатывать коды ответов сервера ,

Любая помощь будет высоко оценена. Спасибо!

ответ

3

Чтобы сохранить промежуточную развязку от http.FileServer как вы окружив его, вы можете передать конкретную реализацию http.ResponseWriter, что будет:

  1. аккумулировать заголовки, в случае, если они нужны чтобы быть угроблены (если он WriteHeader вызывается с 404)
  2. если WriteHeader вызывается с 404:
    1. дис промахи накопили заголовки
    2. отправить обычай 404
    3. игнорировать звонки с Write из обернутого обработчика
  3. если WriteHeader не вызываются, или вызываются с не-404, а затем:
    1. испускают накоплено заголовки к реальному ResponseWriter
    2. маршруту на WriteHeader и Write вызов к реальному ResponseWriter
type notFoundInterceptorWriter struct { 
    rw    http.ResponseWriter // set to nil to signal a 404 has been intercepted 
    h    http.Header   // set to nil to signal headers have been emitted 
    notFoundHandler http.Handler 
    r    *http.Request 
} 

func (rw *notFoundInterceptorWriter) Header() http.Header { 
    if rw.h == nil && rw.rw != nil { 
     return rw.rw.Header() 
    } 
    return rw.h 
} 

func (rw *notFoundInterceptorWriter) WriteHeader(status int) { 
    if status == http.StatusNotFound { 
     rw.notFoundHandler.ServeHTTP(rw.rw, rw.r) 
     rw.rw = nil 
    } else { 
     for k, vs := range rw.h { 
      for _, v := range vs { 
       rw.rw.Header().Add(k, v) 
      } 
     } 
     rw.rw.WriteHeader(status) 
    } 
    rw.h = nil 
} 

func (rw *notFoundInterceptorWriter) Write(b []byte) (int, error) { 
    if rw.rw != nil { 
     return rw.rw.Write(b) 
    } 
    // ignore, so do as if everything was written OK 
    return len(b), nil 
} 

func StaticSiteHandler(h, notFoundHandler http.Handler) http.Handler { 
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 
     w = &notFoundInterceptorWriter{ 
      rw:    w, 
      h:    make(http.Header), 
      notFoundHandler: notFoundHandler, 
      r:    r, 
     } 
     h.ServeHTTP(w, r) 
    }) 
} 
+0

Для будущих читателей, если вам нужно, чтобы поймать ошибки на полпути в рендеринге шаблона, или вы хотите, чтобы изменить подпись обработчика (возвращает ошибку из обработчика, к примеру), я бы полностью рекомендуйте этот подход, чтобы обернуть автоответчик, чтобы добавить к нему пользовательскую логику. – ernestoalejo

+0

Спасибо, Томас Бройер, это решение работает хорошо. Я внедрил notFoundHandler, который просто служит для моего статического файла 404.html. – pascal

0

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

package main 

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

func init() { 
    http.Handle("/", staticHandler) 
} 

func error404Handler(w http.ResponseWriter, r *http.Request) { 
    http.Error(w, "404 not found", http.StatusNotFound) 
} 

func staticHandler(w http.ResponseWriter, r *http.Request) { 
    name := path.Clean(r.URL.Path) 
    if _, err := os.Stat(name); err != nil { 
     if os.IsNotExist(err) { 
      error404Handler(w, r) 
      return 
     } 

     http.Error(w, "internal error", http.StatusInternalServerError) 
     return 
    } 

    return http.ServeFile(w, r, name) 
}