2012-04-03 27 views
47

Giả sử rằng chúng ta có:Hiển thị trang 404 tuỳ chỉnh lỗi với tiêu chuẩn http gói

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

tài nhìn thấy một đồng bằng "trang 404 không tìm thấy" khi họ cố gắng một URL sai. Làm thế nào tôi có thể trả lại một trang tùy chỉnh cho trường hợp đó?

Cập nhật liên quan đến khỉ đột/MUX

câu trả lời chấp nhận là ok cho những người sử dụng net/gói http tinh khiết.

Nếu bạn sử dụng Gorilla/MUX bạn nên sử dụng một cái gì đó như thế này:

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

Và thực hiện func notFound(w http.ResponseWriter, r *http.Request) như bạn muốn.

+3

+1 Cảm ơn vì phím tắt gorilla/mux. Có thể đã đá chính mình với điều đó. – eduncan911

+0

Vâng, nó luôn luôn tốt đẹp khi một câu hỏi dự đoán một câu hỏi tiếp theo! –

Trả lời

46

Tôi thường làm điều này:

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

Ở đây tôi đã đơn giản hóa mã để chỉ hiển thị 404 tuỳ chỉnh, nhưng tôi thực sự làm nhiều hơn với thiết lập này: Tôi xử lý tất cả các lỗi HTTP với errorHandler, trong đó tôi đăng nhập thông tin hữu ích và gửi email cho chính tôi.

+2

Ít nhất là Go 1.7 câu trả lời này vẫn đúng nhưng 'mẫu'/"khớp với tất cả các đường dẫn không khớp với các mẫu đã đăng ký khác '. Việc kiểm tra có điều kiện cho errorHandler trong ví dụ này sẽ chỉ cần thiết trong homeHandler hoặc "/". – RomaH

+1

@RomaH trừ khi bạn muốn khớp với '/ smth /' nhưng không phải '/ smth/foo', trong trường hợp này nó sẽ rơi xuống dưới smthHandler nhưng không gây ra lỗi. – Enrico

0

Có lẽ tôi là sai, nhưng tôi chỉ cần kiểm tra các nguồn: http://golang.org/src/pkg/net/http/server.go

Dường như việc xác định notfound tùy chỉnh() chức năng là hầu như không thể xảy ra: NotFoundHandler() trả về một chức năng mã hóa cứng gọi là notfound().

Có thể, bạn nên gửi một vấn đề về vấn đề này.

Để giải quyết sự cố này, bạn có thể sử dụng trình xử lý "/" của mình, đó là dự phòng nếu không tìm thấy trình xử lý nào khác (vì đây là trình xử lý ngắn nhất). Vì vậy, kiểm tra là trang tồn tại trong trình xử lý đó và trả lại lỗi 404 tùy chỉnh.

+0

Tôi nghĩ như vậy. Tôi đã kết thúc kiểm tra các nguồn và thấy rằng hàm NotFoundHandler trả về một giá trị được mã hóa cứng thay vì cho phép bạn tùy chỉnh trình xử lý đó. Tôi không hiểu tại sao nên vô vọng để có được các chức năng NotFound nếu họ không cho phép bạn tùy chỉnh các loại tính năng. –

3

Bạn chỉ cần tạo trình xử lý notFound của riêng bạn và đăng ký nó bằng HandleFunc cho đường dẫn mà bạn không xử lý.

Nếu bạn muốn kiểm soát nhiều nhất logic định tuyến, bạn sẽ cần sử dụng máy chủ tùy chỉnh và loại trình xử lý tùy chỉnh của riêng bạn.

này cho phép bạn thực hiện logic định tuyến phức tạp hơn so với HandleFunc sẽ cho phép bạn để làm.

0

Sau đây là phương pháp tôi chọn. Nó dựa trên đoạn mã mà tôi không thể xác nhận vì tôi đã mất dấu trang của trình duyệt.

Mẫu mã: (Tôi đặt nó trong gói chính của tôi)

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

hãy tùy chỉnh func fire404 để đầu ra phiên bản riêng của bạn thông điệp cho lỗi 404.

Nếu bạn tình cờ được sử dụng Gorilla Mux, bạn có thể muốn thay thế các chức năng chính với bên dưới:

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

Xin vui lòng sửa mã nếu nó là sai, vì tôi chỉ là một newbie to Go. Cảm ơn.

1

Chủ đề cũ, nhưng tôi vừa tạo ra thứ gì đó để chặn http.ResponseWriter, có thể có liên quan ở đây.

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) 
    } 
} 
Các vấn đề liên quan