du31992
du31992
采纳率0%
2019-01-14 10:28

jsonrpc服务器接受请求的小写方法名称(用于大写注册的服务)

已采纳

I'm trying to write a jsonrpc server that will accept requested method names in lower case, eg Arith.multiply, and correctly route them to the corresponding uppercase methed, e.g Arith.Multiply. Is this possible?

P.S. It's lightweight clone of production server for testing, the API is fixed, including the lowercase method names, so I can't change requested method names to uppercase.

package main

import (
    "log"
    "net/http"

    "github.com/gorilla/mux"
    "github.com/gorilla/rpc"
    "github.com/gorilla/rpc/json"
)

type Args struct {
    A, B int
}

type Arith int

type Result int

func (t *Arith) Multiply(r *http.Request, args *Args, result *Result) error {
    log.Printf("Multiplying %d with %d
", args.A, args.B)
    *result = Result(args.A * args.B)
    return nil
}

func main() {
    s := rpc.NewServer()
    s.RegisterCodec(json.NewCodec(), "application/json")
    s.RegisterCodec(json.NewCodec(), "application/json;charset=UTF-8")
    arith := new(Arith)
    s.RegisterService(arith, "")

    r := mux.NewRouter()
    r.Handle("/rpc", s)
    http.ListenAndServe(":1234", r)
}

  • 点赞
  • 写回答
  • 关注问题
  • 收藏
  • 复制链接分享
  • 邀请回答

2条回答

  • du8864 du8864 2年前

    It appears you can sneak something into a custom Codec to direct the lowercase method to the correct uppercase one. Compose a CodecRequest of the gorilla/rpc/json implementation and you can continue to use all of the gorilla machinery to process the request.

    Working example below. It looks long but it's all comments.

    package main
    
    import (
        "fmt"
        "log"
        "net/http"
        "strings"
        "unicode"
        "unicode/utf8"
    
        "github.com/gorilla/mux"
        "github.com/gorilla/rpc"
        "github.com/gorilla/rpc/json"
    )
    
    type Args struct {
        A, B int
    }
    type Arith int
    
    type Result int
    
    func (t *Arith) Multiply(r *http.Request, args *Args, result *Result) error {
        log.Printf("Multiplying %d with %d
    ", args.A, args.B)
        *result = Result(args.A * args.B)
        return nil
    }
    
    // UpCodec creates a CodecRequest to process each request.
    type UpCodec struct {
    }
    
    // NewUpCodec returns a new UpCodec.
    func NewUpCodec() *UpCodec {
        return &UpCodec{}
    }
    
    // NewRequest returns a new CodecRequest of type UpCodecRequest.
    func (c *UpCodec) NewRequest(r *http.Request) rpc.CodecRequest {
        outerCR := &UpCodecRequest{}   // Our custom CR
        jsonC := json.NewCodec()       // json Codec to create json CR
        innerCR := jsonC.NewRequest(r) // create the json CR, sort of.
    
        // NOTE - innerCR is of the interface type rpc.CodecRequest.
        // Because innerCR is of the rpc.CR interface type, we need a
        // type assertion in order to assign it to our struct field's type.
        // We defined the source of the interface implementation here, so
        // we can be confident that innerCR will be of the correct underlying type
        outerCR.CodecRequest = innerCR.(*json.CodecRequest)
        return outerCR
    }
    
    // UpCodecRequest decodes and encodes a single request. UpCodecRequest
    // implements gorilla/rpc.CodecRequest interface primarily by embedding
    // the CodecRequest from gorilla/rpc/json. By selectively adding
    // CodecRequest methods to UpCodecRequest, we can modify that behaviour
    // while maintaining all the other remaining CodecRequest methods from
    // gorilla's rpc/json implementation
    type UpCodecRequest struct {
        *json.CodecRequest
    }
    
    // Method returns the decoded method as a string of the form "Service.Method"
    // after checking for, and correcting a lowercase method name
    // By being of lower depth in the struct , Method will replace the implementation
    // of Method() on the embedded CodecRequest. Because the request data is part
    // of the embedded json.CodecRequest, and unexported, we have to get the
    // requested method name via the embedded CR's own method Method().
    // Essentially, this just intercepts the return value from the embedded
    // gorilla/rpc/json.CodecRequest.Method(), checks/modifies it, and passes it
    // on to the calling rpc server.
    func (c *UpCodecRequest) Method() (string, error) {
        m, err := c.CodecRequest.Method()
        if len(m) > 1 && err == nil {
            parts := strings.Split(m, ".")
            service, method := parts[0], parts[1]
            r, n := utf8.DecodeRuneInString(method) // get the first rune, and it's length
            if unicode.IsLower(r) {
                upMethod := service + "." + string(unicode.ToUpper(r)) + method[n:]
                log.Printf("lowercase method %s requested: treated as %s
    ", m, upMethod)
                return upMethod, err
            }
        }
        return m, err
    }
    
    func main() {
        s := rpc.NewServer()
    
        // Register our own Codec
        s.RegisterCodec(NewUpCodec(), "application/json")
        s.RegisterCodec(NewUpCodec(), "application/json;charset=UTF-8")
    
        arith := new(Arith)
        s.RegisterService(arith, "")
        r := mux.NewRouter()
        r.Handle("/rpc", s)
        fmt.Println(http.ListenAndServe(":1234", r))
    }
    

    Call the method via:

    curl -X POST -H "Content-Type: application/json" -d '{"id": 1, "method": "Arith.multiply", "params": [{"A": 10, "B": 30}]}' 127.0.0.1:1234/rpc
    
    点赞 评论 复制链接分享
  • dongpu1908 dongpu1908 2年前

    It's a feature of Go that to export an identifier, it starts with upper case

    https://golang.org/ref/spec#Exported_identifiers

    To use the "Multiply" method in the way you are suggesting, it must be exported. So it must start with an upper case character

    点赞 评论 复制链接分享

相关推荐