为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

Go
慕莱坞森 2022-03-07 22:34:13
我正在使用 Google App Engine 来服务我用 Hugo 生成的(半)静态网站。我有一个“公共”目录,所有的 HTML 文件都在其中存储并提供。例如,我还有一些用于处理联系表单的服务器端脚本。该app.yaml文件看起来像这样。// app.yamlruntime: goapi_version: go1handlers:- url: /.*  script: _go_app  secure: always简化的main.go文件看起来像这样// main.gopackage mainimport (   "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 mainimport (   "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 回答

?
蛊毒传说

TA贡献1895条经验 获得超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)

    })

}


查看完整回答
反对 回复 2022-03-07
?
富国沪深

TA贡献1790条经验 获得超9个赞

您可以在提供文件之前统计文件以查看它是否存在。根据需要调整 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)

}


查看完整回答
反对 回复 2022-03-07
  • 2 回答
  • 0 关注
  • 436 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信