Golang中如何自定义405 Method Not Allowed的JSON响应
Golang中如何自定义405 Method Not Allowed的JSON响应 你好,
正如此处所述,Go 1.22 的 http.NewServeMux 附带了一个默认的 405 Method Not Allowed 纯文本响应,但我希望覆盖此行为,改为返回自定义的 JSON 响应。是否可以不编写中间件来实现?
谢谢
mux := http.NewServeMux()
mux.HandleFunc("GET /api/info", handler.Info)
甚至不确定该怎么做。
更多关于Golang中如何自定义405 Method Not Allowed的JSON响应的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
也许你可以将所有无效路径都交给一个特定的处理器来处理,在那里你可以自定义你的消息。
简而言之:我认为如果不使用中间件或类似的东西,这是不可能实现的。以下是相关的标准库代码:
https://cs.opensource.google/go/go/+/master:src/net/http/server.go;l=2553
看起来他们在严格执行 HTTP 语义。换句话说:返回 JSON 意味着该方法是受支持的。基于这一点,我认为你可以这样做:
mux := http.NewServeMux()
// 处理 GET 请求
mux.HandleFunc("GET /api/info", handler.Info)
// 向客户端发送友好的 JSON 而不是 405 错误
mux.HandleFunc("/api/info", handler.InfoNotSupported)
这并不完全理想,但代码易于阅读和理解其功能;并且避免了使用中间件。免责声明:我尚未使用 Go 1.22 的路由增强功能,因此不能 100% 确定这将如何表现。
在Go 1.22中,可以通过实现自定义的http.Handler来覆盖405响应,无需中间件。以下是示例代码:
package main
import (
"encoding/json"
"net/http"
)
type CustomMux struct {
*http.ServeMux
}
func (m *CustomMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// 先尝试标准路由匹配
handler, pattern := m.Handler(r)
if pattern == "" {
// 没有匹配的路由,返回404
http.NotFound(w, r)
return
}
// 检查方法是否允许
if handler != nil {
// 这里需要检查方法是否匹配
// 由于标准库不直接暴露方法检查,我们可以通过尝试调用handler来判断
// 但更简单的方式是使用自定义路由逻辑
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusMethodNotAllowed)
json.NewEncoder(w).Encode(map[string]string{
"error": "Method Not Allowed",
"message": "The requested method is not supported for this endpoint",
})
return
}
handler.ServeHTTP(w, r)
}
func main() {
mux := &CustomMux{ServeMux: http.NewServeMux()}
mux.HandleFunc("GET /api/info", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]string{
"message": "Info endpoint",
})
})
http.ListenAndServe(":8080", mux)
}
更精确的实现需要检查具体的方法:
package main
import (
"encoding/json"
"net/http"
"strings"
)
type routeInfo struct {
pattern string
methods []string
handler http.Handler
}
type JSONMethodMux struct {
routes []routeInfo
}
func (m *JSONMethodMux) Handle(method, pattern string, handler http.Handler) {
m.routes = append(m.routes, routeInfo{
pattern: pattern,
methods: []string{method},
handler: handler,
})
}
func (m *JSONMethodMux) HandleFunc(method, pattern string, handler func(http.ResponseWriter, *http.Request)) {
m.Handle(method, pattern, http.HandlerFunc(handler))
}
func (m *JSONMethodMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
var allowedMethods []string
var matchedHandler http.Handler
for _, route := range m.routes {
if route.pattern == r.URL.Path || strings.HasSuffix(route.pattern, "/") && strings.HasPrefix(r.URL.Path, route.pattern) {
for _, method := range route.methods {
if method == r.Method {
matchedHandler = route.handler
break
}
allowedMethods = append(allowedMethods, method)
}
}
}
if matchedHandler != nil {
matchedHandler.ServeHTTP(w, r)
return
}
if len(allowedMethods) > 0 {
w.Header().Set("Content-Type", "application/json")
w.Header().Set("Allow", strings.Join(allowedMethods, ", "))
w.WriteHeader(http.StatusMethodNotAllowed)
json.NewEncoder(w).Encode(map[string]interface{}{
"error": "Method Not Allowed",
"allowed_methods": allowedMethods,
"path": r.URL.Path,
})
return
}
http.NotFound(w, r)
}
func main() {
mux := &JSONMethodMux{}
mux.HandleFunc("GET", "/api/info", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]string{
"message": "Info endpoint",
})
})
mux.HandleFunc("POST", "/api/create", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]string{
"message": "Create endpoint",
})
})
http.ListenAndServe(":8080", mux)
}
第一个示例展示了包装标准ServeMux的基本方法,第二个示例提供了更完整的自定义路由实现,能准确返回允许的方法列表。

