使用标准的http包显示自定义的404错误页面

huangapple go评论80阅读模式
英文:

Showing custom 404 error page with standard http package

问题

假设我们有以下代码:

http.HandleFunc("/smth", smthPage)
http.HandleFunc("/", homePage)

当用户尝试错误的URL时,他们会看到一个简单的“404页面未找到”。我该如何返回一个自定义页面来处理这种情况?

关于gorilla/mux的更新

接受的答案对于使用纯net/http包的人来说是可以的。

如果你使用gorilla/mux,你应该像这样使用:

func main() {
    r := mux.NewRouter()
    r.NotFoundHandler = http.HandlerFunc(notFound)
}

并根据你的需要实现func notFound(w http.ResponseWriter, r *http.Request)

英文:

Assuming that we have:

http.HandleFunc("/smth", smthPage)
http.HandleFunc("/", homePage)

User sees a plain "404 page not found" when they try a wrong URL. How can I return a custom page for that case?

Update concerning gorilla/mux

Accepted answer is ok for those using pure net/http package.

If you use gorilla/mux you should use something like this:

func main() {
    r := mux.NewRouter()
    r.NotFoundHandler = http.HandlerFunc(notFound)
}

And implement func notFound(w http.ResponseWriter, r *http.Request) as you want.

答案1

得分: 80

我通常这样做:

package main

import (
    "fmt"
    "net/http"
)

func main() {
    http.HandleFunc("/", homeHandler)
    http.HandleFunc("/smth/", smthHandler)
    http.ListenAndServe(":12345", nil)
}

func homeHandler(w http.ResponseWriter, r *http.Request) {
    if r.URL.Path != "/" {
        errorHandler(w, r, http.StatusNotFound)
        return
    }
    fmt.Fprint(w, "welcome home")
}

func smthHandler(w http.ResponseWriter, r *http.Request) {
    if r.URL.Path != "/smth/" {
        errorHandler(w, r, http.StatusNotFound)
        return
    }
    fmt.Fprint(w, "welcome smth")
}

func errorHandler(w http.ResponseWriter, r *http.Request, status int) {
    w.WriteHeader(status)
    if status == http.StatusNotFound {
        fmt.Fprint(w, "custom 404")
    }
}

这里我简化了代码,只显示了自定义的404页面,但实际上我在这个设置中做了更多事情:我使用errorHandler处理所有的HTTP错误,在其中记录有用的信息并发送邮件给自己。

英文:

I usually do this:

package main

import (
    "fmt"
    "net/http"
)

func main() {
    http.HandleFunc("/", homeHandler)
    http.HandleFunc("/smth/", smthHandler)
    http.ListenAndServe(":12345", nil)
}

func homeHandler(w http.ResponseWriter, r *http.Request) {
    if r.URL.Path != "/" {
        errorHandler(w, r, http.StatusNotFound)
        return
    }
    fmt.Fprint(w, "welcome home")
}

func smthHandler(w http.ResponseWriter, r *http.Request) {
    if r.URL.Path != "/smth/" {
        errorHandler(w, r, http.StatusNotFound)
        return
    }
    fmt.Fprint(w, "welcome smth")
}

func errorHandler(w http.ResponseWriter, r *http.Request, status int) {
    w.WriteHeader(status)
    if status == http.StatusNotFound {
        fmt.Fprint(w, "custom 404")
    }
}

Here I've simplified the code to only show custom 404, but I actually do more with this setup: I handle all the HTTP errors with errorHandler, in which I log useful information and send email to myself.

答案2

得分: 8

以下是我选择的方法。它基于一个代码片段,由于我丢失了浏览器书签,所以无法确认。

示例代码:(我将其放在我的主包中)

type hijack404 struct {
    http.ResponseWriter
    R *http.Request
    Handle404 func (w http.ResponseWriter, r *http.Request) bool
}

func (h *hijack404) WriteHeader(code int) {
	if 404 == code && h.Handle404(h.ResponseWriter, h.R) {
		panic(h)
	}

	h.ResponseWriter.WriteHeader(code)
}

func Handle404(handler http.Handler, handle404 func (w http.ResponseWriter, r *http.Request) bool) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request){
		hijack := &hijack404{ ResponseWriter:w, R: r, Handle404: handle404 }

		defer func() {
			if p:=recover(); p!=nil {
				if p==hijack {
					return
				}
				panic(p)
			}
		}()

		handler.ServeHTTP(hijack, r)
	})
}

