如何解决gin c.BindJSON捕获时如何断言错误类型json.UnmarshalTypeError
我正在尝试使用gin gonic捕获绑定错误,并且对于来自go-playground / validator / v10的所有验证错误都可以正常工作,但是在解组为适当的数据类型时遇到捕获错误的问题。
对结构字段的验证失败会在使用验证器标签时返回gin.ErrorTypeBind
错误类型(必填,...)
但是如果我有一个结构
type Foo struct {
ID int `json:"id"`
Bar string `json:"bar"`
}
我尝试传递的json格式错误(传递字符串而不是id的数字)
{
"id":"string","bar":"foofofofo"
}
它将失败,并显示错误json: cannot unmarshal string into Go struct field Foo.id of type int
它仍然在我的处理程序中被视为gin.ErrorTypeBind
,因为它是绑定错误,但是由于我需要区分验证错误和解组错误,所以我遇到了问题。
我尝试过在validaton错误上进行类型转换,无法进行解组:
e.Err.(validator.ValidationErrors)
会惊慌
或者只是错误。是,但这根本无法捕捉到错误
if errors.Is(e.Err,&json.UnmarshalTypeError{}) {
log.Println("Json binding error")
}
我这样做的目的是将正确格式的错误消息返回给用户。目前,对于所有验证逻辑而言,它都运行良好,但是我似乎无法使其适用于json数据,在该数据中,错误的数据会发送给我。
有什么想法吗?
编辑:
添加示例以重现:
package main
import (
"encoding/json"
"errors"
"log"
"net/http"
"github.com/gin-gonic/gin"
"github.com/go-playground/validator/v10"
)
type Foo struct {
ID int `json:"id" binding:"required"`
Bar string `json:"bar"`
}
func FooEndpoint(c *gin.Context) {
var fooJSON Foo
err := c.BindJSON(&fooJSON)
if err != nil {
// caught and answer in the error MW
return
}
c.JSON(200,"test")
}
func main() {
api := gin.Default()
api.Use(ErrorMW())
api.POST("/foo",FooEndpoint)
api.Run(":5000")
}
func ErrorMW() gin.HandlerFunc {
return func(c *gin.Context) {
c.Next()
if len(c.Errors) > 0 {
for _,e := range c.Errors {
switch e.Type {
case gin.ErrorTypeBind:
log.Println(e.Err)
var jsonErr json.UnmarshalTypeError
if errors.Is(e.Err,&jsonErr) {
log.Println("Json binding error")
}
// if errors.As(e.Err,&jsonErr) {
// log.Println("Json binding error")
// }
// in reality i'm making it panic.
// errs := e.Err.(validator.ValidationErrors)
errs,ok := e.Err.(validator.ValidationErrors)
if ok {
log.Println("error trying to cast validation type")
}
log.Println(errs)
status := http.StatusBadRequest
if c.Writer.Status() != http.StatusOK {
status = c.Writer.Status()
}
c.JSON(status,gin.H{"error": "error"})
default:
log.Println("other error")
}
}
if !c.Writer.Written() {
c.JSON(http.StatusInternalServerError,gin.H{"Error": "internal error"})
}
}
}
}
尝试发送带有正文的帖子请求
{
"id":"rwerewr","bar":"string"
}
interface conversion: error is *json.UnmarshalTypeError,not validator.ValidationErrors
这将起作用:
{
"id":1,"bar":"string"
}
这将(正确地)返回 Key: 'Foo.ID' Error:Field validation for 'ID' failed on the 'required' tag
{ “ bar”:“ string” }
解决方法
//redirect code sample
URI redirectUrl = new URI(AppConfig.BASE_URL + "order/failed/");
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setLocation(redirectUrl);
return new ResponseEntity<>(httpHeaders,HttpStatus.SEE_OTHER);
查找完全匹配(在您的情况下,为errors.Is
的空实例)。请改用json.UnmarshalTypeError
:
errors.As
但是,正如@LeGEC所指出的那样,这假设杜松子酒的错误符合标准的var jsonErr *json.UnmarshalTypeError
if errors.As(e.Err,&jsonErr) {
log.Println("Json binding error")
}
接口,而事实并非如此。
[edit]:嗯,它不会解决@Flimzy的答案:看着the docs,gin.Error
没有实现.Unwrap()
方法。
您必须首先将错误转换为gin.Error
,然后检查ginErr.Err
:
// you can probably work with straight conversion from interface to target type :
if g,ok := err.(*gin.Error); ok {
if _,ok := g.Err.(*json.UnmarshalTypeError); ok {
log.Println("Json binding error")
}
}
// or use errors.As() :
var g *gin.Error
if errors.As(err,&g) {
var j *json.UnmarshalTypeError
if errors.As(g.Err,&j) {
log.Println("Json binding error")
}
}
我的最初答案:
(修正@Fllimzy的答案)
- 使用
errors.As
- 由于
json.UnmarshalTypeError
是一个结构(而不是接口),因此您必须在json.UnmarshalTypeError
和*json.UnmarshalTypeError
之间进行明确区分
尝试运行:
var jsonErr *json.UnmarshalTypeError // emphasis on the '*'
if errors.As(e.Err,&jsonErr) {
log.Println("Json binding error")
}
以下是errors.As()
行为的说明:
https://play.golang.org/p/RVz6xop5k4u
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。