drhg24275 2016-09-04 15:10
浏览 288
已采纳

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

I am using Google App Engine to serve my (semi-)static website generated with Hugo. I have a directory "public" where all the HTML files are stored and are to be served. I also have some server-side scripts for the contact form handling for example. The app.yaml file looks like this.

// app.yaml
runtime: go
api_version: go1

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

And the simplified main.go file looks like this

// 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)
}

This works perfectly well and serves the html files. However, I am looking at a solution to handle the cases where the pages are not found and the response is 404 Not Found for example (or any other server error).

My thought was to create a custom handler which can be passed in the http.Handle("/", myCustomHandler) and would handle the server response and would redirect to a custom 404.html or the like if necessary. I am new to Go and can't seem to figure out how this should be implemented. I have also looked at the Gorilla Mux, but would prefer (if possible) not to use external libraries to keep it simple.

Based on this post, I have tried the following

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)
}

This solution works in the sense that it also does serve my HTML pages, however I still can't figure out how to handle the server response codes.

Any help would be highly appreciated. Thanks!

  • 写回答

2条回答 默认 最新

  • dongyinting3179 2016-09-20 16:36
    关注

    To keep the middleware decoupled from the http.FileServer, as you're wrapping it, you can pass a specific implementation of http.ResponseWriter that will:

    1. accumulate headers, in case they'd need to be ditched away (if WriteHeader is called with a 404)
    2. if WriteHeader is called with a 404:
      1. dismiss accumulated headers
      2. send a custom 404
      3. ignore calls with Write from the wrapped handler
    3. if WriteHeader is not called, or called with a non-404, then:
      1. emit accumulated headers to the real ResponseWriter
      2. route the WriteHeader and Write calls to the real 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)
        })
    }
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥15 如何能达到用ping0.cc检测成这样?如图
  • ¥15 关于#DMA固件#的问题,请各位专家解答!
  • ¥15 matlab生成的x1图不趋于稳定,之后的图像是稳定的水平线
  • ¥15 请问华为OD岗位的内部职业发展通道都有哪些,以及各个级别晋升的要求
  • ¥20 微信小程序 canvas 问题
  • ¥15 系统 24h2 专业工作站版,浏览文件夹的图库,视频,图片之类的怎样删除?
  • ¥15 怎么把512还原为520格式
  • ¥15 MATLAB的动态模态分解出现错误,以CFX非定常模拟结果为快照
  • ¥15 求高通平台Softsim调试经验
  • ¥15 canal如何实现将mysql多张表(月表)采集入库到目标表中(一张表)?