2

我正在使用 Google App Engine 来服务我用 Hugo 生成的(半)静态网站。我有一个“公共”目录,所有的 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,但希望(如果可能的话)不使用外部库来保持简单。

基于这篇文章,我尝试了以下

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 页面提供服务,但是我仍然不知道如何处理服务器响应代码。

任何帮助将不胜感激。谢谢!

4

2 回答 2

3

为了使中间件与 . 解耦,http.FileServer在包装它时,您可以传递一个特定的实现http.ResponseWriter

  1. 累积标头,以防它们需要被丢弃(如果WriteHeader使用 404 调用)
  2. 如果WriteHeader使用 404 调用:
    1. 取消累积的标题
    2. 发送自定义 404
    3. 忽略Write来自包装处理程序的调用
  3. 如果WriteHeader未调用,或使用非 404 调用,则:
    1. 将累积的标头发送到真实的ResponseWriter
    2. WriteHeader将andWrite呼叫路由到真实的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)
    })
}
于 2016-09-20T16:36:34.830 回答
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)
}
于 2016-09-20T13:57:04.720 回答