GoでのWeb Framework選択時にルーティングの問題でGinを諦めてしまった人へ

## 前置き
久しぶりに記事を書いています。（結構雑にですが）
なんで書く気になったかというと、あまりに嬉しい修正が入っていたことに気が付いたからです。
恥ずかしながら結構前のアップデートだったようですが、12月中頃に気付きました・・・

GoでAPIサーバを作成する際にWAFの選択肢は様々だと思います。
`go-swagger`を使ってみようかなんて考えたり、`Echo`と`Gin`ってどっちが良いんだろうと悩んだり。

ちなみに僕は過去に`Gin`を選択したり、WAFというかRouterとして`gorilla/mux`を使用したりもしていました。

そして`Gin`についてはRouterのPathの記述というか、ルーティングに悩まされることがありました。

いくつかそのような記事も見かけたので気になる方は検索してみてください。

上記のようにPathが柔軟に作れないことで一定数`Gin`を選択肢から外してしまった方もいるのでは無いでしょうか。

今回はそんなルーティングに難があったGinでかなり問題が解決されていた部分について書いていこうと思う。

## 前提
- go version: `go1.17.6 darwin/arm64`
- 問題があったGinのversion: `~ https://github.com/gin-gonic/gin@v1.6.3`
- 問題が解消されたGinのversion: `https://github.com/gin-gonic/gin@v1.7.0 ~`

## 今までの問題 ~v1.6.3
APIサーバなどを作成する際のルーティングで衝突してしまう問題

下記のようなルーティングを記載したとします
※GinではPathParametersを使用する際には`:PathParameters`として記述する

```go
package main

import "github.com/gin-gonic/gin"

func main() {
	r := gin.Default()
	r.GET("/:path", func(c *gin.Context) {
		path := c.Param("path")
		c.JSON(200, gin.H{"message": path})
	})

	r.Run()
}
```
このようなルーティングでは正しく機能します。

```shell
$ curl localhost:8080/ok  
{"message":"ok"}

$ curl localhost:8080/sample
{"message":"sample"}

$ curl localhost:8080/test
{"message":"test"}
```

しかし上記のルーティングのように一番上の階層でPathParametersを使用してしまった場合
下記のようなルーティングを記述すると衝突して起動できなくなってしまっていました。

```go
package main

import "github.com/gin-gonic/gin"

func main() {
	r := gin.Default()
	r.GET("/:path", func(c *gin.Context) {
		path := c.Param("path")
		c.JSON(200, gin.H{"message": path})
	})
//追加した部分Start
	r.GET("/ping", func(c *gin.Context) {
		c.JSON(200, gin.H{"message": "pong"})
	})
//追加した部分End
	r.Run()
}
```

```shell
$ go run main.go
```


```shell
panic: 'ping' in new path '/ping' conflicts with existing wildcard ':path' in existing prefix '/:path'
```

- /:PathParameters
- /ping

第一階層を`/ping`と`PathParametersで受け付けたそれ以外`にしたかったのですが衝突してしまい起動すらできません。
この問題は第一階層だけに限った話ではなく同一階層でPathParametersと記述したPathを区別できず衝突してしまうというものでした。(というか解決される順番の問題)

#### できない例

- /users/:id
- /users/search

#### できる例
下記のようにuserとusersを分ける必要がある。

- /user/:id
- /users/search


上記の例は少し乱暴というか分ける設計で行くのが自然に見えるので分かり辛いかもしれませんが
ある程度制約が生まれてしまっていることが分かっていただければと思います。


他にも`/`が入ったPathParametersの`/`を自動的に削除してしまうという問題があったが殆どの原因が上記の`PathParameters`が`記述したPath`と衝突しまう問題と絡んでいたものなので割愛。



## 問題が解消された v1.7.0 ~
先ほど起動できなかったもの

```go
package main

import "github.com/gin-gonic/gin"

func main() {
	r := gin.Default()
	r.GET("/:path", func(c *gin.Context) {
		path := c.Param("path")
		c.JSON(200, gin.H{"message": path})
	})
//追加した部分Start
	r.GET("/ping", func(c *gin.Context) {
		c.JSON(200, gin.H{"message": "pong"})
	})
//追加した部分End
	r.Run()
}
```

問題なく起動できる

```shell
$ go run ./main.go                      
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.

[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:   export GIN_MODE=release
 - using code:  gin.SetMode(gin.ReleaseMode)

[GIN-debug] GET    /:path                    --> main.main.func1 (3 handlers)
[GIN-debug] GET    /ping                     --> main.main.func2 (3 handlers)
[GIN-debug] Environment variable PORT is undefined. Using port :8080 by default
[GIN-debug] Listening and serving HTTP on :8080

```

ルーティングも問題ない

```
$ curl localhost:8080/test
{"message":"test"}

$ curl localhost:8080/sample
{"message":"sample"}

$ curl localhost:8080/ping  
{"message":"pong"}
```

以上！！！

今までルーティングの問題でGinを諦めてしまった人はもう一度選択肢の中に含めても良いかもしれません。
誰かの技術選択の際に少しでも参考になれば嬉しいです。


ちなみにどんな変更だったかは検索ツリーの変更を行いワイルドカード(PathParameters)が解決される順番を前にしたとのことです。
詳しい内容は[対象のPR](https://github.com/gin-gonic/gin/pull/2663)を見てみるのも良いかと思います。