The interface I wrote was given to test. There is no page yet, so I will directly test the interface. use
(&req)
There is always an error, and the general error message is as follows:
err="invalid character '-' in numeric literal"
This is because my interface requires the parameters to be passed to the background in json format. As a result, my test colleague used the form-data format, so the above error occurred.
=============Supplement 2018-11-09 18:20:00=============
The EOF problem just happened again. The front-end confirmed that the parameters have been transferred in the json format, but there is still this problem.
Through wireshark packet capture, it was found that the Content-Length given by the front end is 0, which means that the parameters were not passed into the background.
Later, the front-end checked the code and found that the parameters were not passed in, but they were defined.
Supplement: gin json get_Gin framework series custom error handling
Overview
Many readers asked me for the demo source code of the Gin framework practical series in the background. Let me explain it here. I have updated the source code to GitHub, address:/xinliangnote/Go
Starting today's article, why customize error handling? What is the default error handling method?
OK, let’s talk about the default error handling first.
The default error handling is ("Error Message"), which is returned by the return value of type error.
Let's give a simple example:
func hello(name string) (str string, err error) { if name == "" { err = ("name cannot be empty") return } str = ("hello: %s", name) return }
When this method is called:
var name = "" str, err := hello(name) if err != nil { (()) return }
This is the default error handling, and I will use this example to illustrate it below.
This default error handling just gets a string of error messages.
However...
I also want to get information such as time, file name, method name, line number, etc. when the error occurs.
I also want to alert you when I get an error, such as SMS alarm, email alarm, WeChat alarm, etc.
When I want to call it, it is not that complicated, and it is similar to the default error handling, such as:
("error message") return
In this way, we get the information we want (time, file name, method name, line number) and notify us of the alarm through WeChat.
Similarly, ("Error Message") and ("Error Message") The information we get is the same, but the alarm method is different.
It is also necessary to ensure that in our business logic, only error information can be obtained when obtaining errors.
What you think of above is what you want to implement today. Custom error handling. Before we implement it, let’s talk about Go’s error handling.
Error handling
package main import ( "errors" "fmt" ) func hello(name string) (str string, err error) { if name == "" { err = ("name cannot be empty") return } str = ("hello: %s", name) return } func main() { var name = "" ("param:", name) str, err := hello(name) if err != nil { (()) return } (str) }
Output:
param: Tom
hello: Tom
When name = "" , the output is:
param:
name cannot be empty
It is recommended that each function have error handling, and error should return the value for the last one.
Let's take a look at the official
// Copyright 2011 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Package errors implements functions to manipulate errors. package errors // New returns an error that formats as the given text. func New(text string) error { return &errorString{text} } // errorString is a trivial implementation of error. type errorString struct { s string } func (e *errorString) Error() string { return }
The above code is not complicated. Let’s write a custom error handling based on the above.
Custom error handling
Let's define a , which is used to handle alarms.
Without further ado, just look at the code.
package alarm import ( "encoding/json" "fmt" "ginDemo/common/function" "path/filepath" "runtime" "strings" ) type errorString struct { s string } type errorInfo struct { Time string `json:"time"` Alarm string `json:"alarm"` Message string `json:"message"` Filename string `json:"filename"` Line int `json:"line"` Funcname string `json:"funcname"` } func (e *errorString) Error() string { return } func New (text string) error { alarm("INFO", text) return &errorString{text} } // Send an emailfunc Email (text string) error { alarm("EMAIL", text) return &errorString{text} } // Send a text messagefunc Sms (text string) error { alarm("SMS", text) return &errorString{text} } // Send WeChatfunc WeChat (text string) error { alarm("WX", text) return &errorString{text} } // Alarm methodfunc alarm(level string, str string) { // Current timecurrentTime := () // Define file name, line number, method namefileName, line, functionName := "?", 0 , "?" pc, fileName, line, ok := (2) if ok { functionName = (pc).Name() functionName = (functionName) functionName = (functionName, ".") } var msg = errorInfo { Time : currentTime, Alarm : level, Message : str, Filename : fileName, Line : line, Funcname : functionName, } jsons, errs := (msg) if errs != nil { ("json marshal error:", errs) } errorJsonInfo := string(jsons) (errorJsonInfo) if level == "EMAIL" { // Execute email} else if level == "SMS" { // Perform text messages} else if level == "WX" { // Execute WeChat message} else if level == "INFO" { // Execution log} }
See how to call:
package v1 import ( "fmt" "ginDemo/common/alarm" "ginDemo/entity" "/gin-gonic/gin" "net/http" ) func AddProduct(c *) { // Get the Get parametersname := ("name") var res = {} str, err := hello(name) if err != nil { (entity.CODE_ERROR) (()) (, res) () return } (entity.CODE_SUCCESS) (str) (, res) } func hello(name string) (str string, err error) { if name == "" { err = ("name cannot be empty") return } str = ("hello: %s", name) return }
Visit: http://localhost:8080/v1/product/add?name=a
{ "code": 1, "msg": "hello: a", "data": null }
No error was thrown and no information was output.
Visit: http://localhost:8080/v1/product/add
{ "code": -1, "msg": "name cannot be empty", "data": null }
An error was thrown, and the output information is as follows:
{"time":"2019-07-23 22:19:17","alarm":"WX","message":"name cannot be empty","filename":"absolute path/ginDemo/router/v1/","line":33,"funcname":"hello"}
Maybe this will be said: "Using the data binding and verification shared in the previous article, binding the incoming parameters: "required" can also be achieved."
I can only say: "Student, you don't understand my good intentions. This is just an example. You can use custom error handling in some complex business logic judgment scenarios."
At this point, when we reported an error, we received the time, error message, file name, line number, and method name.
It's relatively simple to call.
Although the alarm method is marked, there is still no alarm notification.
I want to say that if you store data in the queue here, and then execute asynchronous tasks to consume it in detail, this will not be implemented, and you can improve it.
Reading file name, method name, line number is used ().
We also know that Go has panic and recover, what do they do, let’s talk about it next.
panic and recover
When the program cannot continue running, panic should be used to throw an error.
When panic occurs in the program, recover can be called inside the defer (delay function) for control, but there is a prerequisite, only in the same Go coroutine.
Panic is divided into two types, one is intentionally thrown, and the other is caused by unintentional sloppy writing procedures. Let’s talk about them one by one.
Intentionally thrown panic:
package main import ( "fmt" ) func main() { ("-- 1 --") defer func() { if r := recover(); r != nil { ("panic: %s\n", r) } ("-- 2 --") }() panic("i am panic") }
Output:
-- 1 --
panic: i am panic
-- 2 --
Unintentionally thrown panic:
package main import ( "fmt" ) func main() { ("-- 1 --") defer func() { if r := recover(); r != nil { ("panic: %s\n", r) } ("-- 2 --") }() var slice = [] int {1, 2, 3, 4, 5} slice[6] = 6 }
Output:
-- 1 --
panic: runtime error: index out of range
-- 2 --
We have captured both of the above through recovery, so how do we use it in the Gin framework? How to implement the alarm if you want to send an alarm when you receive a panic?
Since you want to implement an alarm, first define a Panic() method in . When a panic exception occurs in the project, call this method, so that the alarm will be implemented.
// Panic exceptionfunc Panic (text string) error { alarm("PANIC", text) return &errorString{text} }
So how do we capture it?
Use middleware to capture and write a recover middleware.
package recover import ( "fmt" "ginDemo/common/alarm" "/gin-gonic/gin" ) func Recover() { return func(c *) { defer func() { if r := recover(); r != nil { (("%s", r)) } }() () } }
Route calling middleware:
((), ()) //Use can pass multiple middleware.
Let's verify, let's throw two exceptions first to see if they can be caught?
It's better to modify this file.
Intentionally throwing panic:
package v1 import ( "fmt" "ginDemo/entity" "/gin-gonic/gin" "net/http" ) func AddProduct(c *) { // Get the Get parametersname := ("name") var res = {} str, err := hello(name) if err != nil { (entity.CODE_ERROR) (()) (, res) () return } (entity.CODE_SUCCESS) (str) (, res) } func hello(name string) (str string, err error) { if name == "" { // Intentionally throwing panicpanic("i am panic") return } str = ("hello: %s", name) return }
Visit: http://localhost:8080/v1/product/add
The interface is blank.
An exception was thrown, and the output information is as follows:
{"time":"2019-07-23 22:42:37","alarm":"PANIC","message":"i am panic","filename":"absolute path/ginDemo/middleware/recover/","line":13,"funcname":"1"}
Obviously, the file name, method name, and line number that are located are not what we want.
Requires adjustment (2), this code is in the alarm method of .
Adjust 2 to 4 and look at the output information:
{"time":"2019-07-23 22:45:24","alarm":"PANIC","message":"i am panic","filename":"absolute path/ginDemo/router/v1/","line":33,"funcname":"hello"}
That's right.
Unintentionally throw panic:
// The above code remains unchangedfunc hello(name string) (str string, err error) { if name == "" { // Unintentionally throw panicvar slice = [] int {1, 2, 3, 4, 5} slice[6] = 6 return } str = ("hello: %s", name) return }
Visit: http://localhost:8080/v1/product/add
The interface is blank.
An exception was thrown, and the output information is as follows:
{"time":"2019-07-23 22:50:06","alarm":"PANIC","message":"runtime error: index out of range","filename":"absolute path/runtime/","line":44,"funcname":"panicindex"}
Obviously, the file name, method name, and line number that are located are not what we want.
Adjust 4 to 5 and look at the output information:
{"time":"2019-07-23 22:55:27","alarm":"PANIC","message":"runtime error: index out of range","filename":"absolute path/ginDemo/router/v1/","line":34,"funcname":"hello"}
That's right.
Strange, why is this?
Here, it is necessary to talk about (skip).
skip refers to the depth of the call.
When 0, print the current call file and number of lines.
When 1 is printed, the file and number of lines called by the previous level are printed.
And so on...
In this area, I need to pay attention to when calling, I don’t have a good solution yet.
I'm passing skip (calling depth) when a parameter is passed in.
for example:
// Send WeChatfunc WeChat (text string) error { alarm("WX", text, 2) return &errorString{text} } // Panic exceptionfunc Panic (text string) error { alarm("PANIC", text, 5) return &errorString{text} }
I won't post the specific code.
However, the call depths of intentionally throwing Panic and unintentionally throwing Panic are different, what should I do?
1. Try to change the Panic that is intentionally thrown into the way of throwing the error.
2. Think of other ways to get it done.
That's it.
I will update the code involved to GitHub.
The above is personal experience. I hope you can give you a reference and I hope you can support me more. If there are any mistakes or no complete considerations, I would like to give you advice.