func fire404(res http.ResponseWriter, req *http.Request) bool{
	fmt.Fprintf(res, "File not found. Please check to see if your URL is correct.");

	return true;
}

func main(){
	handler_statics := http.StripPrefix("/static/", http.FileServer(http.Dir("/Path_To_My_Static_Files")));

	var v_blessed_handler_statics http.Handler = Handle404(handler_statics, fire404);

    http.Handle("/static/", v_blessed_handler_statics);

    // add other handlers using http.Handle() as necessary

    if err := http.ListenAndServe(":8080", nil); err != nil{
		log.Fatal("ListenAndServe: ", err);
	}
}

请自定义 func fire404 以输出您自己版本的404错误消息。

如果您正在使用Gorilla Mux,您可能希望将主函数替换为以下内容:

func main(){
    handler_statics := http.StripPrefix("/static/", http.FileServer(http.Dir("/Path_To_My_Static_Files")));
    
    var v_blessed_handler_statics http.Handler = Handle404(handler_statics, fire404);

	r := mux.NewRouter();
	r.PathPrefix("/static/").Handler(v_blessed_handler_statics);

    // add other handlers with r.HandleFunc() if necessary...

    http.Handle("/", r);

	log.Fatal(http.ListenAndServe(":8080", nil));
}

如果代码有错误,请友好地纠正,因为我只是Go的新手。谢谢。

英文:

Following is the approach I choose. It is based on a code snippet which I cannot acknowledge since I lost the browser bookmark.

Sample code : (I put it in my main package)

type hijack404 struct {
    http.ResponseWriter
    R *http.Request
    Handle404 func (w http.ResponseWriter, r *http.Request) bool
}

func (h *hijack404) WriteHeader(code int) {
	if 404 == code && h.Handle404(h.ResponseWriter, h.R) {
		panic(h)
	}

	h.ResponseWriter.WriteHeader(code)
}

func Handle404(handler http.Handler, handle404 func (w http.ResponseWriter, r *http.Request) bool) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request){
		hijack := &hijack404{ ResponseWriter:w, R: r, Handle404: handle404 }

		defer func() {
			if p:=recover(); p!=nil {
				if p==hijack {
					return
				}
				panic(p)
			}
		}()

		handler.ServeHTTP(hijack, r)
	})
}

func fire404(res http.ResponseWriter, req *http.Request) bool{
	fmt.Fprintf(res, "File not found. Please check to see if your URL is correct.");

	return true;
}

func main(){
	handler_statics := http.StripPrefix("/static/", http.FileServer(http.Dir("/Path_To_My_Static_Files")));

	var v_blessed_handler_statics http.Handler = Handle404(handler_statics, fire404);

    http.Handle("/static/", v_blessed_handler_statics);

    // add other handlers using http.Handle() as necessary

    if err := http.ListenAndServe(":8080", nil); err != nil{
		log.Fatal("ListenAndServe: ", err);
	}
}

Please customize the func fire404 to output your own version of message for error 404.

If you happen to be using Gorilla Mux, you may wish to replace the main function with below :

func main(){
    handler_statics := http.StripPrefix("/static/", http.FileServer(http.Dir("/Path_To_My_Static_Files")));
    
    var v_blessed_handler_statics http.Handler = Handle404(handler_statics, fire404);

	r := mux.NewRouter();
	r.PathPrefix("/static/").Handler(v_blessed_handler_statics);

    // add other handlers with r.HandleFunc() if necessary...

    http.Handle("/", r);

	log.Fatal(http.ListenAndServe(":8080", nil));
}

Please kindly correct the code if it is wrong, since I am only a newbie to Go. Thanks.

答案3

得分: 3

Ancient thread, but I just made something to intercept http.ResponseWriter, might be relevant here.

package main

//GAE POC originally inspired by https://thornelabs.net/2017/03/08/use-google-app-engine-and-golang-to-host-a-static-website-with-same-domain-redirects.html

import (
	"net/http"
)

func init() {
	http.HandleFunc("/", handler)
}

// HeaderWriter is a wrapper around http.ResponseWriter which manipulates headers/content based on upstream response
type HeaderWriter struct {
	original http.ResponseWriter
	done     bool
}

func (hw *HeaderWriter) Header() http.Header {
	return hw.original.Header()
}

