gin解析json格式的数据出错的处理方案
写的接口给测试测试,现在还没有页面,直接测试接口。使用
总是报错,大致错误信息如下:
err="invalid character '-' in numeric literal"
这是由于我的接口要求将参数按照json格式传递到后台,结果测试同事使用了form-data格式,所以才会有上面这个错误。
=============补充2018-11-09 18:20:00=============
刚刚又出现了这个eof的问题,前端确定已经按照json格式传参,但是还是有这个问题。
通过wireshark抓包发现,前端给的content-length为0,说明没有将参数传入后台。
后来前端核查代码发现,确实是没有将参数传入,只是定义了
补充:gin json 获取_gin框架系列 自定义错误处理
概述
很多读者在后台向我要 gin 框架实战系列的 demo 源码,在这里再说明一下,源码我都更新到 github 上,地址:https://github.com/xinliangnote/go
开始今天的文章,为什么要自定义错误处理?默认的错误处理方式是什么?
那好,咱们就先说下默认的错误处理。
默认的错误处理是 errors.new("错误信息"),这个信息通过 error 类型的返回值进行返回。
举个简单的例子:
当调用这个方法时:
这就是默认的错误处理,下面还会用这个例子进行说。
这个默认的错误处理,只是得到了一个错误信息的字符串。
然而...
我还想得到发生错误时的 时间、 文件名、 方法名、 行号 等信息。
我还想得到错误时进行告警,比如 短信告警、 邮件告警、 微信告警 等。
我还想调用的时候,不那么复杂,就和默认错误处理类似,比如:
这样,我们就得到了我们想要的信息( 时间、 文件名、 方法名、 行号),并通过 微信 的方式进行告警通知我们。
同理, alarm.email("错误信息")、 alarm.sms("错误信息") 我们得到的信息是一样的,只是告警方式不同而已。
还要保证,我们业务逻辑中,获取错误的时候,只获取错误信息即可。
上面这些想出来的,就是今天要实现的,自定义错误处理,我们就实现之前,先说下 go 的错误处理。
错误处理
输出:
param: tom
hello: tom
当 name = "" 时,输出:
param:
name 不能为空
建议每个函数都要有错误处理,error 应该为最后一个返回值。
咱们一起看下官方 errors.go
上面的代码,并不复杂,参照上面的,咱们进行写一个自定义错误处理。
自定义错误处理
咱们定义一个 alarm.go,用于处理告警。
废话不多说,直接看代码。
看下如何调用:
访问:http://localhost:8080/v1/product/add?name=a
未抛出错误,不会输出信息。
访问:http://localhost:8080/v1/product/add
抛出了错误,输出信息如下:
{"time":"2019-07-23 22:19:17","alarm":"wx","message":"name 不能为空","filename":"绝对路径/gindemo/router/v1/product.go","line":33,"funcname":"hello"}
可能这会有同学说:“用上一篇分享的数据绑定和验证,将传入的参数进行 binding:"required" 也可以实现呀”。
我只能说:“同学呀,你不理解我的良苦用心,这只是个例子,大家可以在一些复杂的业务逻辑判断场景中使用自定义错误处理”。
到这里,报错时我们收到了 时间、 错误信息、 文件名、 行号、 方法名 了。
调用起来,也比较简单。
虽然标记了告警方式,还是没有进行告警通知呀。
我想说,在这里存储数据到队列中,再执行异步任务具体去消耗,这块就不实现了,大家可以去完善。
读取 文件名、 方法名、 行号 使用的是 runtime.caller()。
我们还知道,go 有 panic 和 recover,它们是干什么的呢,接下来咱们就说说。
panic 和 recover
当程序不能继续运行的时候,才应该使用 panic 抛出错误。
当程序发生 panic 后,在 defer(延迟函数) 内部可以调用 recover 进行控制,不过有个前提条件,只有在相同的 go 协程中才可以。
panic 分两个,一种是有意抛出的,一种是无意的写程序马虎造成的,咱们一个个说。
有意抛出的 panic:
输出:
-- 1 --
panic: i am panic
-- 2 --
无意抛出的 panic:
输出:
-- 1 --
panic: runtime error: index out of range
-- 2 --
上面的两个我们都通过 recover 捕获到了,那我们如何在 gin 框架中使用呢?如果收到 panic 时,也想进行告警怎么实现呢?
既然想实现告警,先在 ararm.go 中定义一个 panic() 方法,当项目发生 panic 异常时,调用这个方法,这样就实现告警了。
那我们怎么捕获到呢?
使用中间件进行捕获,写一个 recover 中间件。
路由调用中间件:
验证下吧,咱们先抛出两个异常,看看能否捕获到?
还是修改 product.go 这个文件吧。
有意抛出 panic:
访问:http://localhost:8080/v1/product/add
界面是空白的。
抛出了异常,输出信息如下:
{"time":"2019-07-23 22:42:37","alarm":"panic","message":"i am panic","filename":"绝对路径/gindemo/middleware/recover/recover.go","line":13,"funcname":"1"}
很显然,定位的文件名、方法名、行号不是我们想要的。
需要调整 runtime.caller(2),这个代码在 alarm.go的alarm 方法中。
将 2 调整成 4 ,看下输出信息:
{"time":"2019-07-23 22:45:24","alarm":"panic","message":"i am panic","filename":"绝对路径/gindemo/router/v1/product.go","line":33,"funcname":"hello"}
这就对了。
无意抛出 panic:
访问:http://localhost:8080/v1/product/add
界面是空白的。
抛出了异常,输出信息如下:
{"time":"2019-07-23 22:50:06","alarm":"panic","message":"runtime error: index out of range","filename":"绝对路径/runtime/panic.go","line":44,"funcname":"panicindex"}
很显然,定位的文件名、方法名、行号也不是我们想要的。
将 4 调整成 5 ,看下输出信息:
{"time":"2019-07-23 22:55:27","alarm":"panic","message":"runtime error: index out of range","filename":"绝对路径/gindemo/router/v1/product.go","line":34,"funcname":"hello"}
这就对了。
奇怪了,这是为什么?
在这里,有必要说下 runtime.caller(skip) 了。
skip 指的调用的深度。
为 0 时,打印当前调用文件及行数。
为 1 时,打印上级调用的文件及行数。
依次类推...
在这块,调用的时候需要注意下,我现在还没有好的解决方案。
我是将 skip(调用深度),当一个参数传递进去。
比如:
具体的代码就不贴了。
但是,有意抛出 panic 和 无意抛出 panic 的调用深度又不同,怎么办?
1、尽量将有意抛出的 panic 改成抛出错误的方式。
2、想其他办法搞定它。
就到这吧。
里面涉及到的代码,我会更新到 github。
以上为个人经验,希望能给大家一个参考,也希望大家多多支持。如有错误或未考虑完全的地方,望不吝赐教。