doujiufutaog59220
doujiufutaog59220
2017-07-02 02:49

如何使用Go服务动态创建的URL路径?

已采纳

I am using react-router and browserHistory's pushState in a reactjs project. This project lets a user create a note which creates a new path. To serve this type of site I need to serve the same HTML file to every path besides the static content. So my nodejs code looks like this.

// Serve the static content
app.use('/static/css/', express.static(path.join(__dirname, '../../react-ui/build/static/css')));
app.use('/static/js/', express.static(path.join(__dirname, '../../react-ui/build/static/js')));
app.use('/static/media/', express.static(path.join(__dirname, '../../react-ui/build/static/media')));
app.use('/static/img/', express.static(path.join(__dirname, '../../react-ui/build/static/img')));
app.use('/img/', express.static(path.join(__dirname, '../../react-ui/build/img')));

// Serve the same HTML file to everything else
app.use('*', express.static(path.join(__dirname, '../../react-ui/build'))); 

I don't see any wildcard support for the Go FileServer. Currently I have all the static pages served using Go code similar to this.

package main

import (
    "net/http"
)

func init(){
    fs := http.FileServer(http.Dir("web"))
    http.Handle("/", fs)
    http.Handle("/static-page-1/", http.StripPrefix("/static-page-1/", fs))
    http.Handle("/static-page-2/", http.StripPrefix("/static-page-2/", fs))
    http.Handle("/static-page-3/", http.StripPrefix("/static-page-3/", fs))
}

Is it possible to serve content to dynamically generated URL paths with a Go server?

If the Handle method supported variables then I'd write the code like this

fs := http.FileServer(http.Dir("web"))
http.Handle("/static/", fs)
http.Handle("/{unknownUserPath}", http.StripPrefix("/{unknownUserPath}", fs))

{unknownUserPath} would be any path that a user types in that is not under /static/ path.

Here's the go project structure

enter image description here

Here's the server based on @putu answer

package main

import (
    "net/http"
    "strings"
)

func adaptFileServer(fs http.Handler) http.Handler {
    fn := func(w http.ResponseWriter, req *http.Request) {
        staticIndex := strings.Index(req.URL.Path, "/static/");
        imgIndex := strings.Index(req.URL.Path, "/img/");

        if staticIndex == -1 && imgIndex == -1 {
            fsHandler := http.StripPrefix(req.URL.Path, fs)
            fsHandler.ServeHTTP(w, req)
        } else {
            fs.ServeHTTP(w, req)
        }
    }
    return http.HandlerFunc(fn)
}

func init() {
    fs := http.FileServer(http.Dir("web"))
    http.Handle("/", adaptFileServer(fs))
}
  • 点赞
  • 写回答
  • 关注问题
  • 收藏
  • 复制链接分享
  • 邀请回答

3条回答

  • dotif64826 dotif64826 4年前

    If you want to serve static contents with URL pattern /* to a specific directory, then use the answer provided by jeevatkm.

    If you need slightly customizable version, you need a kind of adapter that map the URL path to static file handler (http.FileServer). The example code looks like:

    package main
    
    import (
        "log"
        "net/http"
        "regexp"
    )
    
    func helloHandler(w http.ResponseWriter, r *http.Request) {
        w.Write([]byte("Hello world!"))
    }
    
    func adaptFileServer(fs http.Handler, mux http.Handler) http.Handler {
        fn := func(w http.ResponseWriter, req *http.Request) {
            //Use your Path matcher here.
            //For demonstration, REGEX match is used
            //and it's probably not the most efficient.
            staticRegex := regexp.MustCompile("^/static-page-[0-9]+/")
            if matches := staticRegex.FindStringSubmatch(req.URL.Path); matches != nil {
                log.Printf("Match: %v, %v", req.URL.Path, matches[0])
                fsHandler := http.StripPrefix(matches[0], fs)
                fsHandler.ServeHTTP(w, req)
            } else if mux != nil {
                log.Printf("Doesn't match, pass to other MUX: %v", req.URL.Path)
                mux.ServeHTTP(w, req)
            } else {
                http.Error(w, "Page Not Found", http.StatusNotFound)
            }
        }
        return http.HandlerFunc(fn)
    }
    
    func init() {
        //Usual routing definition with MUX
        mux := http.NewServeMux()
        mux.HandleFunc("/hello", helloHandler)
    
        //"Dynamic" static file server.
        fs := http.FileServer(http.Dir("web"))
        http.Handle("/", adaptFileServer(fs, mux))
    }
    
    func main() {
        log.Fatal(http.ListenAndServe(":8080", nil))
    }
    

    In the above adapter example, if request path match to a specific pattern (/static-page-*/ in the above example), it will be passed to http.FileServer. If doesn't match, and if a multiplexer is specified, it will call mux.ServeHTTP. Otherwise it will return 404 error.

    If you want another matching rule, just change the regex pattern (or use your custom matcher).

    Note:
    Please don't use a same handler instance for FileServer and mux. For example, when you call http.Handle, it use http.DefaultServeMux to handle routing. If you pass http.DefaultServeMux as the second argument of adaptFileServer you may end up with endless recursion.

    点赞 评论 复制链接分享
  • dsl36367 dsl36367 2年前

    First of all, the gorilla/mux package is great for dynamic routing support. But it still doesn't let you strip prefixes from dynamic routes. Here's how you can get that to work:

    fileServer := http.FileServer(http.Dir("static"))
    r.PathPrefix("/user/{name}/").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        // Figure out what the resolved prefix was.
        name := mux.Vars(r)["name"]
        prefix := fmt.Sprintf("/user/%s/", name)
        // Strip it the normal way.
        http.StripPrefix(prefix, fileServer).ServeHTTP(w, r)
    })
    
    点赞 评论 复制链接分享
  • douchun5976 douchun5976 4年前

    http.FileServer is good choice for serving static files from directory and it's subdirectories.

    func main() {
      http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))
    
      log.Println("Listening...")
      if err := http.ListenAndServe(":8080", nil); err != nil {
         log.Fatal(err)
      }
    }
    

    It will serve any files under /static/* directory and its subdirectories via http://localhost:8080/static/<path-to-file>.

    So design your directory structure and map it via one or more file server handler.


    EDIT:

    As asked in the comment. Serve static files form root and underneath.

    http.Handle("/", http.StripPrefix("/", http.FileServer(http.Dir("web"))))
    

    It means files under web/* will be served from root /.

    点赞 评论 复制链接分享