gob deserialization can not work well on the nested struct - go

I am writing an rpc call flow. When I use tcp read and write, I found another nested struct problem. I created a request and response object that contains a traceId and struct request object or return object struct, but when I write back the client's response object on the server, I can't get the correct reply on the client gob deserialization The object entity can only get traceId. I want to ask what is the reason for this. The code is as follows
enter image description here
// Server
for {
// Read data from connection
data, err := readData(conn)
if err != nil {
if err.Error() == "EOF" {
return
} else {
log.Printf("Server readData error: %v, remoteAddr:%v", err.Error(), conn.RemoteAddr().String())
return
}
} else if len(data) == 0 {
continue
}
// Decode the data from client request
var req Request
edcode, err := GetEdcode()
if err != nil {
log.Printf("Server encode error: %v, remoteAddr:%v", err.Error(), conn.RemoteAddr().String())
return
}
err = edcode.Decode(data, &req)
if err != nil {
log.Printf("Server decode error: %v, remoteAddr:%v", err.Error(), conn.RemoteAddr().String())
return
}
// Get the service from the registerd service map
methodStr := strings.Split(req.MethodName, ".")
if len(methodStr) != 2 {
log.Printf("Server methodStr length invalid: %v, remoteAddr:%v", err.Error(), conn.RemoteAddr().String())
return
}
service := server.ServiceMap[methodStr[0]][methodStr[1]]
// Construct the request args
argv, err := req.MakeArgs(edcode, *service)
// Construct reply data
reply := reflect.New(service.ReplyType.Elem())
// Call the method related
function := service.Method.Func
out := function.Call([]reflect.Value{reflect.New(server.ServerType.Elem()), argv, reply})
if out[0].Interface() != nil {
log.Printf("Server out result length invalid: %v, remoteAddr:%v", err.Error(), conn.RemoteAddr().String())
return
}
// Construct response data
response := NewResponse(req.TraceId, reply.Elem().Interface())
// We should register the matching type related to the GOB encoding to prevent the encoding error
codeErr := response.RegisterGobRespType()
if codeErr != nil {
log.Printf("Server response.RegisterGobRespType eror : %v, remoteAddr:%v", codeErr.Error(), conn.RemoteAddr().String())
return
}
// Encode the reply data to send to the client
//replyData, err := edcode.Encode(reply.Elem().Interface())
replyData, err := edcode.Encode(response)
if err != nil {
log.Printf("Server replayData encode error: %v, remoteAddr:%v", err.Error(), conn.RemoteAddr().String())
return
}
// Write data to connection
_, err = writeData(conn, replyData)
if err != nil {
return
}
}
Client
// Receive and read the encoding response data from client
replyData, err := readData(conn)
if err != nil {
log.Printf("Client writeData eror : %v, remoteAddr:%v", err.Error(), conn.RemoteAddr().String())
return err
}
var response etnet.Response;
// Decode data and assign it to reply variable
//var response etnet.Responseļ¼Œ response reply is always nil and I check the write value in server it is correct
edcode.Decode(replyData, &response)
type Request struct {
TraceId string
MethodName string
Args interface{}
}
type Response struct {
TraceId string
Reply interface{}
}
func NewRequest(traceId string, methodName string, args interface{}) *Request {
return &Request{
TraceId: traceId,
MethodName: methodName,
Args: args,
}
}
func NewResponse(traceId string, reply interface{}) *Response {
return &Response{
TraceId: traceId,
Reply: reply,
}
}
// If encode in GOB we should register the type of Args to prevent the GOB encode error
func (request *Request) RegisterGobArgsType() error {
edcodeStr := new(Config).GetEdcodeConf()
switch edcodeStr {
case "gob":
args := reflect.New(reflect.TypeOf(request.Args))
if args.Kind() == reflect.Ptr {
args = args.Elem()
}
gob.Register(args.Interface())
return nil
case "json":
return nil
default:
return errors.New("Unknown edcode protocol: " + edcodeStr)
}
}
// If encode in GOB we should register the type of Args to prevent the GOB encode error
func (response *Response) RegisterGobRespType() error {
edcodeStr := new(Config).GetEdcodeConf()
switch edcodeStr {
case "gob":
reply := reflect.New(reflect.TypeOf(response.Reply))
if reply.Kind() == reflect.Ptr {
reply = reply.Elem()
}
gob.Register(reply.Interface())
return nil
case "json":
return nil
default:
return errors.New("Unknown edcode protocol: " + edcodeStr)
}
}
// Return the reflect.Value type of Args
func (request *Request) MakeArgs(edcode Edcode, service Service) (reflect.Value, error) {
switch edcode.(type) {
case GobEncode:
return reflect.ValueOf(request.Args), nil
case JsonEncode:
reqArgs := request.Args.(map[string]interface{})
argv := reflect.New(service.ArgType)
err := MakeArgType(reqArgs, argv)
if err != nil {
log.Println(err.Error())
return reflect.New(nil), err
}
if argv.Kind() == reflect.Ptr {
argv = argv.Elem()
}
return argv, nil
default:
return reflect.ValueOf(request.Args), errors.New("Unknown edcode")
}
}

