英文:
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 "Content-Type: application/json" \
-d '{"id": 1, "method": "Test.Say", "params": [{"greet": "world"}]}' \
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 (
"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")
}
}
答案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 (
"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)
}
Now you can call it with curl -X POST -H "Content-Type: application/json" -d '{"id": 1, "method": "Test.Greet", "params": [{"name":"world"}]}' 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 '^]'.
{"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}
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论