使用go的HTTP PUT请求处理程序

Please consider this sample Go code snippet,

package main

import (
        "fmt"
        "log"
        "net/http"
        "time"
)

func main() {
        listen_at := ":3114"
        go http.Handle("/", http.FileServer(http.Dir(".")))
        //go http.Handle("/max", http.FileServer(http.Dir("."))) <-- Fails
        go http.HandleFunc("/ping", ping_handler)
        log.Fatal(http.ListenAndServe(listen_at, nil))

}

func ping_handler(w http.ResponseWriter, r *http.Request) {
        a := time.Now()
        layout := "2006-01-02-03-04-05-000"

        fmt.Println("Got root!")
        fmt.Println("r is", r)
        fmt.Println("RemoteAddr is", r.RemoteAddr)
        send_this := "OK GOT ping! " + a.Format(layout)
        w.Write([]byte(send_this))
}

I've two questions:

(1) How can I change the FileServer to serve /max instead of / - my attempts failed, I get 404 for http://localhost:3114/max/ and http://localhost:3114/max.

(2) I wish to accept PUT requests to /max - how can I achieve this?

Please point me the right direction, thanks!

Edit 1

package main

import (
        "fmt"
        "log"
        "net/http"
)

func main() {
        go http.HandleFunc("/ping", hello)
        log.Fatal(http.ListenAndServe(":3114", nil))
}

func hello(w http.ResponseWriter, r *http.Request) {
        fmt.Println("STarting hello!")

        log.Println("Got connection")
        if r.URL.Path != "/ping" {
                http.Error(w, "404 not found", http.StatusNotFound)
                return
        }
        log.Println("Method:", r.Method)
        switch r.Method {
        case "GET":
                send_this := "OK GOT ping! "
                w.Write([]byte(send_this))
        case "PUT":
                fmt.Println("We got put!")
                err := r.ParseForm()
                checkErr(err)
                fmt.Println("r now", r)
                fmt.Println("r.Form", r.Form)
                fmt.Println("r.PostForm", r.PostForm)
        default:
                send_this := "Please dont!"
                w.Write([]byte(send_this))
                fmt.Fprintf(w, "Unknown request!")
        }

}

func checkErr(err error) {
        if err != nil {
                fmt.Println("Error", err)
        }
}

I send a PUT request as curl -k http://localhost:3114/ping -T /tmp/a.go -v, it shows:

STarting hello!
2019/06/07 15:05:10 Got connection
2019/06/07 15:05:10 Method: PUT
We got put!
r now &{PUT /ping HTTP/1.1 1 1 map[Content-Length:[10115] Expect:[100-continue] User-Agent:[curl/7.47.0] Accept:[*/*]] 0xc4200ec2e0 <nil> 10115 [] false localhost:3114 map[] map[] <nil> map[] 127.0.0.1:64612 /ping <nil> <nil> <nil> 0xc42005e340}
r.Form map[]
r.PostForm map[]

How can I find the actual data, and the filename that came in from PUT?

From https://golang.org/pkg/net/http/#ServeMux:

Patterns name fixed, rooted paths, like "/favicon.ico", or rooted subtrees, like "/images/" (note the trailing slash).

That means that /max, which is a fixed rooted path, will match only /max and the pattern /max/, which is a rooted subtree, will match /max/, any other path that starts with /max/, and by default it will also match /max.

http.Handle("/max/", http.FileServer(http.Dir(".")))

Depending on what the layout of your . directory is, you may need to use https://golang.org/pkg/net/http/#StripPrefix.

Let's say your directory contains two files:

.
├── foo.txt
└── max
    └── bar.txt

Given the handler above, a request to /max/bar.txt will return the bar.txt file, but a request to /max/foo.txt or /foo.txt will return 404, no file.

So if you want to serve the files from the /max/ path, but your . directory doesn't have a max sub-directory then you can use StripPrefix to remove the /max prefix from the request's url path before it is passed on to the FileServer.

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

To handle PUT requests at the same route you need a custom handler.

type myhandler struct {
    fs http.Handler
}

func (h myhandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    if r.Method != "PUT" {
        // business as usual
        h.fs.ServeHTTP(w, r)
        return
    }

    // handle PUT
    // ...
}

And then to register it, do:

fs := http.StripPrefix("/max/", http.FileServer(http.Dir(".")))
http.Handle("/max/", myhandler{fs: fs})