Go中的HTTP方法指南

78 阅读2分钟

作为一个网络开发者,你可能已经知道HTML表单只支持 GETPOST HTTP方法。

如果你想发送一个PUTPATCHDELETE 的请求,你需要求助于从JavaScript发送一个XMLHttpRequest (大多数主要浏览器都支持),或者在你的服务器端应用代码中实现一个变通,以支持 "欺骗 "的HTTP方法。

事实上的变通方法--如果你使用过Ruby on Rails、Laravel或Express这样的框架,你可能会很熟悉--就是在你的表单中加入一个隐藏的_method ,其中包含欺骗的HTTP方法。有点像这样:

<form method="POST" action="/">
    <input type="hidden" name="_method" value="PUT">
    <button type="submit">Submit</button>
</form>

另一个常见的解决方法是在X-HTTP-Method-Override 头中发送一个欺骗的HTTP方法。

那么,我们怎样才能在Go应用程序中支持这些东西呢?

MethodOverride中间件

拦截和处理欺骗性的HTTP方法是一些自定义中间件的完美任务。我们希望该中间件能够:

  1. 在请求到达任何应用程序处理程序之前拦截POST
  2. 检查欺骗性的HTTP方法,无论是在请求主体的_method 参数中还是在X-HTTP-Method-Override 头部。
  3. 如果存在欺骗的方法--并且等于"PUT""PATCH""DELETE" --当前的http.Request.Method 值应该被相应地更新。

这是很快速的实现:

package main

import (
    "net/http"
)

func MethodOverride(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        // Only act on POST requests.
        if r.Method == "POST" {

            // Look in the request body and headers for a spoofed method.
            // Prefer the value in the request body if they conflict.
            method := r.PostFormValue("_method")
            if method == "" {
                method = r.Header.Get("X-HTTP-Method-Override")
            }

            // Check that the spoofed method is a valid HTTP method and
            // update the request object accordingly.
            if method == "PUT" || method == "PATCH" || method == "DELETE" {
                r.Method = method
            }
        }

        // Call the next handler in the chain.
        next.ServeHTTP(w, r)
    })
}

然后你可以在你的应用程序中这样使用中间件:

package main

import (
    "html/template"
    "io"
    "log"
    "net/http"
)

const form = `
<!DOCTYPE HTML>
<html>
    <body>
        <form method="POST" action="/">
            <input type="hidden" name="_method" value="PUT">
            <label>Example field</label>
            <input type="text" name="example">
            <button type="submit">Submit</button>
        </form>
    </body>
</html>
`

func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("/", formHandler)

    // Wrap the servemux with the MethodOverride middleware.
    err := http.ListenAndServe(":4000", MethodOverride(mux))
    log.Print(err)
}

func formHandler(w http.ResponseWriter, r *http.Request) {
    switch r.Method {
    case "GET":
        t, err := template.New("form").Parse(form)
        if err != nil {
            http.Error(w, err.Error(), 500)
        }
        t.Execute(w, nil)
    case "PUT":
        io.WriteString(w, "This is a PUT request")
    default:
        http.Error(w, http.StatusText(405), 405)
    }
}