控制器的输入与输出使用了结构体定义进行约束,结构化维护输入输出数据结构是推荐的方式。例如:
// 账号唯一性检测请求参数,用于前后端交互参数格式约定 type UserApiCheckPassportReq struct { Passport string `v:"required#账号不能为空"` } |
虽然只有一个参数,也采用了结构化定义,我们直接查看该结构体便可得知该接口的输入参数格式,而不用进入代码中去分析,从而极大提高维护效率。
结构体转换可以使用GetStruct
或者Parse
方法,其中Parse
同时可以执行数据校验。结构体转换方法的参数都可以给定一个结构体的空指针,内部会自动初始化结构体对象,转换失败(例如提交参数不存在)不会执行初始化。例如:
var ( data *model.UserApiSignInReq ) if err := r.Parse(&data); err != nil { response.JsonExit(r, 1, err.Error()) } |
客户端提交的数据都是不可信的,必须要做数据校验。
可以通过给结构体绑定v
的标签进行设定校验规则以及定义的错误提示。例如:
// 登录请求参数,用于前后端交互参数格式约定 type UserApiSignInReq struct { Passport string `v:"required#账号不能为空"` Password string `v:"required#密码不能为空"` } |
控制器负责接收、转换、校验、处理请求参数后,将所需的参数传递给调用的service
对象方法,而不是直接将Request
对象传递给service
。例如:
func (a *apiUser) SignIn(r *ghttp.Request) { var ( data *model.UserApiSignInReq ) if err := r.Parse(&data); err != nil { response.JsonExit(r, 1, err.Error()) } if err := service.User.SignIn(r.Context(), data.Passport, data.Password); err != nil { response.JsonExit(r, 1, err.Error()) } else { response.JsonExit(r, 0, "ok") } } |
https://github.com/gogf/gf-demos/blob/master/app/api/user.go