Related

Where is the message handler in go-ethereum?

I'm currently trying to figure out the Ethereum code and have learned how to send transactions to the blockchain using the client module.Here is an example of a contract call function:
func (ec *Client) CallContract(ctx context.Context, msg ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) {
var hex hexutil.Bytes
err := ec.c.CallContext(ctx, &hex, "eth_call", toCallArg(msg), toBlockNumArg(blockNumber))
if err != nil {
return nil, err
}
return hex, nil
}
, where CallContext defined as:`
func (c *Client) CallContext(ctx context.Context, result interface{}, method string, args ...interface{}) error {
if result != nil && reflect.TypeOf(result).Kind() != reflect.Ptr {
return fmt.Errorf("call result parameter must be pointer or nil interface: %v", result)
}
msg, err := c.newMessage(method, args...)
if err != nil {
return err
}
op := &requestOp{ids: []json.RawMessage{msg.ID}, resp: make(chan *jsonrpcMessage, 1)}
if c.isHTTP {
err = c.sendHTTP(ctx, op, msg)
} else {
err = c.send(ctx, op, msg)
}
if err != nil {
return err
}
// dispatch has accepted the request and will close the channel when it quits.
switch resp, err := op.wait(ctx, c); {
case err != nil:
return err
case resp.Error != nil:
return resp.Error
case len(resp.Result) == 0:
return ErrNoResult
default:
return json.Unmarshal(resp.Result, &result)
}`
And my question is: Where is the handler for these messages implemented in go - ethereum?
For example:
switch msg.name:
case "eth_call": ...
case "eth_sendTx": ...
...

Using or in bind and validation?

I'm using go gin with validation package called ozzo-validation and when I bind json data and validation error like this:
err := c.ShouldBind(&user)
valErrors := user.LoginValidate()
if err != nil || valErrors != nil {
util.ErrorJSON(c, http.StatusBadRequest, err.Error())
return
}
I got errors in console:
/usr/local/go/src/net/http/server.go:2878 +0x43b
net/http.(*conn).serve(0xc0002cebe0, {0x17430c0, 0xc0004a6f30})
/usr/local/go/src/net/http/server.go:1929 +0xb08
created by net/http.(*Server).Serve
/usr/local/go/src/net/http/server.go:3033 +0x4e8
when I convert the above code to the following it works!
var user models.User
// Decode request data
if err := c.ShouldBind(&user); err != nil {
util.ErrorJSON(c, http.StatusBadRequest, err.Error())
return
}
// Validate Error
if err := user.LoginValidate(); err != nil {
util.ErrorJSON(c, http.StatusBadRequest, err.Error())
return
}
what is variable user? is it a model? why don't you declare validation on your model with validatin-ozzo like :
func (f *user) Validate() error {
if err := validation.Validate(f.name, validation.Required); err != nil {
return &response.ErrorResponse{
ErrorID: 422,
Msg: map[string]string{
"en": "name cannot be empty",
},
}
}
return nil
}
type ErrorResponse struct {
ErrorID int `json:"error_id"`
Msg map[string]string `json:"message"`
}
func (c *ErrorResponse) Error() string {
b, _ := json.Marshal(c)
return string(b)
}
// and validate it on your code like :
if err := user.Validate(); err != nil {
return nil, err
}

Error unmarshalling message body from amqp

I'm trying to send a JSON inside the body of the message using AMQP and receive it. The message sends without any error, however I can't unmarshall it on the other side. I get a unexpected end of JSON input error.
JSON:
var jsonStr = []byte(`
{
"name": "Test",
"phone_number": "12345"
}`)
err = files.SendMessageToCore(jsonStr, "event.saveorder")
if err != nil {
logr.Error("Error sending a message to rabbitmq:", fmt.Sprintf("%v", err))
}
SendMessageToCore function:
func SendMessageToCore(body []byte, routingKey string) error {
pb, err := GetRabbitPublisher()
if err != nil {
return err
}
var message amqp.Message
message.Exchange = "example"
message.RoutingKey = routingKey
message.Body = body
messageByte, err := json.Marshal(message)
if err != nil {
return err
}
err = (*pb).Publish(amqp.Message{Exchange: message.Exchange, RoutingKey: message.RoutingKey, Body: messageByte})
if err != nil {
return err
}
fmt.Println("Message was successfully sent")
return nil
}
Unmarshalling part:
func SaveOrderEventHandler(mes amqp.Message) *amqp.Message {
var incomingMessage amqp.Message
if err := json.Unmarshal(mes.Body, &incomingMessage); err != nil {
fmt.Println(err)
return nil
}
var user foodDomain.User
if err := json.Unmarshal(incomingMessage.Body, &user); err != nil {
fmt.Println("Error unmarshalling incomingMessage body: ", err) //Error appears here
return nil
}
fmt.Println(user.PhoneNumber)
return nil
}
User struct:
type User struct {
Name string `json:"name"`
PhoneNumber string `json:"phone_number"`
}
In your SendMessageToCore you already initialized amqb.Message. No need to re-initialize in the Publish func, just pass your initialized message. Also, your json string body is already in jsonByte, so no need to marshal it once more.
For instance -
func SendMessageToCore(body []byte, routingKey string) error {
pb, err := GetRabbitPublisher()
if err != nil {
return err
}
var message amqp.Message
message.Exchange = "example"
message.RoutingKey = routingKey
message.Body = body
err = (*pb).Publish(message)
if err != nil {
return err
}
fmt.Println("Message was successfully sent")
return nil
}
And your mes already in amqp.Message type, so no need to unmarshal mes.Body into amqp.Message type variable.
Just directly unmarshal your mes.Body into user.
func SaveOrderEventHandler(mes amqp.Message) *amqp.Message {
var user foodDomain.User
if err := json.Unmarshal(mes.Body, &user); err != nil {
fmt.Println(err)
return nil
}
fmt.Println(user.PhoneNumber)
return nil
}

