You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何使用Golang结合Beego框架批量验证API请求的JSON输入?

解决Beego中JSON请求批量验证的问题

我之前在做Beego项目的时候也碰到过这个痛点——标准库的json.Unmarshal只会返回第一个类型不匹配的错误,没法一次性给用户所有问题反馈,而且不小心还容易因为未处理错误导致panic。结合你的需求,我给你一套可行的解决方案,分两步搞定类型校验+批量错误收集

一、先解决JSON解析的批量错误问题

标准库的JSON解析确实只返回第一个错误,我们可以用支持多错误收集的第三方JSON库来替代,比如goccy/go-json,它能一次性捕获所有类型不匹配、未知字段等解析错误。

步骤1:安装依赖

go get github.com/goccy/go-json
go get github.com/go-playground/validator/v10

步骤2:在Beego控制器中安全解析JSON

先读取请求体,然后用带配置的解析方式收集所有解析错误:

import (
    "fmt"
    "io/ioutil"

    "github.com/goccy/go-json"
    "github.com/go-playground/validator/v10"
    "github.com/astaxie/beego"
)

// 定义请求结构体,同时加上业务校验标签
type UserCreateRequest struct {
    Name  string `json:"name" validate:"required,min=2,max=20"`
    Age   int    `json:"age" validate:"required,gte=18,lte=100"`
    Email string `json:"email" validate:"required,email"`
}

func (this *MainController) Post() {
    // 1. 读取请求体
    body, err := ioutil.ReadAll(this.Ctx.Request.Body)
    if err != nil {
        this.Data["json"] = map[string]interface{}{
            "code":  400,
            "error": "读取请求内容失败",
        }
        this.ServeJSON()
        return
    }
    defer this.Ctx.Request.Body.Close()

    var req UserCreateRequest
    var allErrors []string

    // 2. 用goccy/go-json解析,开启多错误收集
    parseOpts := json.UnmarshalOptions{
        CollectAllErrors:      true,  // 收集所有解析错误
        DisallowUnknownFields: true,  // 禁止传入结构体未定义的字段
    }
    parseErr := parseOpts.Unmarshal(body, &req)
    if parseErr != nil {
        // 提取所有解析错误信息
        if jsonErr, ok := parseErr.(*json.UnmarshalError); ok {
            for _, e := range jsonErr.Errors {
                allErrors = append(allErrors, fmt.Sprintf("字段%s:%s", e.Field, e.Message))
            }
        } else {
            allErrors = append(allErrors, parseErr.Error())
        }
    }

二、添加业务规则校验

在确保JSON解析没有类型错误后,用go-playground/validator做业务规则校验(比如必填、长度、格式等),同样收集所有错误:

// 3. 业务规则校验(只有解析无错时才执行)
    if len(allErrors) == 0 {
        validate := validator.New()
        validateErr := validate.Struct(req)
        if validateErr != nil {
            // 提取所有业务校验错误
            if validationErrs, ok := validateErr.(validator.ValidationErrors); ok {
                for _, e := range validationErrs {
                    allErrors = append(allErrors, fmt.Sprintf("字段%s:不符合%s规则", e.Field(), e.Tag()))
                }
            } else {
                allErrors = append(allErrors, validateErr.Error())
            }
        }
    }

    // 4. 返回结果
    if len(allErrors) > 0 {
        this.Data["json"] = map[string]interface{}{
            "code":   400,
            "errors": allErrors,
        }
    } else {
        // 这里写你的业务逻辑,比如保存数据等
        this.Data["json"] = map[string]interface{}{
            "code": 200,
            "msg":  "请求验证通过",
            "data": req,
        }
    }
    this.ServeJSON()
}

关键说明

  1. 避免panic:所有的错误都做了捕获和处理,不会因为JSON解析失败导致panic;DisallowUnknownFields还能防止用户传入多余字段,保证请求的规范性。
  2. 批量错误反馈:不管是类型不匹配、未知字段,还是业务规则不满足,都会被一次性收集并返回,用户不用反复修改重试。
  3. 替代方案:如果你坚持用govalidator,可以把JSON先解析成map[string]interface{},然后用它的ValidateMap方法传入校验规则,同样能收集所有错误,不过个人更推荐go-playground/validator,它的标签式校验更简洁。

内容的提问来源于stack exchange,提问作者anshuraj

火山引擎 最新活动