使用Gorilla/rpc在Golang中进行JSON RPC请求

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

JSON RPC request in Golang with Gorilla/rpc

问题

我正在尝试使用Gorilla/rpc包来建立一个RPC,以接收请求并回复响应(显然)。

首先,我正在尝试使用Gorilla/rpc提供的示例。

这是我的代码:

type HelloArgs struct {
	Who string
}

type HelloReply struct {
	Message string
}

type HelloService struct{}

func (h *HelloService) Say(r *http.Request, args *HelloArgs, reply *HelloReply) error {
	reply.Message = "Hello, " + args.Who + "!"
	return nil
}

func main() {
	r := mux.NewRouter()

	jsonRPC := rpc.NewServer()
	jsonCodec := json.NewCodec()
	jsonRPC.RegisterCodec(jsonCodec, "application/json")
	jsonRPC.RegisterCodec(jsonCodec, "application/json; charset=UTF-8") // For firefox 11 and other browsers which append the charset=UTF-8
	jsonRPC.RegisterService(new(HelloService), "")
	r.Handle("/api", jsonRPC)

	http.ListenAndServe(":"+port, nil)
}

我有几个问题:

  1. 我不确定如何设置Access-Control-Allow-Origin头,就像我通常在http.ResponseWriter上(使用常规的Web服务器)为跨域请求设置一样,因为它不接受http.ResponseWriter作为参数。

  2. 我应该发送什么来访问HelloService.Say方法?我尝试过{ method: "HelloService.Say", params:[{Who: "Me"}]},但是我得到了405 (Method Not Allowed)(不确定是否因为我不能进行跨域请求?)

非常感谢任何见解。

英文:

I'm trying to use the Gorilla/rpc package to set up a RPC to receive a request and reply with a response (obviously).

First I'm trying with the example provided with Gorilla/rpc

Here's my code:

type HelloArgs struct {
	Who string
}

type HelloReply struct {
	Message string
}

type HelloService struct{}

func (h *HelloService) Say(r *http.Request, args *HelloArgs, reply *HelloReply) error {
	reply.Message = "Hello, " + args.Who + "!"
	return nil
}

func main() {
	r := mux.NewRouter()

	jsonRPC := rpc.NewServer()
	jsonCodec := json.NewCodec()
	jsonRPC.RegisterCodec(jsonCodec, "application/json")
	jsonRPC.RegisterCodec(jsonCodec, "application/json; charset=UTF-8") // For firefox 11 and other browsers which append the charset=UTF-8
	jsonRPC.RegisterService(new(HelloService), "")
	r.Handle("/api", jsonRPC)

	http.ListenAndServe(":"+port, nil)
}

I have a couple of issues:

  1. I'm not sure how I would set a Access-Control-Allow-Origin header like I normally would on a http.ResponseWriter (with a regular webserver) for cross domain requests, as this doesn't take a http.ResponseWriter as an argument.

  2. What would I actually send to access the HelloService.Say method? I've tried { method: "HelloService.Say", params:[{Who: "Me"}]} but I get 405 (Method Not Allowed) (not sure if this is because I can't make x-domain requests though?)

Any insight greatly appreciated.

答案1

得分: 2

Gorilla/rpc/jsonCodecRequest.WriteResponse(实现了Gorilla/rpcCodecRequest)是代码中唯一涉及http.ResponseWriter的地方。

这意味着我们必须有自己的CodecRequest实现来设置CORS头。

服务器使用的每个CodecRequest实际上都是由Codec生成的;可以说,Codec是生成CodecRequest的工厂。

这意味着我们必须创建一个Codec来生成设置CORS头的CodecRequest

Go的一个很棒的特点是,组合这种额外行为非常容易!

试试这个:

package cors_codec
import (
    "Gorilla/rpc"
    "net/http"
    "strings"
)
//interface: ain't nobody dope like me I feel so fresh and clean
func CodecWithCors([]string corsDomains, unpimped rpc.Codec) rpc.Codec {
    return corsCodecRequest{corsDomains, unpimped}
}


