| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267 |
- // Author: NiuJiuRu
- // Email: niujiuru@qq.com
- package jsonrpc2
- import (
- "encoding/json"
- "errors"
- "fmt"
- )
- type ErrCode int
- const (
- ErrParse ErrCode = -32700 // "Parse error"
- ErrInvalidRequest ErrCode = -32600 // "Invalid request"
- ErrMethodNotFound ErrCode = -32601 // "Method not found"
- ErrInvalidParams ErrCode = -32602 // "Invalid params"
- ErrInternal ErrCode = -32603 // "Internal error"
- )
- type Error struct {
- Code ErrCode `json:"code"` //// 错误码
- Message string `json:"message"` //// 错误信息
- }
- func (e Error) Error() string {
- return e.Message
- }
- var errMessages = map[ErrCode]string{
- ErrParse: "Parse error", // 解析错误
- ErrInvalidRequest: "Invalid request", // 无效请求
- ErrMethodNotFound: "Method not found", // 无效方法
- ErrInvalidParams: "Invalid params", // 无效参数
- ErrInternal: "Internal error", // 内部错误
- }
- type Request struct {
- JSONRPC string `json:"jsonrpc"` // 版本号, 固定: "2.0"
- Method string `json:"method"` // 调用方法, 执行函数名
- Params json.RawMessage `json:"params,omitempty"` // 请求参数, 可选可为空
- ID *int `json:"id,omitempty"` // 请求ID, 用于标识请求
- }
- type Response struct {
- JSONRPC string `json:"jsonrpc"` // 版本号, 固定: "2.0"
- Result json.RawMessage `json:"result,omitempty"` // 响应结果, 可选可为空
- Error *Error `json:"error,omitempty"` // 错误信息, 可选可为空
- ID *int `json:"id"` // 应答ID, 响应匹配请求
- }
- // 解析请求数据
- func ParseRequest(input any) (*Request, error) {
- var raw []byte
- switch v := input.(type) {
- case nil:
- return nil, errors.New("input is nil")
- case string:
- raw = []byte(v)
- case []byte:
- raw = v
- default:
- return nil, errors.New("unsupported input type: must be string or []byte")
- }
- var req Request
- if err := json.Unmarshal(raw, &req); err != nil {
- return nil, fmt.Errorf("unmarshal request: %w", err)
- }
- if req.JSONRPC != "2.0" {
- return nil, errors.New(`"jsonrpc" must be "2.0"`)
- }
- return &req, nil
- }
- // 解析应答数据
- func ParseResponse(input any) (*Response, error) {
- var raw []byte
- switch v := input.(type) {
- case nil:
- return nil, errors.New("input is nil")
- case string:
- raw = []byte(v)
- case []byte:
- raw = v
- default:
- return nil, errors.New("unsupported input type: must be string or []byte")
- }
- var resp Response
- if err := json.Unmarshal(raw, &resp); err != nil {
- return nil, fmt.Errorf("unmarshal response: %w", err)
- }
- if resp.JSONRPC != "2.0" {
- return nil, errors.New(`"jsonrpc" must be "2.0"`)
- }
- // 情况 1:result 和 error 同时为空 → 错误
- if resp.Error == nil && resp.Result == nil {
- return nil, errors.New(`response must contain either "result" or "error" field`)
- }
- // 情况 2:result 和 error 同时存在 → 错误
- if resp.Error != nil && resp.Result != nil {
- return nil, errors.New(`response can't contain both "result" and "error" field`)
- }
- // 情况 3:成功时的应答 → 必须有id&且不能空
- if resp.Result != nil {
- if resp.ID == nil {
- return nil, errors.New(`"id" must exist and cannot be null for successful response`)
- }
- return &resp, nil
- }
- // 情况 4:错误时的应答 → 必须有id&且不能空
- if resp.Error.Code == ErrParse { // 特例: Parse error (-32700)时
- return &resp, nil
- }
- if resp.ID == nil {
- return nil, errors.New(`"id" must exist and cannot be null for error response`)
- }
- return &resp, nil
- }
- // 编码JSON数据
- func marshalJSON(input any) (json.RawMessage, error) {
- switch v := input.(type) {
- case nil:
- return nil, nil
- case []byte:
- return nil, errors.New("[]byte is not allowed: use json.RawMessage for raw JSON")
- case json.RawMessage:
- return v, nil
- default:
- b, err := json.Marshal(v)
- if err != nil {
- return nil, fmt.Errorf("marshal json: %w", err)
- }
- return json.RawMessage(b), nil
- }
- }
- // 构建一个请求
- func BuildRequest(method string, params any, id ...int) (*Request, error) {
- if method == "" {
- return nil, errors.New("method must be non-empty")
- }
- raw, err := marshalJSON(params)
- if err != nil {
- return nil, err
- }
- var rid int
- if len(id) > 0 {
- rid = id[0]
- } else {
- rid = nextID()
- }
- req := &Request{
- JSONRPC: "2.0",
- Method: method,
- ID: &rid,
- }
- if raw != nil { // 非空参数
- req.Params = raw
- }
- return req, nil
- }
- // 构建一个通知
- func BuildNotification(method string, params any) (*Request, error) {
- if method == "" {
- return nil, errors.New("method must be non-empty")
- }
- raw, err := marshalJSON(params)
- if err != nil {
- return nil, err
- }
- req := &Request{
- JSONRPC: "2.0",
- Method: method,
- }
- if raw != nil { // 非空参数
- req.Params = raw
- }
- return req, nil
- }
- // 构建成功应答
- func BuildResult(req *Request, result any) (*Response, error) {
- if req.ID == nil { // 通知类型, 不用应答
- return nil, nil
- }
- raw, err := marshalJSON(result)
- if err != nil {
- return nil, err
- }
- if raw == nil {
- raw = json.RawMessage("null")
- }
- return &Response{
- JSONRPC: "2.0",
- Result: raw,
- ID: req.ID,
- }, nil
- }
- // 构建错误应答
- func BuildError(req *Request, code ErrCode, message string) *Response {
- id := (*int)(nil)
- if req != nil {
- id = req.ID
- }
- emsg := message
- if emsg == "" {
- emsg = errMessages[code]
- }
- return &Response{
- JSONRPC: "2.0",
- Error: &Error{
- Code: code,
- Message: emsg,
- },
- ID: id,
- }
- }
- // 请求转字符串
- func (req *Request) String() (string, error) {
- b, err := json.Marshal(req)
- if err != nil {
- return "", fmt.Errorf("marshal request: %w", err)
- }
- return string(b), nil
- }
- // 应答转字符串
- func (resp *Response) String() (string, error) {
- b, err := json.Marshal(resp)
- if err != nil {
- return "", fmt.Errorf("marshal response: %w", err)
- }
- return string(b), nil
- }
|