Call golang jsonrpc with curl

huangapple go评论78阅读模式
英文:

Call golang jsonrpc with curl

问题

我有一个用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结构
type Test struct {}

// Greet方法的参数
type GreetArgs struct {
  Name string
}

// Greet方法接受一个带有单个参数Name的对象
func (test *Test) Greet(args *GreetArgs, result *string) error {
  *result = "Hello " + args.Name
  return nil
}

// 使用Test实例作为服务启动服务器
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
  }
}

// 启动客户端并调用Test.Greet方法
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)

  // 每次连接关闭时产生日志消息
  for {
    <-ch
    log.Println("Closed")
  }
}

请问有什么我可以帮助您的吗?

英文:

I have "hello world" rpc service written in golang. It works fine and go jsonrpc client is working. But I need to send request with curl and this example doesn't work:

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

Go accept connection but produce absolutely no result:

curl: (52) Empty reply from server 

Here my go code:

package main
import (
&quot;log&quot;
&quot;os&quot;
&quot;time&quot;
&quot;net&quot;
&quot;net/rpc&quot;
&quot;net/rpc/jsonrpc&quot;
)
// 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 = &quot;Hello &quot; + args.Name
return nil
}
// Start server with Test instance as a service
func startServer(ch chan&lt;- bool, port string) {
test := new(Test)
server := rpc.NewServer()
server.Register(test)
listener, err := net.Listen(&quot;tcp&quot;, &quot;:&quot; + port)
if err != nil {
log.Fatal(&quot;listen error:&quot;, err)
}
defer listener.Close()
for {
conn, err := listener.Accept()
if err != nil {
log.Fatal(err)
}
go server.ServeCodec(jsonrpc.NewServerCodec(conn))
ch &lt;- true
}
}
// Start client and call Test.Greet method
func startClient(port string) {
conn, err := net.Dial(&quot;tcp&quot;, &quot;:&quot; + port)
if err != nil {
panic(err)
}
defer conn.Close()
c := jsonrpc.NewClient(conn)
var reply string
var args = GreetArgs{&quot;world&quot;}
err = c.Call(&quot;Test.Greet&quot;, args, &amp;reply)
if err != nil {
log.Fatal(&quot;arith error:&quot;, err)
}
log.Println(&quot;Result: &quot;, reply)
}
func main() {
if len(os.Args) &lt; 2 {
log.Fatal(&quot;port not specified&quot;)
}
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 {
&lt;-ch
log.Println(&quot;Closed&quot;)
}
}

答案1

得分: 8

jsonrpc包目前不支持通过HTTP进行json-rpc。所以,你不能使用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来调用它。

代码的一部分来自这篇帖子

英文:

The jsonrpc package doesn't support json-rpc over HTTP currently. So, you can't call jsonrpc with curl. If you really want to do that, you can make a HTTP handler that adapts the HTTP request/response to a ServerCodec. For example:

package main
import (
&quot;io&quot;
&quot;log&quot;
&quot;net&quot;
&quot;net/http&quot;
&quot;net/rpc&quot;
&quot;net/rpc/jsonrpc&quot;
&quot;os&quot;
)
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 = &quot;Hello &quot; + 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(&quot;tcp&quot;, &quot;:&quot;+port)
if err != nil {
log.Fatal(&quot;listen error:&quot;, err)
}
defer listener.Close()
http.Serve(listener, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == &quot;/test&quot; {
serverCodec := jsonrpc.NewServerCodec(&amp;HttpConn{in: r.Body, out: w})
w.Header().Set(&quot;Content-type&quot;, &quot;application/json&quot;)
w.WriteHeader(200)
err := server.ServeRequest(serverCodec)
if err != nil {
log.Printf(&quot;Error while serving JSON request: %v&quot;, err)
http.Error(w, &quot;Error while serving JSON request, details have been logged.&quot;, 500)
return
}
}
}))
}
func main() {
if len(os.Args) &lt; 2 {
log.Fatal(&quot;port not specified&quot;)
}
port := os.Args[1]
startServer(port)
}

Now you can call it with curl -X POST -H &quot;Content-Type: application/json&quot; -d &#39;{&quot;id&quot;: 1, &quot;method&quot;: &quot;Test.Greet&quot;, &quot;params&quot;: [{&quot;name&quot;:&quot;world&quot;}]}&#39; http://localhost:port/test

Part of the code is from this post

答案2

得分: 6

@jfly有一个很棒的解决方案。

另一个选项是,如果你还想尝试除了go jsonrpc客户端之外的其他东西(可能是最简单的选项),或者使用@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是我的好朋友。

英文:

@jfly has a nifty solution.

Another option, if you still wanted to test with something besides the go jsonrpc cient (probably the easiest option), or use @jfly's answer, is you can use telnet to send raw data:

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

The above is the output including payload I typed in and your server's responses.

tcpdump was my friend when I was figuring out the right payload to send.

huangapple
  • 本文由 发表于 2016年4月14日 05:45:03
  • 转载请务必保留本文链接:https://go.coder-hub.com/36610140.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定