提出

	goodsGroup := router.Group("/goods")
	{
		goodsGroup.GET("/list", goodlist)
		goodsGroup.GET("/1", goodadetail)
		goodsGroup.POST("add", creatgoods)
	}

其中 goodsGroup.GET("/1", goodadetail)这样获取id为1的商品并不科学。

	goodsGroup := router.Group("/goods")
	{
		goodsGroup.GET("/list", goodlist)
		goodsGroup.GET("/:id/:action", goodadetail) //改成这样的变量
		goodsGroup.POST("add", creatgoods)
	}

解决

这样可以把url的id放在变量里面 :id的位置,用于函数处理。在这里函数是goodadetail
下面改函数。

func goodadetail(context *gin.Context) {
	id := context.Param("id")
	action := context.Param("action")
	context.JSON(http.StatusOK, gin.H{
		"id": id, 
		"action":action,
	})

}

id := context.Param("id")这是取出url中id的位置放到新创建的id变量里面。
⚠️这样直接开始会有问题

goodsGroup.GET("/list", goodlist)
goodsGroup.GET("/:id", goodadetail)

显然list的匹配模式和id匹配模式冲突。
改成

goodsGroup.GET("", goodlist) //直接get
goodsGroup.GET("/:id", goodadetail) 
goodsGroup.POST("", creatgoods) //直接post

这样比较符合规范。

另外有一种 *的匹配模式,慎用
goodsGroup.GET("/:id/*action", goodadetail)
如果访问/goods/1/delete/other
*模式下会获取到 action:"/delete/other"就是把后面的东西全部放进去
:模式下,如果访问/goods/1/delete/another等只会得到404",要完成匹配必须访问类似 /goods/1/delete/other。。可能说不清楚。简而言之:下匹配更加强制格式,*则更像对路径的一把抓。
*模式一般用于获取文件路径等情况。动态url,比如id之类还是用:

约束url

goodsGroup.GET("/list", goodlist)
goodsGroup.GET("/:id/:action", goodadetail)

会把list当id,需要约束,比如非数字的404处理。
下面是写的例子

package main

import (
	"github.com/gin-gonic/gin"
	"net/http"
)

type Person struct {
	ID   string `uri:"id" binding:"required,uuid"` //ID必须string类型,required表示必须,格式是uuid类型
	Name string `uri:"name" binding:"required"`
}

func main() {

	rooter := gin.Default()
	rooter.GET("/:name/:id", func(context *gin.Context) {
		var person Person

		//err取值为匹配后的错误 &person传入函数的地址。 err != nil就是有错
		if err := context.ShouldBindUri(&person); err != nil {
			context.Status(404)
			return //直接return,否则出错了还要执行下面的逻辑
		}

		context.JSON(http.StatusOK,gin.H{
			"name":person.Name,
			"id":person.ID,
		})

	})
   rooter.Run(":8083")
}

主要是定义 Person结构体,用于匹配url里面的传值类型。
然后分别对传值无误和有误的数据进行处理
其中表单验证之后在写。