Categories: GoFrame 教程

GoFrame 请求输入-请求校验

Request​对象支持非常完美的请求校验能力,通过给结构体属性绑定​v​标签即可。

需要注意的是,从​goframe v1.16​版本开始,如果参数采用结构化的输入输出管理,​HTTP​请求的数据校验不再受结构体默认值的影响,底层调用的是​gvalid​组件的​CheckStructWithData​方法,即直接使用请求的参数执行数据校验,而给定的结构体对象仅用于校验规则和错误提示信息的定义管理。

示例1,基本使用

我们将之前的示例做下调整,增加​v​校验标签。

package main

import (
 "github.com/gogf/gf/v2/frame/g"
 "github.com/gogf/gf/v2/net/ghttp"
)

// 注册请求数据结构
type RegisterReq struct {
 Name  string `p:"username"  v:"required|length:4,30#请输入账号|账号长度为:min到:max位"`
 Pass  string `p:"password1" v:"required|length:6,30#请输入密码|密码长度不够"`
 Pass2 string `p:"password2" v:"required|length:6,30|same:password1#请确认密码|密码长度不够|两次密码不一致"`
}

// 注册返回数据结构
type RegisterRes struct {
 Code  int         `json:"code"`
 Error string      `json:"error"`
 Data  interface{} `json:"data"`
}

func main() {
 s := g.Server()
 s.Group("/", func(group *ghttp.RouterGroup) {
  group.ALL("/register", func(r *ghttp.Request) {
   var req *RegisterReq
   if err := r.Parse(&req); err != nil {
    r.Response.WriteJsonExit(RegisterRes{
     Code:  1,
     Error: err.Error(),
    })
   }
   // ...
   r.Response.WriteJsonExit(RegisterRes{
    Data: req,
   })
  })
 })
 s.SetPort(8199)
 s.Run()
}

在该示例中,我们定义了两个结构体:​RegisterReq​用于参数接收,​RegisterRes​用于数据返回。由于该接口返回的是​JSON​数据结构,可以看到,只有返回的结构体中存在​json​标签,而接收的结构体中只有​p​标签。因为​RegisterReq​仅用于参数接收,无需设置返回的​json​标签。

p​标签是可选的,默认情况下会通过 忽略特殊字符+不区分大小写 的规则进行属性名称匹配转换,默认匹配规则满足绝大部分业务场景。

为了演示测试效果,这里在正常的返回结果​Data​属性中将​RegisterReq​对象返回,由于该对象没有绑定​json​标签,因此返回的​JSON​字段将会为其属性名称。

执行后,我们通过​curl​工具来测试一下:

$ curl "http://127.0.0.1:8199/register?name=john&password1=123456&password2=123456"
{"code":0,"error":"","data":{"Name":"john","Pass":"123456","Pass2":"123456"}}

$ curl "http://127.0.0.1:8199/register?name=john&password1=123456&password2=12345"
{"code":1,"error":"密码长度不够; 两次密码不一致","data":null}

$ curl "http://127.0.0.1:8199/register"
{"code":1,"error":"请输入账号; 账号长度为4到30位; 请输入密码; 密码长度不够; 请确认密码; 密码长度不够; 两次密码不一致","data":null}

示例2,校验错误处理

可以看到在以上示例中,当请求校验错误时,所有校验失败的错误都返回了,这样对于用户体验不是特别友好。当产生错误时,我们可以将校验错误转换为​gvalid.Error​接口对象,随后可以通过灵活的方法控制错误的返回。

package main

import (
 "github.com/gogf/gf/v2/frame/g"
 "github.com/gogf/gf/v2/net/ghttp"
 "github.com/gogf/gf/v2/util/gvalid"
)

type RegisterReq struct {
 Name  string `p:"username"  v:"required|length:4,30#请输入账号|账号长度为:min到:max位"`
 Pass  string `p:"password1" v:"required|length:6,30#请输入密码|密码长度不够"`
 Pass2 string `p:"password2" v:"required|length:6,30|same:password1#请确认密码|密码长度不够|两次密码不一致"`
}

type RegisterRes struct {
 Code  int         `json:"code"`
 Error string      `json:"error"`
 Data  interface{} `json:"data"`
}

func main() {
 s := g.Server()
 s.Group("/", func(group *ghttp.RouterGroup) {
  group.ALL("/register", func(r *ghttp.Request) {
   var req *RegisterReq
   if err := r.Parse(&req); err != nil {
    // Validation error.
    if v, ok := err.(gvalid.Error); ok {
     r.Response.WriteJsonExit(RegisterRes{
      Code:  1,
      Error: v.FirstString(),
     })
    }
    // Other error.
    r.Response.WriteJsonExit(RegisterRes{
     Code:  1,
     Error: err.Error(),
    })
   }
   // ...
   r.Response.WriteJsonExit(RegisterRes{
    Data: req,
   })
  })
 })
 s.SetPort(8199)
 s.Run()
}

可以看到,当错误产生后,我们可以通过​err.(gvalid.Error​)断言的方式判断错误是否为校验错误,如果是的话则返回第一条校验错误,而不是所有都返回。

此外,我们这里也可以使用​gerror.Current​获取第一条报错信息,而不是使用断言判断。例如:

var req *RegisterReq
if err := r.Parse(&req); err != nil {
 r.Response.WriteJsonExit(RegisterRes{
  Code:  1,
  Error: gerror.Current(err).Error(),
 })
}

执行后,我们通过​curl​工具来测试一下:

$ curl "http://127.0.0.1:8199/register"
{"code":1,"error":"请输入账号","data":null}

$ curl "http://127.0.0.1:8199/register?name=john&password1=123456&password2=12345"
{"code":1,"error":"两次密码不一致","data":null}

terry

这个人很懒,什么都没有留下~

Share
Published by
terry

Recent Posts

了解 ChatGPT 如何维护上下文

对话中的语境概念至关重要,因为…

4 天 ago

GenAI:如何通过快速压缩技术降低成本

在本文中,我们将探讨在开发早期…

1 周 ago

vue:页面注入js修改input值

一般会直接这样写: let z…

2 周 ago

聊聊vue3中的defineProps

在Vue 3中,defineP…

3 周 ago

在 Chrome 中删除、允许和管理 Cookie

您可以选择删除现有 Cooki…

4 周 ago

自定义指令:聊聊vue中的自定义指令应用法则

今天我们来聊聊vue中的自定义…

1 月 ago