この記事で分かること
-
X-HTTP-Method-Override
ヘッダとはAPI側でGET
,POST
以外のメソッドをPOST
を使って表現することを許可するのを可能にするヘッダのこと - ミドルウェアはデコレーターパターンによりリクエストオブジェクトを上書きすることができる
- 例えばミドルウェアで
X-HTTP-Method-Override
ヘッダーからメソッドを上書きすることができる - Go系のフレームワークで
X-HTTP-Method-Overrideヘッダー
を扱うには自前でミドルウェアを実装する必要がある - JavaScript系だと
Express
、PHP系だとslim
がX-HTTP-Method-Overrideヘッダー
を扱うミドルウェアを提供している
LT版はこちらです
背景 🖼️
X-HTTP-Method-Override
ヘッダを以下の本を読んで知りました。
サーバー側のフレームワークやミドルウェアがこのヘッダをサポートして自動で解釈してくれる場合も多い
このような記載がありましたのでどんなフレームワークやミドルウェアがこれに対応しているのか調査します。
また、業務ではバックエンドでGo
を使っているのでGo
のビルトインではどんな記述をする必要があるのか実際にコードを書いて試します。
X-HTTP-Method-Override
ヘッダとは 💡
API側でGET
,POST
以外のメソッドをPOST
を使って表現することを許可するのを可能にするヘッダです。
例えばHTMLのForm経由だとメソッドにはGET,
POST`しか指定できず、PATCH,DELETE,PUTはサポートされていないです。
参考
https://blog.jxck.io/entries/2023-11-27/hixie.html
他の手段として_method
パラメータを使う方法もありますがこちらは今回触れません。
X-HTTP-Method-Override
ヘッダーからMethodを上書きする機能を持つフレームワークやミドルウェア 🔍
- JavaScript
Expressではmethod-override
というミドルウェアを使うことで実現できます。
https://expressjs.com/en/resources/middleware/method-override.html - PHP
slimではMethodOverrideMiddleware
というミドルウェアを使うことで実現できます。
https://www.slimframework.com/docs/v4/middleware/method-overriding.html - Go
メジャー所のものだと見当たりませんでした - Ruby
Rack
でミドルウェアが用意されています
https://github.com/rack/rack/blob/main/lib/rack/method_override.rb
ミドルウェアとは 🔄
そもそもミドルウェアについてわかっていないので調べます。
The way this works is that middleware implements a decorator pattern: it takes the request, does something, and returns another request object to the next layer of the stack.
ミドルウェアは以下の役割を持つことがわかります。
リクエストを受け取り、何らかの処理を行い、次の層に別のリクエストオブジェクトを返す
今回のメソッドの上書きはミドルウェアで行われるので以下のようなイメージです。
参考
実装:Goでミドルウェアを実装しX-HTTP-Method-Override
からメソッドを上書きする 🏃♀️
以下のリポジトリが完成版です。
go-blueprint
でテンプレートを作成しました。
https://github.com/yamatai12/sample-x-http-method-override
package server
import (
"encoding/json"
"log"
"net/http"
)
func (s *Server) RegisterRoutes() http.Handler {
mux := http.NewServeMux()
// Register routes
mux.HandleFunc("/", s.HelloWorldHandler)
return mux
}
func (s *Server) HelloWorldHandler(w http.ResponseWriter, r *http.Request) {
log.Println("Method:", r.Method)
resp := map[string]string{"message": "Hello World"}
jsonResp, err := json.Marshal(resp)
if err != nil {
http.Error(w, "Failed to marshal response", http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
if _, err := w.Write(jsonResp); err != nil {
log.Printf("Failed to write response: %v", err)
}
}
以下の関数を実装します。
func (s *Server) methodOverrideMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method == http.MethodPost {
if override := r.Header.Get("X-HTTP-Method-Override"); override != "" {
r.Method = override
}
}
next.ServeHTTP(w, r)
})
}
そして、RegisterRoutesのmux
をこの関数でラップします。
func (s *Server) RegisterRoutes() http.Handler {
mux := http.NewServeMux()
// Register routes
mux.HandleFunc("/", s.HelloWorldHandler)
// Wrap the mux with middleware
return s.methodOverrideMiddleware(mux)
}
ミドルウェアはデコレーターパターンで実装しているということについて
デコレーターパターンとは
- 関数を別の関数でラップすることで、その関数の機能を変更できるデザインパターンのこと
- 元となるオブジェクトにどんどんデコレート(装飾)を行うことで機能の拡張を行うようなパターンのこと
methodOverrideMiddleware
は、引数として受け取ったnext http.Handler
をラップし、新しいhttp.Handler
を返しています。
参考
検証;X-HTTP-Method-Override
からメソッドを上書きする ✅
curlで以下のリクエストを送ります。
curl -X POST localhost:8080 -H "X-HTTP-Method-Override: PUT"
X-HTTP-Method-Override
ヘッダでPUTを指定すると、API側でPUTメソッドとして解釈されるのか確認します。
HelloWorldHandler
内にlogでメソッドを表示するようにしました。確認すると以下が表示されました。
Method: PUT
これでリクエストを送るとMethodがPOST
からPUT
に変換されたとわかります。
今後調べたいこと 💪
- HTTPメソッドをPost,Getメソッドでしかクライアント側で指定できないことがあるケースについて
- HTMLのForm経由だとメソッドには
GET,
POST`しか指定できない理由について
最後に
以下を理解できました
- ミドルウェアの概念
- Go系だと
X-HTTP-Method-Override
ヘッダーによるメソッドの上書きは自前で実装する必要があること - 言語によってはフレームワーク側で
X-HTTP-Method-Override
ヘッダーによるメソッドの上書きを提供するミドルウェアが用意されているということ