func (hw *HeaderWriter) Write(b []byte) (int, error) {
	if hw.done {
		//Silently let caller think they are succeeding in sending their boring 404...
		return len(b), nil
	}
	return hw.original.Write(b)
}

func (hw *HeaderWriter) WriteHeader(s int) {
	if hw.done {
		//Hmm... I don't think this is needed...
		return
	}
	if s < 400 {
		//Set CC header when status is < 400...
		//TODO: Use diff header if static extensions
		hw.original.Header().Set("Cache-Control", "max-age=60, s-maxage=2592000, public")
	}
	hw.original.WriteHeader(s)
	if s == 404 {
		hw.done = true
		hw.original.Write([]byte("This be custom 404..."))
	}
}

func handler(w http.ResponseWriter, r *http.Request) {
	urls := map[string]string{
		"/example-post-1.html": "https://example.com/post/example-post-1.html",
		"/example-post-2.html": "https://example.com/post/example-post-2.html",
		"/example-post-3.html": "https://example.com/post/example-post-3.html",
	}
	w.Header().Set("Strict-Transport-Security", "max-age=15768000")
	//TODO: Put own logic
	if value, ok := urls[r.URL.Path]; ok {
		http.Redirect(&HeaderWriter{original: w}, r, value, 301)
	} else {
		http.ServeFile(&HeaderWriter{original: w}, r, "static/"+r.URL.Path)
	}
}
英文:

Ancient thread, but I just made something to intercept http.ResponseWriter, might be relevant here.

package main

//GAE POC originally inspired by https://thornelabs.net/2017/03/08/use-google-app-engine-and-golang-to-host-a-static-website-with-same-domain-redirects.html

import (
	&quot;net/http&quot;
)

func init() {
	http.HandleFunc(&quot;/&quot;, handler)
}

// HeaderWriter is a wrapper around http.ResponseWriter which manipulates headers/content based on upstream response
type HeaderWriter struct {
	original http.ResponseWriter
	done     bool
}

func (hw *HeaderWriter) Header() http.Header {
	return hw.original.Header()
}

func (hw *HeaderWriter) Write(b []byte) (int, error) {
	if hw.done {
		//Silently let caller think they are succeeding in sending their boring 404...
		return len(b), nil
	}
	return hw.original.Write(b)
}

func (hw *HeaderWriter) WriteHeader(s int) {
	if hw.done {
		//Hmm... I don&#39;t think this is needed...
		return
	}
	if s &lt; 400 {
		//Set CC header when status is &lt; 400...
		//TODO: Use diff header if static extensions
		hw.original.Header().Set(&quot;Cache-Control&quot;, &quot;max-age=60, s-maxage=2592000, public&quot;)
	}
	hw.original.WriteHeader(s)
	if s == 404 {
		hw.done = true
		hw.original.Write([]byte(&quot;This be custom 404...&quot;))
	}
}

func handler(w http.ResponseWriter, r *http.Request) {
	urls := map[string]string{
		&quot;/example-post-1.html&quot;: &quot;https://example.com/post/example-post-1.html&quot;,
		&quot;/example-post-2.html&quot;: &quot;https://example.com/post/example-post-2.html&quot;,
		&quot;/example-post-3.html&quot;: &quot;https://example.com/post/example-post-3.html&quot;,
	}
	w.Header().Set(&quot;Strict-Transport-Security&quot;, &quot;max-age=15768000&quot;)
	//TODO: Put own logic
	if value, ok := urls[r.URL.Path]; ok {
		http.Redirect(&amp;HeaderWriter{original: w}, r, value, 301)
	} else {
		http.ServeFile(&amp;HeaderWriter{original: w}, r, &quot;static/&quot;+r.URL.Path)
	}
}

答案4

得分: 3

我认为最简洁的方法是这样的:

func main() {

http.HandleFunc("/calculator", calculatorHandler)
http.HandleFunc("/history", historyHandler)
http.HandleFunc("/", notFoundHandler)

log.Fatal(http.ListenAndServe(":80", nil))
}

如果地址不是/calculator或/history,则处理notFoundHandler函数。

英文:

i think the clean way is this:

func main() {

http.HandleFunc(&quot;/calculator&quot;, calculatorHandler)
http.HandleFunc(&quot;/history&quot;, historyHandler)
http.HandleFunc(&quot;/&quot;, notFoundHandler)

log.Fatal(http.ListenAndServe(&quot;:80&quot;, nil))
}

