it-swarm.com.de

Golang Was zu benutzen http.ServeFile (..) oder http.FileServer (..)?

Ich bin etwas durcheinander. Viele Beispiele zeigen die Verwendung von beidem: http.ServeFile(..) und http.FileServer(..), aber sie scheinen sehr nahe zu funktionieren. Ich habe auch keine Informationen zum Festlegen von benutzerdefinierten NotFound-Handlern gefunden. 

// This works and strip "/static/" fragment from path
fs := http.FileServer(http.Dir("static"))
http.Handle("/static/", http.StripPrefix("/static/", fs))

// This works too, but "/static2/" fragment remains and need to be striped manually
http.HandleFunc("/static2/", func(w http.ResponseWriter, r *http.Request) {
    http.ServeFile(w, r, r.URL.Path[1:])
})

http.ListenAndServe(":8080", nil)

Ich habe versucht, Quellcode zu lesen, und beide verwenden die serveFile(ResponseWriter, *Request, FileSystem, string, bool) zugrunde liegende Funktion. http.FileServer gibt jedoch fileHandler mit seiner eigenen ServeHTTP()-Methode zurück und macht einige Vorbereitungen, bevor die Datei bereitgestellt wird (z. B. path.Clean ()).

Warum also diese Trennung brauchen? Welche Methode ist besser zu verwenden? Und wie kann ich einen benutzerdefinierten NotFound-Handler festlegen, z. B. wenn die angeforderte Datei nicht gefunden wurde?

31

Der Hauptunterschied besteht darin, dass http.FileServer Ein HTTP-Präfix mit einem Dateisystem praktisch 1: 1 abbildet. Im Klartext wird ein ganzer Verzeichnispfad bereitgestellt. und all seine Kinder.

Angenommen, Sie hatten ein Verzeichnis mit dem Namen /home/bob/static Und hatten dieses Setup:

fs := http.FileServer(http.Dir("/home/bob/static"))
http.Handle("/static/", http.StripPrefix("/static", fs))

Ihr Server würde Anfragen für z. /static/foo/bar Und servieren, was auch immer bei /home/bob/static/foo/bar Ist (oder 404)

Im Gegensatz dazu ist ServeFile ein Helfer auf niedrigerer Ebene, der verwendet werden kann, um etwas zu implementieren, das FileServer ähnelt, oder um Ihr eigenes Path-Munging-Potenzial und eine beliebige Anzahl von Dingen zu implementieren. Es nimmt einfach die angegebene lokale Datei und sendet sie über die HTTP-Verbindung. An sich wird kein ganzes Verzeichnispräfix bereitgestellt (es sei denn, Sie haben einen Handler geschrieben, der eine ähnliche Suche wie FileServer durchführte).

NOTE Ein Dateisystem naiv zu betreiben ist eine potenziell gefährliche Sache (es gibt potenziell Möglichkeiten, aus dem Stammbaum auszubrechen), daher empfehle ich, es sei denn, Sie wirklich wissen, was Sie tun, verwenden Sie http.FileServer und http.Dir, da sie Überprüfungen enthalten, um sicherzustellen, dass die Leute nicht ausbrechen können der FS, die ServeFile nicht.

Nachtrag Ihre zweite Frage, wie Sie einen benutzerdefinierten NotFound-Handler erstellen, ist leider nicht einfach zu beantworten. Da dies, wie Sie bemerkt haben, von der internen Funktion serveFile aufgerufen wird, gibt es keinen besonders einfachen Ort, um darin einzusteigen. Möglicherweise gibt es einige hinterhältige Dinge wie das Abfangen der Antwort mit Ihrem eigenen ResponseWriter, das den 404-Antwortcode abfängt, aber ich überlasse diese Übung Ihnen.

63
Crast

Hier ein Handler, der eine Weiterleitung an "/" sendet, wenn die Datei nicht gefunden wird. Dies ist praktisch, wenn Sie ein Fallback für eine Angular-Anwendung hinzufügen, wie dies hier vorgeschlagen wird, das von einem Golang-Service aus bedient wird.

Hinweis: Dieser Code ist nicht produktionsbereit. Nur zur Veranschaulichung (am besten :-)

    package main

    import "net/http"

    type (
        // FallbackResponseWriter wraps an http.Requesthandler and surpresses
        // a 404 status code. In such case a given local file will be served.
        FallbackResponseWriter struct {
            WrappedResponseWriter http.ResponseWriter
            FileNotFound          bool
        }
    )

    // Header returns the header of the wrapped response writer
    func (frw *FallbackResponseWriter) Header() http.Header {
        return frw.WrappedResponseWriter.Header()
    }

    // Write sends bytes to wrapped response writer, in case of FileNotFound
    // It surpresses further writes (concealing the fact though)
    func (frw *FallbackResponseWriter) Write(b []byte) (int, error) {
        if frw.FileNotFound {
            return len(b), nil
        }
        return frw.WrappedResponseWriter.Write(b)
    }

    // WriteHeader sends statusCode to wrapped response writer
    func (frw *FallbackResponseWriter) WriteHeader(statusCode int) {
        Log.Printf("INFO: WriteHeader called with code %d\n", statusCode)
        if statusCode == http.StatusNotFound {
            Log.Printf("INFO: Setting FileNotFound flag\n")
            frw.FileNotFound = true
            return
        }
        frw.WrappedResponseWriter.WriteHeader(statusCode)
    }

    // AddFallbackHandler wraps the handler func in another handler func covering authentication
    func AddFallbackHandler(handler http.HandlerFunc, filename string) http.HandlerFunc {
        Log.Printf("INFO: Creating fallback handler")
        return func(w http.ResponseWriter, r *http.Request) {
            Log.Printf("INFO: Wrapping response writer in fallback response writer")
            frw := FallbackResponseWriter{
                WrappedResponseWriter: w,
                FileNotFound:          false,
            }
            handler(&frw, r)
            if frw.FileNotFound {
                Log.Printf("INFO: Serving fallback")
                http.Redirect(w, r, "/", http.StatusSeeOther)
            }
        }
    }

Es kann wie in diesem Beispiel hinzugefügt werden (mit goji als Mux):

    mux.Handle(pat.Get("/*"),
        AddFallbackHandler(http.FileServer(http.Dir("./html")).ServeHTTP, "/"))

0
Oliver Koeth