はじめに
net/httpではルーティングを自前で実装する必要があるため、他のルーティングライブラリを利用した方が便利だと思っていました。
しかし、Go1.22でnet/httpのルーティング機能が強化されたようです。(調査不足で知らなかった)
そこでこの記事では、Go1.22で対応した機能を使いつつ、標準ライブラリでルーティングを実装する簡単なサンプルをまとめます。
Hello, world!
まずはシンプルなHello, world!から。
package main
import (
"io"
"log"
"net/http"
)
func HelloHandler(w http.ResponseWriter, r *http.Request) {
io.WriteString(w, "Hello, world!\n")
}
func main() {
http.HandleFunc("/hello", HelloHandler)
if err := http.ListenAndServe(":8080", nil); err != nil {
log.Fatal(err)
}
}
サーバーを起動しリクエストを送信すると、Hello, world!が出力されます。
go-sample $ curl http://localhost:8080/hello
Hello, world!
HTTPメソッドの指定
Go1.22から標準ライブラリでHTTPメソッドの指定が可能になっています。
下記はGETメソッドの場合のみHello, world!を出力する例です。
package main
import (
"io"
"log"
"net/http"
)
func HelloHandler(w http.ResponseWriter, r *http.Request) {
io.WriteString(w, "Hello, world!\n")
}
func main() {
// GET メソッドを指定
http.HandleFunc("GET /hello", HelloHandler)
if err := http.ListenAndServe(":8080", nil); err != nil {
log.Fatal(err)
}
}
GETメソッド以外のリクエストは弾かれていることが確認できます。
go-sample $ curl http://localhost:8080/hello -X GET
Hello, world!
go-sample $ curl http://localhost:8080/hello -X POST
Method Not Allowed
go-sample $ curl http://localhost:8080/hello -X PUT
Method Not Allowed
go-sample $ curl http://localhost:8080/hello -X DELETE
Method Not Allowed
なおGo1.22以前だと、次のように自前でHTTPメソッドに応じた処理を用意する必要がありました。
package main
import (
"io"
"log"
"net/http"
)
func HelloHandler(w http.ResponseWriter, r *http.Request) {
// GET メソッド以外は 405 (Method Not Allowed) を返す
if r.Method == http.MethodGet {
io.WriteString(w, "Hello, world!\n")
} else {
http.Error(w, "HTTPメソッドが不正です。", http.StatusMethodNotAllowed)
}
}
func main() {
// "GET /hello"とは指定できない
http.HandleFunc("/hello", HelloHandler)
if err := http.ListenAndServe(":8080", nil); err != nil {
log.Fatal(err)
}
}
パスパラメータの取得
今度はusers/123
のように特定のidをパスに含むケースを考えます。
Go1.22よりパスパラメータの取得もnet/httpのRequest.PathValue
で簡単にできるようになりました。
package main
import (
"fmt"
"log"
"net/http"
"strconv"
)
func UserHandler(w http.ResponseWriter, r *http.Request) {
// パスパラメータを取得
userIDStr := r.PathValue("id")
userID, err := strconv.Atoi(userIDStr)
if err != nil {
http.Error(w, "数値以外のIDは無効です", http.StatusBadRequest)
return
}
fmt.Fprintf(w, "ユーザーID: %d\n", userID)
}
func main() {
// プレースホルダで{id}と定義
http.HandleFunc("GET /users/{id}", UserHandler)
if err := http.ListenAndServe(":8080", nil); err != nil {
log.Fatal(err)
}
}
idの取得を確認できます。
go-sample $ curl http://localhost:8080/users/123 -X GET
ユーザーID: 123
go-sample $ curl http://localhost:8080/users/aaa -X GET
数値以外のIDは無効です
なおGo1.22以前でパスパラメータを取得する場合、/users/123
を”/”区切りで文字列分割したり、正規表現を用いるなどして、強引に123
を抽出しなくてはダメだったようです。
クエリパラメータの取得
最後は/users?id=123&age=30
のようにクエリパラメータが付与されているケースを考えます。
取得方法は次のとおりです。
package main
import (
"fmt"
"log"
"net/http"
"strconv"
)
func UserHandler(w http.ResponseWriter, r *http.Request) {
// Query メソッドでクエリパラメータの構造体を取得
query := r.URL.Query()
idStr := query.Get("id")
ageStr := query.Get("age")
id, err := strconv.Atoi(idStr)
if err != nil {
http.Error(w, "id は数値で指定してください", http.StatusBadRequest)
return
}
age, err := strconv.Atoi(ageStr)
if err != nil {
http.Error(w, "age は数値で指定してください", http.StatusBadRequest)
return
}
fmt.Fprintf(w, "ユーザーID: %d, 年齢: %d\n", id, age)
}
func main() {
http.HandleFunc("GET /users", UserHandler)
if err := http.ListenAndServe(":8080", nil); err != nil {
log.Fatal(err)
}
}
クエリパラメータに関してはGo1.22以前からURL.Query()
による取得が可能だったようです。
まとめ
Go1.22でnet/httpのルーティング機能が強化されたことで、他のライブラリを使わずともかなり便利にルーティングを実装できることがわかりました。
Goを学び始めて日が浅いため、この調子でまずは標準パッケージの理解を少しずつ深めていけたらと思います。
記事の内容について誤りがあればコメントにて教えていただけると幸いです。
コメント