使用curl调用golang的jsonrpc

7

我有一个用Go语言编写的“hello world” RPC服务,它可以正常运行,并且Go jsonrpc客户端也可以使用。但是我需要使用curl发送请求,但是这个例子无法正常工作:

curl \
-X POST \
-H "Content-Type: application/json" \
-d '{"id": 1, "method": "Test.Say", "params": [{"greet": "world"}]}' \
http://localhost:1999/_goRPC_

Go接受连接但不产生任何结果:

curl: (52) Empty reply from server 

这是我的Go语言代码:

package main

import (
  "log"
  "os"
  "time"
  "net"
  "net/rpc"
  "net/rpc/jsonrpc"
)

// RPC Api structure
type Test struct {}

// Greet method arguments
type GreetArgs struct {
  Name string
}

// Grret message accept object with single param Name
func (test *Test) Greet(args *GreetArgs, result *string) (error) {
  *result = "Hello " + args.Name
  return nil
}

// Start server with Test instance as a service
func startServer(ch chan<- bool, port string) {
  test := new(Test)

  server := rpc.NewServer()
  server.Register(test)

  listener, err := net.Listen("tcp", ":" + port)

  if err != nil {
      log.Fatal("listen error:", err)
  }

  defer listener.Close()

  for {
      conn, err := listener.Accept()

      if err != nil {
          log.Fatal(err)
      }

      go server.ServeCodec(jsonrpc.NewServerCodec(conn))
      ch <- true
  }
}

// Start client and call Test.Greet method
func startClient(port string) {
  conn, err := net.Dial("tcp", ":" + port)

  if err != nil {
      panic(err)
  }
  defer conn.Close()

  c := jsonrpc.NewClient(conn)

  var reply string
  var args = GreetArgs{"world"}
  err = c.Call("Test.Greet", args, &reply)
  if err != nil {
      log.Fatal("arith error:", err)
  }
  log.Println("Result: ", reply)
}

func main() {
  if len(os.Args) < 2 {
    log.Fatal("port not specified")
  }

  port := os.Args[1]
  ch := make(chan bool)

  go startServer(ch, port)
  time.Sleep(500 * time.Millisecond)
  go startClient(port)

  // Produce log message each time connection closes
  for {
    <-ch
    log.Println("Closed")
  }
}
2个回答

8

目前,jsonrpc包不支持json-rpc通过HTTP进行调用。因此,您不能使用curl调用jsonrpc。如果您真的想这样做,可以创建一个HTTP处理程序,将HTTP请求/响应适配到ServerCodec。例如:

package main

import (
    "io"
    "log"
    "net"
    "net/http"
    "net/rpc"
    "net/rpc/jsonrpc"
    "os"
)

type HttpConn struct {
    in  io.Reader
    out io.Writer
}

func (c *HttpConn) Read(p []byte) (n int, err error)  { return c.in.Read(p) }
func (c *HttpConn) Write(d []byte) (n int, err error) { return c.out.Write(d) }
func (c *HttpConn) Close() error                      { return nil }

// RPC Api structure
type Test struct{}

// Greet method arguments
type GreetArgs struct {
    Name string
}

// Grret message accept object with single param Name
func (test *Test) Greet(args *GreetArgs, result *string) error {
    *result = "Hello " + args.Name
    return nil
}

// Start server with Test instance as a service
func startServer(port string) {
    test := new(Test)

    server := rpc.NewServer()
    server.Register(test)

    listener, err := net.Listen("tcp", ":"+port)

    if err != nil {
        log.Fatal("listen error:", err)
    }

    defer listener.Close()
    http.Serve(listener, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {

        if r.URL.Path == "/test" {
            serverCodec := jsonrpc.NewServerCodec(&HttpConn{in: r.Body, out: w})
            w.Header().Set("Content-type", "application/json")
            w.WriteHeader(200)
            err := server.ServeRequest(serverCodec)
            if err != nil {
                log.Printf("Error while serving JSON request: %v", err)
                http.Error(w, "Error while serving JSON request, details have been logged.", 500)
                return
            }
        }

    }))
}

func main() {
    if len(os.Args) < 2 {
        log.Fatal("port not specified")
    }

    port := os.Args[1]

    startServer(port)
}

现在您可以使用以下命令进行调用:curl -X POST -H "Content-Type: application/json" -d '{"id": 1, "method": "Test.Greet", "params": [{"name":"world"}]}' http://localhost:port/test 代码的一部分来自这篇帖子

看起来不太美观,但它能用!谢谢)也许这段代码应该是一个独立的包? - Paul Rumkin
是的,这只是一个简单的例子。 实际上,已经有了这样的软件包,这里有一个。 - jfly
这是一个独立的软件包吗? - Paul Rumkin
是的,使用 go get github.com/gorilla/rpc/json 来安装它。更多详情请查看这里 - jfly
谢谢!这正是我需要的! - Paul Rumkin

6

@jfly提供了一个巧妙的解决方案。

另一个选项是,如果您仍然想使用除go jsonrpc client(可能是最简单的选项)以外的东西进行测试或者使用@jfly的答案,您可以使用telnet发送原始数据:

computer:~ User$ telnet 127.0.0.1 8888
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
{"method":"Test.Greet","params":[{"Name":"world"}],"id":0}
{"id":0,"result":"Hello world","error":null}
{"method":"Test.Greet","params":[{"Name":"world"}],"id":0}
{"id":0,"result":"Hello world","error":null}
{"method":"Test.Greet","params":[{"Name":"world"}],"id":0}
{"id":0,"result":"Hello world","error":null}

以上是我输入的有效载荷和服务器响应的输出。

在我找到正确的有效载荷时,tcpdump 是我的好帮手。


感谢您的补充,非常有帮助。 - Paul Rumkin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接