2012-04-03 7 views
47

Unter der Annahme, dass wir haben:Zeige benutzerdefinierte 404-Fehlerseite mit Standard-HTTP-Paket

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

Benutzer sieht einen einfachen „404 Seite nicht gefunden“, wenn sie eine falsche URL versuchen. Wie kann ich eine benutzerdefinierte Seite für diesen Fall zurückgeben?

-Update über Gorilla/Mux

akzeptierte Antwort ist für die Verwendung von reinem net/http Paket ok.

Wenn Sie Gorilla/Mux verwenden, sollten Sie etwas wie folgt verwenden:

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

Und implementieren func notFound(w http.ResponseWriter, r *http.Request) wie Sie wollen.

+3

+1 Danke für die Gorilla/Mux Abkürzung. Könnte mich damit selbst getreten haben. – eduncan911

+0

Ja, es ist immer schön, wenn eine Frage eine Folgefrage vorwegnimmt! –

Antwort

46

ich tun dies in der Regel:

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

Hier habe ich den Code vereinfacht nur benutzerdefinierte 404 zeigen, aber ich habe eigentlich mehr mit diesem Setup: ich alle HTTP-Fehler mit errorHandler handhaben, in denen ich nützliche Informationen protokollieren und E-Mails an mich selbst senden.

+2

Seit mindestens Go 1.7 ist diese Antwort immer noch korrekt, aber "das Muster"/"stimmt mit allen Pfaden überein, die nicht mit anderen registrierten Mustern übereinstimmen". Die bedingte Überprüfung auf errorHandler in diesem Beispiel wäre nur in homeHandler oder "/" erforderlich. – RomaH

+1

@RomaH es sei denn, Sie wollen '/ smth /', aber nicht '/ smth/foo' anpassen, in diesem Fall würde es unter sththHandler fallen, aber keinen Fehler auslösen. – Enrico

0

Vielleicht bin ich falsch, aber ich habe gerade überprüft die Quellen: http://golang.org/src/pkg/net/http/server.go

Es scheint, wie benutzerdefinierte NotFound Angabe() Funktion ist kaum möglich: NotFoundHandler() gibt eine hartcodierte Funktion namens NotFound().

Wahrscheinlich sollten Sie ein Problem zu diesem Thema einreichen.

Als Workaround können Sie Ihren "/" - Handler verwenden, der ein Fallback ist, wenn keine anderen Handler gefunden wurden (wie es der kürzeste ist). Überprüfen Sie also, ob in diesem Handler eine Seite vorhanden ist, und geben Sie einen benutzerdefinierten 404-Fehler zurück.

+0

Ich denke das Gleiche. Ich habe die Quellen überprüft und festgestellt, dass die NotFoundHandler-Funktion einen fest codierten Wert zurückgibt, anstatt dass Sie diesen Handler anpassen können. Ich verstehe nicht, warum so eine Indirektion, um die NotFound-Funktion zu bekommen, wenn sie Sie diese Art von Funktionen nicht anpassen können. –

3

Sie müssen nur Ihren eigenen NotFound-Handler erstellen und ihn mit HandleFunc für den Pfad registrieren, den Sie nicht behandeln.

Wenn Sie die Routing-Logik optimal steuern möchten, müssen Sie einen eigenen Server und einen benutzerdefinierten Handler-Typ verwenden.

Auf diese Weise können Sie komplexere Routing-Logik als die HandleFunc implementieren ermöglicht es Ihnen, zu tun.

0

Folgendes ist der Ansatz, den ich wähle. Es basiert auf einem Code-Snippet, das ich nicht bestätigen kann, seit ich das Browser-Lesezeichen verloren habe.

Beispielcode: (Ich habe es in meinem Haupt-Paket)

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

Bitte passen Sie die func fire404 zur Ausgabe Ihre eigene Version der Meldung Fehler 404.

Wenn Sie werden mit Gorilla Mux passieren, können Sie möchten die Hauptfunktion mit unten ersetzen:

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

Bitte teilen Sie den Code korrigieren, wenn es falsch ist, da ich nur ein Neuling bin zu gehen. Vielen Dank.

1

Ancient Thread, aber ich habe gerade etwas abzufangen http.ResponseWriter, könnte hier relevant sein.

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