如何在Golang中使用http.FileServer处理GAE上的404错误

时间:2016-09-04 15:10:51

标签: google-app-engine go google-app-engine-go

我正在使用Google App Engine来为Hugo生成的(半)静态网站提供服务。我有一个目录" public"所有HTML文件都存储并将被提供的位置。我也有一些服务器端脚本用于联系表单处理。 app.yaml文件看起来像这样。

// 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页面提供服务,但是我仍然无法弄清楚如何处理服务器响应代码。

任何帮助都将受到高度赞赏。 谢谢!

2 个答案:

答案 0 :(得分:3)

为了使中间件与http.FileServer分离,当您将其包装起来时,您可以传递http.ResponseWriter的具体实现:

  1. 累积标题,以防他们需要被抛弃(如果使用404调用WriteHeader
  2. 如果使用404调用WriteHeader
    1. 解除累积标题
    2. 发送自定义404
    3. 忽略来自包装处理程序
    4. Write调用
  3. 如果未调用WriteHeader或使用非404调用,则:
    1. 将累积的标头发送到真实的ResponseWriter
    2. WriteHeaderWrite来电转接至真实ResponseWriter
  4.     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)
        })
    }
    

答案 1 :(得分: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)
}