type corsCodecRequest struct {
    corsDomains []string
    underlyingCodecRequest rpc.CodecRequest
}

//override exactly one method of the underlying anonymous field and delegate to it.
func (ccr corsCodecRequest) WriteResponse(w http.ResponseWriter, reply interface{}, methodErr error) error {
    w.Header().add("Access-Control-Allow-Origin", strings.join(ccr.corsDomains, " "))
    return ccr.underlyingCodecRequest.WriteResponse(w, reply, error)
}

type corsCodec struct {
    corsDomains []string
    underlyingCodec rpc.Codec
}

//override exactly one method of the underlying anonymous field and delegate to it.
func (cc corsCodec) NewRequest(req *http.Request) rpc.CodecRequest {
  return corsCodecRequest{cc.corsDomains, cc.underlyingCodec.NewRequest(req)}
}

这是一个有趣的练习!

英文:

EDITED: to fix bad use of 'type synonyms'

For number 1:

Gorilla/rpc/json's CodecRequest.WriteResponse (which implements Gorilla/rpc's CodecRequest) is the one place where the code touches an http.ResponseWriter.

This means that we have to have our own implementation of CodecRequest which sets the CORS header.

Every CodecRequest used by the server is actually generated by a Codec; Codecs are factories for making CodecRequests, to put it another way.

This means that we have to create a Codec to generate our CodecRequests that will set CORS headers.

The great thing about Go is that it's really easy to compose this extra behavior!

Try this:

package cors_codec
import (
    "Gorilla/rpc"
    "net/http"
    "strings"
)
//interface: ain't nobody dope like me I feel so fresh and clean
func CodecWithCors([]string corsDomains, unpimped rpc.Codec) rpc.Codec {
    return corsCodecRequest{corsDomains, unpimped}
}


type corsCodecRequest struct {
    corsDomains []string
    underlyingCodecRequest rpc.CodecRequest
}

//override exactly one method of the underlying anonymous field and delegate to it.
func (ccr corsCodecRequest) WriteResponse(w http.ResponseWriter, reply interface{}, methodErr error) error {
    w.Header().add("Access-Control-Allow-Origin", strings.join(ccr.corsDomains, " "))
    return ccr.underlyingCodecRequest.WriteResponse(w, reply, error)
}

type corsCodec struct {
    corsDomains []string
    underlyingCodec rpc.Codec
}

//override exactly one method of the underlying anonymous field and delegate to it.
func (cc corsCodec) NewRequest(req *http.Request) rpc.CodecRequest {
  return corsCodecRequest{cc.corsDomains, cc.underlyingCodec.NewRequest(req)}
}

That was a fun exercise!

答案2

得分: 0

我知道这是一个相当旧的问题,但实际上有一种更简单的方法来实现这个。

你可以通过rs/corsjustinas/alice包在几行代码中实现这个。

func main() {
	chain := alice.New(cors.Default().Handler)

	server := rpc.NewServer()
	server.RegisterCodec(json2.NewCodec(), "application/json")
	server.RegisterService(new(HelloService), "")

	http.Handle("/rpc", chain.Then(server))

	http.ListenAndServe(":8081", nil)
}
英文:

I know this is a pretty old question but there is actually an easier way to achieve this.

You can do this thanks to rs/cors and justinas/alice packages in few lines.

func main() {
	chain := alice.New(cors.Default().Handler)

	server := rpc.NewServer()
	server.RegisterCodec(json2.NewCodec(), "application/json")
	server.RegisterService(new(HelloService), "")

	http.Handle("/rpc", chain.Then(server))

	http.ListenAndServe(":8081", nil)
}

huangapple
  • 本文由 发表于 2012年10月12日 09:44:27
  • 转载请务必保留本文链接:https://go.coder-hub.com/12851109.html
匿名

发表评论

匿名网友

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

确定