在Go中验证URL中的UUID参数,可以使用httprouter的正则表达式功能配合Go标准库的regexp包。以下是完整的解决方案:
1. 定义UUID正则表达式
UUID的正则表达式可以这样定义:
package main
import (
"regexp"
)
// UUID正则表达式(支持带或不带连字符)
const uuidPattern = `[0-9a-fA-F]{8}-?[0-9a-fA-F]{4}-?[0-9a-fA-F]{4}-?[0-9a-fA-F]{4}-?[0-9a-fA-F]{12}`
// 严格版本的UUID正则表达式(必须带连字符)
const strictUUIDPattern = `[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}`
2. 在httprouter中使用正则表达式验证
package main
import (
"fmt"
"log"
"net/http"
"github.com/julienschmidt/httprouter"
)
func main() {
router := httprouter.New()
// 使用正则表达式验证UUID参数
router.GET("/books/:uuid", BookIndex)
router.GET("/books/strict/:uuid", BookIndexStrict)
log.Fatal(http.ListenAndServe(":8080", router))
}
// 处理带连字符的UUID
func BookIndex(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
uuid := ps.ByName("uuid")
// 验证UUID格式
matched, _ := regexp.MatchString(`^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$`, uuid)
if !matched {
http.Error(w, "Invalid UUID format", http.StatusBadRequest)
return
}
fmt.Fprintf(w, "Valid UUID: %s", uuid)
}
// 处理严格验证的UUID(路由级别验证)
func BookIndexStrict(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
uuid := ps.ByName("uuid")
fmt.Fprintf(w, "Strict UUID: %s", uuid)
}
3. 使用预编译的正则表达式提高性能
package main
import (
"fmt"
"log"
"net/http"
"regexp"
"github.com/julienschmidt/httprouter"
)
var (
uuidRegex = regexp.MustCompile(`^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$`)
)
func main() {
router := httprouter.New()
// 在路由定义中使用正则表达式
router.GET("/books/:uuid", validateUUID(BookIndex))
log.Fatal(http.ListenAndServe(":8080", router))
}
// 中间件验证UUID
func validateUUID(next httprouter.Handle) httprouter.Handle {
return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
uuid := ps.ByName("uuid")
if !uuidRegex.MatchString(uuid) {
http.Error(w, "Invalid UUID format", http.StatusBadRequest)
return
}
next(w, r, ps)
}
}
func BookIndex(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
uuid := ps.ByName("uuid")
fmt.Fprintf(w, "Processing book with UUID: %s", uuid)
}
4. 使用标准库的uuid包进行验证
package main
import (
"fmt"
"log"
"net/http"
"github.com/google/uuid"
"github.com/julienschmidt/httprouter"
)
func main() {
router := httprouter.New()
router.GET("/books/:uuid", validateUUIDWithPackage(BookIndex))
log.Fatal(http.ListenAndServe(":8080", router))
}
// 使用google/uuid包验证
func validateUUIDWithPackage(next httprouter.Handle) httprouter.Handle {
return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
uuidStr := ps.ByName("uuid")
_, err := uuid.Parse(uuidStr)
if err != nil {
http.Error(w, "Invalid UUID format", http.StatusBadRequest)
return
}
next(w, r, ps)
}
}
func BookIndex(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
uuidStr := ps.ByName("uuid")
fmt.Fprintf(w, "Valid UUID using package: %s", uuidStr)
}
5. 在路由定义中直接使用正则表达式(推荐)
package main
import (
"fmt"
"log"
"net/http"
"github.com/julienschmidt/httprouter"
)
func main() {
router := httprouter.New()
// 直接在路由中使用正则表达式验证UUID
router.GET("/books/:uuid([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})", BookIndex)
// 或者使用更灵活的正则表达式(支持带或不带连字符)
router.GET("/items/:uuid([0-9a-fA-F]{8}-?[0-9a-fA-F]{4}-?[0-9a-fA-F]{4}-?[0-9a-fA-F]{4}-?[0-9a-fA-F]{12})", ItemIndex)
log.Fatal(http.ListenAndServe(":8080", router))
}
func BookIndex(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
uuid := ps.ByName("uuid")
fmt.Fprintf(w, "Book UUID (strict): %s", uuid)
}
func ItemIndex(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
uuid := ps.ByName("uuid")
fmt.Fprintf(w, "Item UUID (flexible): %s", uuid)
}
这些方法都可以有效地验证HTTP URL中的UUID参数。第一种方法在路由级别进行验证,第二种使用中间件,第三种使用专门的UUID包,最后一种直接在路由定义中使用正则表达式。