1 Star 0 Fork 0

golangx / jsonrpc

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
client.go 18.01 KB
一键复制 编辑 原始数据 按行查看 历史
luohancai 提交于 2020-06-24 23:32 . 添加SetData函数
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589
package jsonrpc
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"net/http"
"reflect"
"strconv"
)
// RPCClient 用于向提供JSON-RPC的后台发送基于HTTP协议的JSON-RPC请求.
type RPCClient interface {
// Call 用于发送JSON-RPC请求到服务端.
//
// 规则说明, 参数只能是数组或map,不能是基础数据类型.
// 所以有几个简单的规则要注意:
//
// 1. 无参数: Call("getinfo")
//
// 2. 单个基础数据参数: 参数值被封装在数组里面. 例如. Call("getByID", 1423)
//
// 3. 单个数组或map参数: 例如. Call("storePerson", &Person{Name: "Alex"})
//
// 4. 多个参数: 封装成数组. 例如. Call("setDetails", "Alex, 35, "Germany", true)
//
// 示例:
// Call("getinfo") -> {"method": "getinfo"}
// Call("getPersonId", 123) -> {"method": "getPersonId", "params": [123]}
// Call("setName", "Alex") -> {"method": "setName", "params": ["Alex"]}
// Call("setMale", true) -> {"method": "setMale", "params": [true]}
// Call("setNumbers", []int{1, 2, 3}) -> {"method": "setNumbers", "params": [1, 2, 3]}
// Call("setNumbers", 1, 2, 3) -> {"method": "setNumbers", "params": [1, 2, 3]}
// Call("savePerson", &Person{Name: "Alex", Age: 35}) -> {"method": "savePerson", "params": {"name": "Alex", "age": 35}}
// Call("setPersonDetails", "Alex", 35, "Germany") -> {"method": "setPersonDetails", "params": ["Alex", 35, "Germany"}}
//
Call(method string, params ...interface{}) (*RPCResponse, error)
// CallRaw 和Call类似,但是没有处理requests.Params字段.
// RPCRequest对象按提供的数据原样发送.
// 查看: NewRequest, RPCRequest, Params()
//
// 建议优先考虑Call和CallFor
CallRaw(request *RPCRequest) (*RPCResponse, error)
// CallFor 一个非常方便的函数,可以向服务器端发送JSON-RPC请求,并直接指定一个对象来存储响应
//
// out: 请求成功时将存储解析json结果的对象
// method和params: 和 Call() 方法的一样
//
// 如果请求不成功 (网络或http错误)或rpc响应返回一个错误,
// error将得到一个错误类型的值. 如果它是JSON-RPC错误类型,那它可以通过*RPCError解析.
//
CallFor(out interface{}, method string, params ...interface{}) error
// CallBatch 在一个请求中调用多个方法.
//
// 最直观的使用方式如下:
// CallBatch(RPCRequests{
// NewRequest("myMethod1", 1, 2, 3),
// NewRequest("myMethod2", "Test"),
// })
//
// 可以自己创建[]*RPCRequest数组,但不建议这样做,且应该注意以下几点:
// - field Params is sent as provided, so Params: 2 forms an invalid json (correct would be Params: []int{2})
// - you can use the helper function Params(1, 2, 3) to use the same format as in Call()
// - `JSONRPC`字段值会被覆盖为"2.0"
// - `ID`字段会被覆盖为一个映射到数组下标的值 (例如. requests[5].ID == 5)
//
// - 返回的RPCResponses是[]*RPCResponse
// - 注意RPCResponses的值是无序的, 这样就可能发生: responses[i] != responses[i].ID
// - RPCPersponses有一些有用的函数, 例如: responses.HasError(), 如果其中某一个请求响应是RPCError则返回true
CallBatch(requests RPCRequests) (RPCResponses, error)
// CallBatchRaw 在一个请求中调用多个RPCRequests.
// It sends the RPCRequests parameter is it passed (no magic, no id autoincrement).
//
// 建议优先考虑CallBatch.
//
// CallBatchRaw(RPCRequests{
// &RPCRequest{
// ID: 123, // 在CallBatchRaw里面,该值不会变
// JSONRPC: "wrong", // 在CallBatchRaw里面,该值不会变
// Method: "myMethod1",
// Params: []int{1}, // 没有处理, 所以必须确保是array或map
// },
// &RPCRequest{
// ID: 612,
// JSONRPC: "2.0",
// Method: "myMethod2",
// Params: Params("Alex", 35, true), // Params()方法可以处理参数
// },
// })
//
// - 返回的RPCResponses是[]*RPCResponse
// - 注意RPCResponses的值是无序的
// - 必须将id映射到您提供的id
// - RPCPersponses有一些有用的函数, 例如: responses.HasError(), 如果其中某一个请求响应是RPCError则返回true
CallBatchRaw(requests RPCRequests) (RPCResponses, error)
}
// RPCRequest 表示JSON-RPC请求对象.
//
// Method: 调用方法
//
// Params: 调用参数,可以是nil. 否则只能是object或map
//
// ID: 单个请求中值是1. 批量请求中每个请求的id值必须是唯一的.
//
// JSONRPC: JSON-RPC 2.0版本的值总是 "2.0"
//
// 大多数时候你不应该自己创建RPCRequest对象.而是使用 Call(), CallFor(), NewRequest()
//
// 如果要自己创建(例如CallRaw()), 组装参数考虑使用Params()函数.
//
// 例如,手动创建如下RPCRequest:
// request := &RPCRequest{
// Method: "myMethod",
// Params: Params("Alex", 35, true),
// }
//
// 如果知道自己在做什么,可以不用Params()以避免使用反射影响性能,但可能会创建不正确的rpc请求:
//request := &RPCRequest{
// Method: "myMethod",
// Params: 2, <-- 无效,因为单个基础数据类型的值必须包装在数组中 --> 没有使用Params()时
// }
//
// 正确方式:
// request := &RPCRequest{
// Method: "myMethod",
// Params: []int{2},
// }
type RPCRequest struct {
Method string `json:"method"`
Params interface{} `json:"params,omitempty"`
ID int `json:"id"`
JSONRPC string `json:"jsonrpc"`
}
// NewRequest 像使用Call()一样方便的返回一个RPCRequest
//
// e.g. NewRequest("myMethod", "Alex", 35, true)
func NewRequest(method string, params ...interface{}) *RPCRequest {
request := &RPCRequest{
Method: method,
Params: Params(params...),
JSONRPC: jsonrpcVersion,
}
return request
}
// RPCResponse 表示JSON-RPC响应对象
//
// Result: 如果没有发生错误,则保存rpc调用的结果,否则为nil。当然,成功的值也可能是nil。
// Error: 如果发生错误,则结果为RPCError对象, 成功则是nil。
// ID: 单个请求时的值是0. 批量请求中每个请求的id值必须是唯一的.
// JSONRPC: JSON-RPC 2.0版本的值总是 "2.0"
type RPCResponse struct {
JSONRPC string `json:"jsonrpc"`
Result interface{} `json:"result,omitempty"`
Error *RPCError `json:"error,omitempty"`
ID interface{} `json:"id"`
}
// RPCError 表示JSON-RPC出错时错误对象
//
// Code: 错误码
// Message: 简短的错误信息
// Data: 额外的错误数据, 值可以是nil
type RPCError struct {
Code int `json:"code"`
Message string `json:"message"`
Data interface{} `json:"data,omitempty"`
}
// Error function is provided to be used as error object.
func (e RPCError) Error() string {
return strconv.Itoa(e.Code) + ":" + e.Message
}
// SetData 给RPCError的Data赋值
func (e RPCError) SetData(data interface{}) RPCError {
e.Data = data
return e
}
// HTTPError represents a error that occurred on HTTP level.
//
// An error of type HTTPError is returned when a HTTP error occurred (status code)
// and the body could not be parsed to a valid RPCResponse object that holds a RPCError.
//
// Otherwise a RPCResponse object is returned with a RPCError field that is not nil.
type HTTPError struct {
Code int
err error
}
// Error function is provided to be used as error object.
func (e *HTTPError) Error() string {
return e.err.Error()
}
type rpcClient struct {
endpoint string
httpClient *http.Client
customHeaders map[string]string
}
// RPCClientOpts 用于给NewClientWithOpts()以更改RPCClient的配置.
//
// HTTPClient: 用于自定义http.Client (例如. 设置proxy,或 tls)
// CustomHeaders: 用于自定义headers, 例如. 设置 BasicAuth
type RPCClientOpts struct {
HTTPClient *http.Client
CustomHeaders map[string]string
}
// RPCResponses 定义[]*RPCResponse的类型.
// 该类型实现了一些常用的处理列表值的方法
type RPCResponses []*RPCResponse
// AsMap RPCResponses改为id作key的map.id无效的会被抛弃,慎用!
func (res RPCResponses) AsMap() map[int]*RPCResponse {
resMap := make(map[int]*RPCResponse, 0)
for _, r := range res {
if id, ok := r.ID.(int); ok {
resMap[id] = r
}
}
return resMap
}
// GetByID 根据给定的id获取RPCResponses的一个RPCResponse, 如果该id不存在则返回nil
func (res RPCResponses) GetByID(id int) *RPCResponse {
for _, r := range res {
if val, ok := r.ID.(int); ok {
if val == id {
return r
}
}
}
return nil
}
// HasError RPCResponses的任一RPCResponse错误则返回true
func (res RPCResponses) HasError() bool {
for _, res := range res {
if res.Error != nil {
return true
}
}
return false
}
// RPCRequests 定义[]*RPCRequest的类型
type RPCRequests []*RPCRequest
// NewClient 返回一个默认配置的RPCClient
//
// endpoint: 发送JSON-RPC请求的JSON-RPC服务端URL
func NewClient(endpoint string) RPCClient {
return NewClientWithOpts(endpoint, nil)
}
// NewClientWithOpts 返回一个包含自定义配置的RPCClient实例
//
// endpoint: 发送JSON-RPC请求的JSON-RPC服务端URL
// opts: 自定义RPCClientOpts
func NewClientWithOpts(endpoint string, opts *RPCClientOpts) RPCClient {
rpcClient := &rpcClient{
endpoint: endpoint,
httpClient: &http.Client{},
customHeaders: make(map[string]string),
}
if opts == nil {
return rpcClient
}
if opts.HTTPClient != nil {
rpcClient.httpClient = opts.HTTPClient
}
if opts.CustomHeaders != nil {
for k, v := range opts.CustomHeaders {
rpcClient.customHeaders[k] = v
}
}
return rpcClient
}
func (client *rpcClient) Call(method string, params ...interface{}) (*RPCResponse, error) {
request := &RPCRequest{
Method: method,
Params: Params(params...),
JSONRPC: jsonrpcVersion,
}
return client.doCall(request)
}
func (client *rpcClient) CallRaw(request *RPCRequest) (*RPCResponse, error) {
return client.doCall(request)
}
func (client *rpcClient) CallFor(out interface{}, method string, params ...interface{}) error {
rpcResponse, err := client.Call(method, params...)
if err != nil {
return err
}
if rpcResponse.Error != nil {
return rpcResponse.Error
}
return rpcResponse.GetObject(out)
}
func (client *rpcClient) CallBatch(requests RPCRequests) (RPCResponses, error) {
if len(requests) == 0 {
return nil, errors.New("empty request list")
}
for i, req := range requests {
req.ID = i
req.JSONRPC = jsonrpcVersion
}
return client.doBatchCall(requests)
}
func (client *rpcClient) CallBatchRaw(requests RPCRequests) (RPCResponses, error) {
if len(requests) == 0 {
return nil, errors.New("empty request list")
}
return client.doBatchCall(requests)
}
func (client *rpcClient) newRequest(req interface{}) (*http.Request, error) {
body, err := json.Marshal(req)
if err != nil {
return nil, err
}
request, err := http.NewRequest("POST", client.endpoint, bytes.NewReader(body))
if err != nil {
return nil, err
}
request.Header.Set("Content-Type", "application/json")
request.Header.Set("Accept", "application/json")
// set default headers first, so that even content type and accept can be overwritten
for k, v := range client.customHeaders {
request.Header.Set(k, v)
}
return request, nil
}
func (client *rpcClient) doCall(RPCRequest *RPCRequest) (*RPCResponse, error) {
httpRequest, err := client.newRequest(RPCRequest)
if err != nil {
return nil, fmt.Errorf("rpc call %v() on %v: %v", RPCRequest.Method, client.endpoint, err.Error())
}
httpResponse, err := client.httpClient.Do(httpRequest)
if err != nil {
return nil, fmt.Errorf("rpc call %v() on %v: %v", RPCRequest.Method, httpRequest.URL.String(), err.Error())
}
defer httpResponse.Body.Close()
var rpcResponse *RPCResponse
decoder := json.NewDecoder(httpResponse.Body)
decoder.DisallowUnknownFields()
decoder.UseNumber()
err = decoder.Decode(&rpcResponse)
// parsing error
if err != nil {
// if we have some http error, return it
if httpResponse.StatusCode >= 400 {
return nil, &HTTPError{
Code: httpResponse.StatusCode,
err: fmt.Errorf("rpc call %v() on %v status code: %v. could not decode body to rpc response: %v", RPCRequest.Method, httpRequest.URL.String(), httpResponse.StatusCode, err.Error()),
}
}
return nil, fmt.Errorf("rpc call %v() on %v status code: %v. could not decode body to rpc response: %v", RPCRequest.Method, httpRequest.URL.String(), httpResponse.StatusCode, err.Error())
}
// response body empty
if rpcResponse == nil {
// if we have some http error, return it
if httpResponse.StatusCode >= 400 {
return nil, &HTTPError{
Code: httpResponse.StatusCode,
err: fmt.Errorf("rpc call %v() on %v status code: %v. rpc response missing", RPCRequest.Method, httpRequest.URL.String(), httpResponse.StatusCode),
}
}
return nil, fmt.Errorf("rpc call %v() on %v status code: %v. rpc response missing", RPCRequest.Method, httpRequest.URL.String(), httpResponse.StatusCode)
}
return rpcResponse, nil
}
func (client *rpcClient) doBatchCall(rpcRequest []*RPCRequest) ([]*RPCResponse, error) {
httpRequest, err := client.newRequest(rpcRequest)
if err != nil {
// return nil, fmt.Errorf("rpc batch call on %v: %v", client.endpoint, err.Error())
return nil, err
}
httpResponse, err := client.httpClient.Do(httpRequest)
if err != nil {
return nil, err
// return nil, fmt.Errorf("rpc batch call on %v: %v", httpRequest.URL.String(), err.Error())
}
defer httpResponse.Body.Close()
var rpcResponse RPCResponses
decoder := json.NewDecoder(httpResponse.Body)
decoder.DisallowUnknownFields()
decoder.UseNumber()
err = decoder.Decode(&rpcResponse)
// parsing error
if err != nil {
// if we have some http error, return it
if httpResponse.StatusCode >= 400 {
return nil, &HTTPError{
Code: httpResponse.StatusCode,
err: fmt.Errorf("rpc batch call on %v status code: %v. could not decode body to rpc response: %v", httpRequest.URL.String(), httpResponse.StatusCode, err.Error()),
}
}
return nil, fmt.Errorf("rpc batch call on %v status code: %v. could not decode body to rpc response: %v", httpRequest.URL.String(), httpResponse.StatusCode, err.Error())
}
// response body empty
if rpcResponse == nil || len(rpcResponse) == 0 {
// if we have some http error, return it
if httpResponse.StatusCode >= 400 {
return nil, &HTTPError{
Code: httpResponse.StatusCode,
err: fmt.Errorf("rpc batch call on %v status code: %v. rpc response missing", httpRequest.URL.String(), httpResponse.StatusCode),
}
}
return nil, fmt.Errorf("rpc batch call on %v status code: %v. rpc response missing", httpRequest.URL.String(), httpResponse.StatusCode)
}
return rpcResponse, nil
}
// Params is a helper function that uses the same parameter syntax as Call().
// But you should consider to always use NewRequest() instead.
//
// e.g. to manually create an RPCRequest object:
// request := &RPCRequest{
// Method: "myMethod",
// Params: Params("Alex", 35, true),
// }
//
// same with new request:
// request := NewRequest("myMethod", "Alex", 35, true)
//
// If you know what you are doing you can omit the Params() call but potentially create incorrect rpc requests:
// request := &RPCRequest{
// Method: "myMethod",
// Params: 2, <-- invalid since a single primitive value must be wrapped in an array --> no magic without Params()
// }
//
// correct:
// request := &RPCRequest{
// Method: "myMethod",
// Params: []int{2}, <-- valid since a single primitive value must be wrapped in an array
// }
func Params(params ...interface{}) interface{} {
var finalParams interface{}
// if params was nil skip this and p stays nil
if params != nil {
switch len(params) {
case 0: // no parameters were provided, do nothing so finalParam is nil and will be omitted
case 1: // one param was provided, use it directly as is, or wrap primitive types in array
if params[0] != nil {
var typeOf reflect.Type
// traverse until nil or not a pointer type
for typeOf = reflect.TypeOf(params[0]); typeOf != nil && typeOf.Kind() == reflect.Ptr; typeOf = typeOf.Elem() {
}
if typeOf != nil {
// now check if we can directly marshal the type or if it must be wrapped in an array
switch typeOf.Kind() {
// for these types we just do nothing, since value of p is already unwrapped from the array params
case reflect.Struct:
finalParams = params[0]
case reflect.Array:
finalParams = params[0]
case reflect.Slice:
finalParams = params[0]
case reflect.Interface:
finalParams = params[0]
case reflect.Map:
finalParams = params[0]
default: // everything else must stay in an array (int, string, etc)
finalParams = params
}
}
} else {
finalParams = params
}
default: // if more than one parameter was provided it should be treated as an array
finalParams = params
}
}
return finalParams
}
// GetInt 将rpc响应转换为一个int64
func (RPCResponse *RPCResponse) GetInt() (int64, error) {
val, ok := RPCResponse.Result.(json.Number)
if !ok {
return 0, fmt.Errorf("could not parse int64 from %s", RPCResponse.Result)
}
i, err := val.Int64()
if err != nil {
return 0, err
}
return i, nil
}
// GetFloat 将rpc响应转换为一个float64
func (RPCResponse *RPCResponse) GetFloat() (float64, error) {
val, ok := RPCResponse.Result.(json.Number)
if !ok {
return 0, fmt.Errorf("could not parse float64 from %s", RPCResponse.Result)
}
f, err := val.Float64()
if err != nil {
return 0, err
}
return f, nil
}
// GetBool 将rpc响应转换为一个bool
func (RPCResponse *RPCResponse) GetBool() (bool, error) {
val, ok := RPCResponse.Result.(bool)
if !ok {
return false, fmt.Errorf("could not parse bool from %s", RPCResponse.Result)
}
return val, nil
}
// GetString 将rpc响应转换为一个string
func (RPCResponse *RPCResponse) GetString() (string, error) {
val, ok := RPCResponse.Result.(string)
if !ok {
return "", fmt.Errorf("could not parse string from %s", RPCResponse.Result)
}
return val, nil
}
// GetObject 将rpc响应转换为任意类型(结果即json.Unmarshal()中所接收的值)
func (RPCResponse *RPCResponse) GetObject(toType interface{}) error {
js, err := json.Marshal(RPCResponse.Result)
if err != nil {
return err
}
err = json.Unmarshal(js, toType)
if err != nil {
return err
}
return nil
}
Go
1
https://gitee.com/golangx/jsonrpc.git
git@gitee.com:golangx/jsonrpc.git
golangx
jsonrpc
jsonrpc
v1.0.5

搜索帮助