if the address is not /calulator or /history, then it handles notFoundHandler function.

答案5

得分: 2

也许我错了,但我刚刚检查了源代码:http://golang.org/src/pkg/net/http/server.go

似乎很难指定自定义的NotFound()函数:NotFoundHandler()返回一个名为NotFound()的硬编码函数。

可能你应该提交一个问题。

作为一种解决方法,你可以使用你的"/"处理程序,如果没有找到其他处理程序(因为它是最短的),它将作为后备。所以,在该处理程序中检查页面是否存在,并返回自定义的404错误。

英文:

Maybe I'm wrong, but I just checked the sources: http://golang.org/src/pkg/net/http/server.go

It seems like specifying custom NotFound() function is hardly possible: NotFoundHandler() returns a hardcoded function called NotFound().

Probably, you should submit an issue on this.

As a workaround, you can use your "/" handler, which is a fallback if no other handlers were found (as it is the shortest one). So, check is page exists in that handler and return a custom 404 error.

答案6

得分: 2

你只需要创建自己的notFound处理程序,并使用HandleFunc为你不处理的路径进行注册。

如果你想对路由逻辑有最大的控制,你需要使用自定义服务器和自定义处理程序类型。

这样可以让你实现比HandleFunc更复杂的路由逻辑。

英文:

You just need to create your own notFound handler and register it with HandleFunc for the path that you don't handle.

If you want the most control over your routing logic you will need to use a custom server and custom handler type of your own.

This allows you to implement more complex routing logic than the HandleFunc will allow you to do.

答案7

得分: 0

当访问未找到的资源时,它将执行到http.HandleFunc("/", xxx)。

英文:

you can define

http.HandleFunc(&quot;/&quot;, func(writer http.ResponseWriter, request *http.Request) {
	if request.URL.Path != &quot;/&quot; {
		writer.WriteHeader(404)
		writer.Write([]byte(`not found, da xiong dei !!!`))
		return
	}
})

when access not found resource, it will execute to http.HandleFunc("/", xxx)

答案8

得分: 0

简而言之,只需检查r.URL.Path是否与您期望找到的URL匹配,例如通过循环遍历页面数组。通过要求精确的URL匹配,您可以避免@RomaH指出的捕获所有问题。

目前,我将此放在一个名为drawPage的函数中:

is404 := true // 假设最坏的情况

// 开始循环遍历我的所有页面的数据结构
	if (r.URL.Path == data.Options[i].URL) { is404 = false }
// 结束循环

if is404 { // 未通过测试
	w.WriteHeader(404)
	w.Write([]byte(`404 not found`))
	return 
} 

// 绘制我们找到的页面

最后一部分来自@许晨峰

英文:

In short, just check if r.URL.Path matches a URL you're expecting to find, such as by looping through an array of pages. Requiring an exact URL match, you avoid the catchall problem pointed out by @RomaH

For now I'm doing this inside a function called drawPage:

is404 := true // assume the worst

// start loop through a data structure of all my pages
	if (r.URL.Path == data.Options[i].URL) { is404 = false }
// end loop

if is404 { // failed our test
	w.WriteHeader(404)
	w.Write([]byte(`404 not found`))
	return 
} 

// draw the page we found

Last part is taken from @许晨峰

答案9

得分: -1

你可以简单地使用以下代码:

func Handle404(w http.ResponseWriter, r *http.Request) {
	fmt.Fprint(w, "404错误\n")
}

func main(){
   http.HandleFunc("/", routes.Handle404)
}

如果你需要使用标准的404页面,只需写入以下代码:

func main(){
   http.HandleFunc("/", http.NotFound)
}

然后你会得到:

404页面未找到
英文:

You can simply use something like:

func Handle404(w http.ResponseWriter, r *http.Request) {
	fmt.Fprint(w, &quot;404 error\n&quot;)
}

func main(){
   http.HandleFunc(&quot;/&quot;, routes.Handle404)
}

If you need to get the standard one, just write:

func main(){
   http.HandleFunc(&quot;/&quot;, http.NotFound)
}

And you'll get:

404 page not found

huangapple
  • 本文由 发表于 2012年4月3日 23:43:43
  • 转载请务必保留本文链接:https://go.coder-hub.com/9996767.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定