Обслуживание карты строк без сторонних библиотек с использованием http.filesystem в Go (Golang) - PullRequest
0 голосов
/ 11 сентября 2018

Я только начинаю с Go и пытаюсь научиться создавать простое веб-приложение без использования сторонних библиотек / пакетов.

Используя этот пост и этот код в качестве руководства, я взломал следующее вместе:

package main

import (
    "bytes"
    "net/http"
    "os"
    "path"
    "time"
)

type StaticFS map[string]*staticFile

type staticFile struct {
    name string
    data []byte
    fs   StaticFS
}

func LoadAsset(name string, data string, fs StaticFS) *staticFile {
    return &staticFile{name: name,
        data: []byte(data),
        fs:   fs}
}

func (fs StaticFS) prepare(name string) (*staticFile, error) {
    f, present := fs[path.Clean(name)]
    if !present {
        return nil, os.ErrNotExist
    }
    return f, nil
}

func (fs StaticFS) Open(name string) (http.File, error) {
    f, err := fs.prepare(name)
    if err != nil {
        return nil, err
    }
    return f.File()
}

func (f *staticFile) File() (http.File, error) {
    type httpFile struct {
        *bytes.Reader
        *staticFile
    }
    return &httpFile{
        Reader:     bytes.NewReader(f.data),
        staticFile: f,
    }, nil
}

//implement the rest of os.FileInfo
func (f *staticFile) Close() error {
    return nil
}

func (f *staticFile) Stat() (os.FileInfo, error) {
    return f, nil
}

func (f *staticFile) Readdir(count int) ([]os.FileInfo, error) {
    return nil, nil
}

func (f *staticFile) Name() string {
    return f.name
}

func (f *staticFile) Size() int64 {
    return int64(len(f.data))
}

func (f *staticFile) Mode() os.FileMode {
    return 0
}

func (f *staticFile) ModTime() time.Time {
    return time.Time{}
}

func (f *staticFile) IsDir() bool {
    return false
}

func (f *staticFile) Sys() interface{} {
    return f
}

func main() {

    const HTML = `<!DOCTYPE html>
<html lang="en">
<head>
<title>Test</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<main>
<p>Hello World</p>
</main>
</body>
</html>
`

    const CSS = `
p {
    color:red;
    text-align:center;
} 
`
    ASSETS := make(StaticFS)
    ASSETS["index.html"] = LoadAsset("index.html", HTML, ASSETS)
    ASSETS["style.css"] = LoadAsset("style.css", CSS, ASSETS)
    http.Handle("/", http.FileServer(ASSETS))
    http.ListenAndServe(":8080", nil)
}

Что компилируется нормально, нофактически не дает никаких результатов, кроме 404 страницы, не найденной ..

Чего я хочу добиться, так это наличия в моем приложении пакета, который позволяет мне создавать карту, вставлять некоторый статический контент, такой как css и jsв нем, а затем подайте его с помощью http.Handle - без использования сторонних инструментов, таких как go-bindata, рис или что-либо еще.

Любая помощь будет принята с благодарностью ..

1 Ответ

0 голосов
/ 11 сентября 2018

Вот основной код, который нам нужно посмотреть, который исходит из источника относительно http.FileServer:

func (f *fileHandler) ServeHTTP(w ResponseWriter, r *Request) {
    upath := r.URL.Path
    if !strings.HasPrefix(upath, "/") {
        upath = "/" + upath
        r.URL.Path = upath
    }
    serveFile(w, r, f.root, path.Clean(upath), true)
}

// name is '/'-separated, not filepath.Separator.
func serveFile(w ResponseWriter, r *Request, fs FileSystem, name string, redirect bool) {
    const indexPage = "/index.html"

    // redirect .../index.html to .../
    // can't use Redirect() because that would make the path absolute,
    // which would be a problem running under StripPrefix
    if strings.HasSuffix(r.URL.Path, indexPage) {
        localRedirect(w, r, "./")
        return
    }

    f, err := fs.Open(name)
    if err != nil {
        msg, code := toHTTPError(err)
        Error(w, msg, code)
        return
    }
    defer f.Close()

    ...
}

В методе ServeHTTP вы увидите вызов неэкспортированной функции.

serveFile(w, r, f.root, path.Clean(upath), true)

, где upath - путь URL запроса, который гарантированно начинается с "/".

В serveFile, вызывается fs.Open(name), где fsFileSystem вы указали, а name - аргумент, который мы передали как path.Clean(upath).Обратите внимание, что path.Clean уже вызывается, поэтому вам не нужно вызывать его в вашем методе prepare.

Вывод здесь заключается в том, что вы сохраняете свои "имена файлов" без предшествующего "/", что означает, что они находятся в корне файловой системы.

Вы можете исправить это двумя различными способами.

1.

ASSETS["/index.html"] = LoadAsset("index.html", HTML, ASSETS)
ASSETS["/style.css"] = LoadAsset("style.css", CSS, ASSETS)

2.

func (fs StaticFS) Open(name string) (http.File, error) {
    if strings.HasPrefix(name, "/") {
        name = name[1:]
    }
    f, err := fs.prepare(name)
    if err != nil {
        return nil, err
    }
    return f.File()
}
...