README ¶
zenrpc: JSON-RPC 2.0 Server Implementation with SMD support
zenrpc
is a JSON-RPC 2.0 server library with Service Mapping Description support.
It's built on top of go generate
instead of reflection.
How to Use
Service is struct with RPC methods, service represents RPC namespace.
- Install zenrpc generator
go get github.com/matteosilv/zenrpc/v2/zenrpc
- Import
github.com/matteosilv/zenrpc/v2
into our code with rpc service. - Add trailing comment
//zenrpc
to your service or embedzenrpc.Service
into your service struct. - Write your funcs almost as usual.
- Do not forget run
go generate
orzenrpc
for magic
Accepted Method Signatures
func(Service) Method([args]) (<value>, <error>)
func(Service) Method([args]) <value>
func(Service) Method([args]) <error>
func(Service) Method([args])
- Value could be a pointer
- Error is error or *zenrpc.Error
Example
package main
import (
"flag"
"context"
"errors"
"math"
"log"
"net/http"
"os"
"github.com/matteosilv/zenrpc/v2"
"github.com/matteosilv/zenrpc/v2/testdata"
)
type ArithService struct{ zenrpc.Service }
// Sum sums two digits and returns error with error code as result and IP from context.
func (as ArithService) Sum(ctx context.Context, a, b int) (bool, *zenrpc.Error) {
r, _ := zenrpc.RequestFromContext(ctx)
return true, zenrpc.NewStringError(a+b, r.Host)
}
// Multiply multiples two digits and returns result.
func (as ArithService) Multiply(a, b int) int {
return a * b
}
type Quotient struct {
Quo, Rem int
}
func (as ArithService) Divide(a, b int) (quo *Quotient, err error) {
if b == 0 {
return nil, errors.New("divide by zero")
} else if b == 1 {
return nil, zenrpc.NewError(401, errors.New("we do not serve 1"))
}
return &Quotient{
Quo: a / b,
Rem: a % b,
}, nil
}
// Pow returns x**y, the base-x exponential of y. If Exp is not set then default value is 2.
//zenrpc:exp=2
func (as ArithService) Pow(base float64, exp float64) float64 {
return math.Pow(base, exp)
}
//go:generate zenrpc
func main() {
addr := flag.String("addr", "localhost:9999", "listen address")
flag.Parse()
rpc := zenrpc.NewServer(zenrpc.Options{ExposeSMD: true})
rpc.Register("arith", testdata.ArithService{})
rpc.Register("", testdata.ArithService{}) // public
rpc.Use(zenrpc.Logger(log.New(os.Stderr, "", log.LstdFlags)))
http.Handle("/", rpc)
log.Printf("starting arithsrv on %s", *addr)
log.Fatal(http.ListenAndServe(*addr, nil))
}
Magic comments
All comments are optional.
Method comments
//zenrpc:<method parameter>[=<default value>][whitespaces<description>]
//zenrpc:<error code>[whitespaces<description>]
//zenrpc:return[whitespaces<description>]
Struct comments
type MyService struct {} //zenrpc
Need to browse your api and do some test api calls?
We recommend to use SMDBox. It is Swagger-like JSON RPC API browser, compatible with smd scheme, generated by zenrpc.
JSON-RPC 2.0 Supported Features
- Requests
- Single requests
- Batch requests
- Notifications
- Parameters
- Named
- Position
- Default values
- SMD Schema
- Input
- Output
- Codes
- Scopes for OAuth
Server Library Features
- go generate
- Transports
- HTTP
- WebSocket
- RabbitMQ
- Server middleware
- Basic support
- Metrics
- Logging
Documentation ¶
Index ¶
- Constants
- func ConvertToObject(keys []string, params json.RawMessage) (json.RawMessage, error)
- func ErrorMsg(code int) string
- func IDFromContext(ctx context.Context) *json.RawMessage
- func IsArray(message json.RawMessage) bool
- func NamespaceFromContext(ctx context.Context) string
- func RequestFromContext(ctx context.Context) (*http.Request, bool)
- func SMDBoxHandler(w http.ResponseWriter, r *http.Request)
- type Error
- type InvokeFunc
- type Invoker
- type MiddlewareFunc
- type Options
- type Printer
- type Request
- type Response
- type Server
- func (s Server) Do(ctx context.Context, req []byte) ([]byte, error)
- func (s *Server) Register(namespace string, service Invoker)
- func (s *Server) RegisterAll(services map[string]Invoker)
- func (s Server) SMD() smd.Schema
- func (s Server) ServeHTTP(w http.ResponseWriter, r *http.Request)
- func (s Server) ServeWS(w http.ResponseWriter, r *http.Request)
- func (s *Server) SetLogger(printer Printer)
- func (s *Server) Use(m ...MiddlewareFunc)
- type Service
Constants ¶
const ( // ParseError is error code defined by JSON-RPC 2.0 spec. // Invalid JSON was received by the server. // An error occurred on the server while parsing the JSON text. ParseError = -32700 // InvalidRequest is error code defined by JSON-RPC 2.0 spec. // The JSON sent is not as valid Request object. InvalidRequest = -32600 // MethodNotFound is error code defined by JSON-RPC 2.0 spec. // The method does not exist / is not available. MethodNotFound = -32601 // InvalidParams is error code defined by JSON-RPC 2.0 spec. // Invalid method parameter(s). InvalidParams = -32602 // InternalError is error code defined by JSON-RPC 2.0 spec. // Internal JSON-RPC error. InternalError = -32603 // ServerError is error code defined by JSON-RPC 2.0 spec. // Reserved for implementation-defined server-errors. ServerError = -32000 // Version is only supported JSON-RPC Version. Version = "2.0" )
const (
// context key for ID.
IDKey contextKey = "id"
)
Variables ¶
This section is empty.
Functions ¶
func ConvertToObject ¶
func ConvertToObject(keys []string, params json.RawMessage) (json.RawMessage, error)
ConvertToObject converts json array into object using key by index from keys array.
func IDFromContext ¶
func IDFromContext(ctx context.Context) *json.RawMessage
IDFromContext returns request ID from context.
func IsArray ¶
func IsArray(message json.RawMessage) bool
IsArray checks json message if it array or object.
func NamespaceFromContext ¶
NamespaceFromContext returns method's namespace from context.
func RequestFromContext ¶
RequestFromContext returns http.Request from context.
func SMDBoxHandler ¶
func SMDBoxHandler(w http.ResponseWriter, r *http.Request)
SMDBoxHandler is a handler for SMDBox web app.
Types ¶
type Error ¶
type Error struct { // A Number that indicates the error type that occurred. // This MUST be an integer. Code int `json:"code"` // A String providing as short description of the error. // The message SHOULD be limited to as concise single sentence. Message string `json:"message"` // A Primitive or Structured value that contains additional information about the error. // This may be omitted. // The value of this member is defined by the Server (e.g. detailed error information, nested errors etc.). Data interface{} `json:"data,omitempty"` // Err is inner error. Err error `json:"-"` }
Error object used in response if function call errored. See: http://www.jsonrpc.org/specification#error_object
func NewStringError ¶
NewStringError makes a JSON-RPC with given code and message.
type InvokeFunc ¶
InvokeFunc is a function for processing single JSON-RPC 2.0 Request after validation and parsing.
type Invoker ¶
type Invoker interface { Invoke(ctx context.Context, method string, params json.RawMessage) Response SMD() smd.ServiceInfo }
Invoker implements service handler.
type MiddlewareFunc ¶
type MiddlewareFunc func(InvokeFunc) InvokeFunc
MiddlewareFunc is a function for executing as middleware.
func Logger ¶
func Logger(l *log.Logger) MiddlewareFunc
Logger is middleware for JSON-RPC 2.0 Server. It's just an example for middleware, will be refactored later.
func Metrics ¶
func Metrics(appName string) MiddlewareFunc
Metrics is a middleware for logging duration of RPC requests via Prometheus. Default AppName is zenrpc. It exposes two metrics: appName_rpc_error_requests_count and appName_rpc_responses_duration_seconds.
type Options ¶
type Options struct { // BatchMaxLen sets maximum quantity of requests in single batch. BatchMaxLen int // TargetURL is RPC endpoint. TargetURL string // ExposeSMD exposes SMD schema with ?smd GET parameter. ExposeSMD bool // DisableTransportChecks disables Content-Type and methods checks. Use only for development mode. DisableTransportChecks bool // AllowCORS adds header Access-Control-Allow-Origin with *. AllowCORS bool // Upgrader sets options for gorilla websocket. If nil, default options will be used Upgrader *websocket.Upgrader // HideErrorDataField removes data field from response error HideErrorDataField bool }
Options is options for JSON-RPC 2.0 Server.
type Request ¶
type Request struct { // A String specifying the version of the JSON-RPC protocol. MUST be exactly "2.0". Version string `json:"jsonrpc"` // An identifier established by the Client that MUST contain as String, Number, or NULL value if included. // If it is not included it is assumed to be as notification. // The value SHOULD normally not be Null [1] and Numbers SHOULD NOT contain fractional parts. ID *json.RawMessage `json:"id"` // A String containing the name of the method to be invoked. // Method names that begin with the word rpc followed by as period character (U+002E or ASCII 46) // are reserved for rpc-internal methods and extensions and MUST NOT be used for anything else. Method string `json:"method"` // A Structured value that holds the parameter values to be used during the invocation of the method. // This member MAY be omitted. Params json.RawMessage `json:"params"` // Namespace holds namespace. Not in spec, for internal needs. Namespace string `json:"-"` }
Request is a json structure for json-rpc request to server. See: http://www.jsonrpc.org/specification#request_object
type Response ¶
type Response struct { // A String specifying the version of the JSON-RPC protocol. MUST be exactly "2.0". Version string `json:"jsonrpc"` // This member is REQUIRED. // It MUST be the same as the value of the id member in the Request Object. // If there was an error in detecting the id in the Request object (e.g. Parse error/Invalid Request), it MUST be Null. ID *json.RawMessage `json:"id"` // This member is REQUIRED on success. // This member MUST NOT exist if there was an error invoking the method. // The value of this member is determined by the method invoked on the Server. Result *json.RawMessage `json:"result,omitempty"` // This member is REQUIRED on error. // This member MUST NOT exist if there was no error triggered during invocation. // The value for this member MUST be an Object as defined in section 5.1. Error *Error `json:"error,omitempty"` // Extensions is additional field for extending standard response. It could be useful for tracing, method execution, etc... Extensions map[string]interface{} `json:"extensions,omitempty"` }
Response is json structure for json-rpc response from server. See: http://www.jsonrpc.org/specification#response_object
func NewResponseError ¶
func NewResponseError(id *json.RawMessage, code int, message string, data interface{}) Response
NewResponseError returns new Response with Error object.
type Server ¶
type Server struct {
// contains filtered or unexported fields
}
Server is JSON-RPC 2.0 Server.
func (Server) Do ¶
Do process JSON-RPC 2.0 request, invokes correct method for namespace and returns JSON-RPC 2.0 Response or marshaller error.
func (*Server) Register ¶
Register registers new service for given namespace. For public namespace use empty string.
func (*Server) RegisterAll ¶
RegisterAll registers all services listed in map.
func (Server) ServeHTTP ¶
func (s Server) ServeHTTP(w http.ResponseWriter, r *http.Request)
ServeHTTP process JSON-RPC 2.0 requests via HTTP. http://www.simple-is-better.org/json-rpc/transport_http.html
func (Server) ServeWS ¶
func (s Server) ServeWS(w http.ResponseWriter, r *http.Request)
ServeWS processes JSON-RPC 2.0 requests via Gorilla WebSocket. https://github.com/gorilla/websocket/blob/master/examples/echo/