GoLang calling a function using gin without passing gin.Context

I've come across a scenario where
func main (c *gin.Context){
if err := g.Bind(&data); err != nil {
log.Fatalln(err)
helper.TraceLog(err)
helper.Fail(c, helper.INPUT_PARAMS_ERROR, "", err)
return
}
}
func Fail(c *gin.Context, errorCode string, result string, message interface{}) {
response := Response{}
response.Code = errorCode
response.Result = message
response.Message = result
json.Marshal(response)
c.JSON(http.StatusBadRequest, response)
}
Is it possible that I do not have to pass gin to Fail func? I've tried every solution I can think of and nothing works.
The reason I'm doing this is to make the code looks more simple and clean.
What I'm looking for is something like this:
func main (c *gin.Context){
if err := g.Bind(&data); err != nil {
log.Fatalln(err)
helper.TraceLog(err)
helper.Fail(helper.INPUT_PARAMS_ERROR, "", err)
return
}
}
func Fail(errorCode string, result string, message interface{}) {
var c *gin.Context
response := Response{}
response.Code = errorCode
response.Result = message
response.Message = result
json.Marshal(response)
c.JSON(http.StatusBadRequest, response)
}

How to judge unmarshal json interface{} type in golang?

I want to judge json type,but it always return "I don't know about type map[string]interface {}!",How to resolve it.
=========================================================================
type getRemoteCardInfo struct {
Code int
Msg string
Data []*remoteCardInfo
}
type remoteCardInfo struct {
Sn string
RemoteCardIp string
RemoteCardMac string
}
func Get_json_data(url string) (interface{}, error) {
client := &http.Client{}
req, err := http.NewRequest("GET", url, nil)
req.Header.Add("X-MYCMDB-Auth-Token", "sPf98SMBWzOZJEJB8KWltbJyKvFYPauu")
if err != nil {
return nil, err
}
resp, _ := client.Do(req)
if resp.StatusCode != http.StatusOK {
resp.Body.Close()
return nil, fmt.Errorf("query failed: %s", resp.Status)
}
var result interface{}
body, err := ioutil.ReadAll(resp.Body)
if err := json.Unmarshal(body, &result); err != nil {
log.Fatalf("JSON unmarshaling failed: %s", err)
}
resp.Body.Close()
fmt.Println(result)
return result, nil
}
func main() {
jsondata, err := Get_json_data(DHCPURL)
if err != nil {
log.Fatal(err)
}
switch v := jsondata.(type) {
case getRemoteCardInfo:
fmt.Println("aaaa")
default:
fmt.Printf("I don't know about type %T!\n", v)
}
The go JSON unmarshaler doesn't know about types, as you can tell by the fact that it stores the result into an interface{} value:
func Unmarshal(data []byte, v interface{}) error
// "v" can be any type -------^
So it's up to you to use the unmarshaler to populate your structure and determine if the result is valid or not.
In your example it looks like you're trying to unmarshal a remoteCardInfo from an HTTP response. To do this you should unmarshal into an empty remoteCardInfo struct and determine if the required fields were populated.
For example, suppose you expect a JSON document like so:
{
"sn": "123",
"ip": "0.0.0.0",
"mac": "ff:ff:ff:ff:ff:ff"
}
Then you should define your "remoteCardInfo" struct as below:
type remoteCardInfo struct {
Sn string `json:"sn"`
RemoteCardIp string `json:"ip"`
RemoteCardMac string `json:"mac"`
}
And then unmarshal and validate it like so:
func getRemoteCardInfo(bs []byte) (*remoteCardInfo, error) {
rci := remoteCardInfo{}
err := json.Unmarshal(bs, &rci)
if err != nil {
return nil, err
}
// Validate the expected fields
if rci.Sn == "" {
return nil, fmt.Errorf(`missing "sn"`)
}
if rci.RemoteCardIp == "" {
return nil, fmt.Errorf(`missing "ip"`)
}
if rci.RemoteCardMac == "" {
return nil, fmt.Errorf(`missing "mac"`)
}
return &rci, nil
}
Of course, you can validate the fields any way you like but the main thing to remember is that the unmarshaler only does the job of ensuring that the input byte array is a valid JSON document and populates the fields from the document into the fields defined by the value.
It cannot tell you what "type" of object the JSON document represents.

Resources