pbgo

package module
v1.4.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 6, 2020 License: BSD-3-Clause Imports: 21 Imported by: 2

README

  • 赞助 BTC: 1Cbd6oGAUUyBi7X7MaR4np4nTmQZXVgkCW
  • 赞助 ETH: 0x623A3C3a72186A6336C79b18Ac1eD36e1c71A8a6
  • Go语言付费QQ群: 1055927514

pbgo: mini rpc/rest framework based on Protobuf

Build Status Go Report Card GoDoc

Features

  • protoc plugin generate code
  • define rpc api with Protobuf, generate stub code, work with standard net/rpc framework
  • define rest api with Protobuf, and generate service and client code
  • work with grpc framework

Install

  1. install protoc at first: http://github.com/google/protobuf/releases
  2. go get github.com/chai2010/pbgo
  3. go get github.com/chai2010/pbgo/protoc-gen-pbgo
  4. go run hello.go

Example (net/rpc)

create proto file:

syntax = "proto3";
package hello_pb;

message String {
	string value = 1;
}
message Message {
	string value = 1;
}

service HelloService {
	rpc Hello (String) returns (String);
	rpc Echo (Message) returns (Message);
}

generate rpc code:

$ protoc -I=. -I=$GOPATH/src --pbgo_out=. hello.proto

use generate code:

package main

import (
	"fmt"
	"log"
	"net"
	"net/rpc"

	"github.com/chai2010/pbgo/examples/hello.pb"
)

type HelloService struct{}

func (p *HelloService) Hello(request *hello_pb.String, reply *hello_pb.String) error {
	reply.Value = "hello:" + request.GetValue()
	return nil
}
func (p *HelloService) Echo(request *hello_pb.Message, reply *hello_pb.Message) error {
	*reply = *request
	return nil
}

func startRpcServer() {
	hello_pb.RegisterHelloService(rpc.DefaultServer, new(HelloService))

	listener, err := net.Listen("tcp", ":1234")
	if err != nil {
		log.Fatal("ListenTCP error:", err)
	}

	for {
		conn, err := listener.Accept()
		if err != nil {
			log.Fatal("Accept error:", err)
		}

		go rpc.ServeConn(conn)
	}
}

func tryRpcClient() {
	client, err := hello_pb.DialHelloService("tcp", "localhost:1234")
	if err != nil {
		log.Fatal(err)
	}

	reply, err := client.Hello(&hello_pb.String{Value: "gopher"})
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(reply.GetValue())
}

func main() {
	go startRpcServer()
	tryRpcClient()
}

Example (rest api)

create proto file:

syntax = "proto3";
package hello_pb;

import "github.com/chai2010/pbgo/pbgo.proto";

message String {
	string value = 1;
}

message Message {
	string value = 1;
	repeated int32 array = 2;
	map<string,string> dict = 3;
	String subfiled = 4;
}

message StaticFile {
	string content_type = 1;
	bytes content_body = 2;
}

service HelloService {
	rpc Hello (String) returns (String) {
		option (pbgo.rest_api) = {
			get: "/hello/:value"
			post: "/hello"

			additional_bindings {
				method: "DELETE"; url: "/hello"
			}
			additional_bindings {
				method: "PATCH"; url: "/hello"
			}
		};
	}
	rpc Echo (Message) returns (Message) {
		option (pbgo.rest_api) = {
			get: "/echo/:subfiled.value"
		};
	}
	rpc Static(String) returns (StaticFile) {
		option (pbgo.rest_api) = {
			additional_bindings {
				method: "GET";
				url: "/static/:value";
				content_type: ":content_type";
				content_body: ":content_body"
			}
		};
	}
}

generate rpc/rest code:

$ protoc -I=. -I=$GOPATH/src --pbgo_out=. hello.proto

use generate code:

package main

import (
	"io/ioutil"
	"log"
	"mime"
	"net/http"
	"time"

	"github.com/chai2010/pbgo/examples/hello.pb"
)

type HelloService struct{}

func (p *HelloService) Hello(request *hello_pb.String, reply *hello_pb.String) error {
	reply.Value = "hello:" + request.GetValue()
	return nil
}
func (p *HelloService) Echo(request *hello_pb.Message, reply *hello_pb.Message) error {
	*reply = *request
	return nil
}
func (p *HelloService) Static(request *hello_pb.String, reply *hello_pb.StaticFile) error {
	data, err := ioutil.ReadFile("./testdata/" + request.Value)
	if err != nil {
		return err
	}

	reply.ContentType = mime.TypeByExtension(request.Value)
	reply.ContentBody = data
	return nil
}

func someMiddleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(wr http.ResponseWriter, r *http.Request) {
		timeStart := time.Now()
		defer func() {
			timeElapsed := time.Since(timeStart)
			log.Println(r.Method, r.URL, timeElapsed)
		}()

		next.ServeHTTP(wr, r)
	})
}

func main() {
	router := hello_pb.HelloServiceHandler(new(HelloService))
	log.Fatal(http.ListenAndServe(":8080", someMiddleware(router)))
}

Go client:

func main() {
	var reply hello_pb.Message
	err := pbgo.HttpDo("GET", "http://127.0.0.1:8080/echo/xx",
		&hello_pb.Message{
			Value: "chai2010",
			Array: []int32{1, 2, 3},
		},
		&reply,
	)
	if err != nil {
		println("aaa")
		log.Fatal(err)
	}

	// {chai2010 [1 2 3] map[] value:"xx"  {} [] 0}
	fmt.Println(reply)
}

CURL client:

$ curl localhost:8080/hello/gopher
{"value":"hello:gopher"}
$ curl "localhost:8080/hello/gopher?value=vgo"
{"value":"hello:vgo"}
$ curl localhost:8080/hello -X POST --data '{"value":"cgo"}'
{"value":"hello:cgo"}

$ curl localhost:8080/echo/gopher
{"subfiled":{"value":"gopher"}}
$ curl "localhost:8080/echo/gopher?array=123&array=456"
{"array":[123,456],"subfiled":{"value":"gopher"}}
$ curl "localhost:8080/echo/gopher?dict%5Babc%5D=123"
{"dict":{"abc":"123"},"subfiled":{"value":"gopher"}}

$ curl localhost:8080/static/gopher.png
$ curl localhost:8080/static/hello.txt

Rest Client

https://github.com/chubin/wttr.in

$ curl http://wttr.in/wuhan?format=j1
$ curl http://wttr.in/武汉?format=j1
func main() {
	var reply struct {
		CurrentCondition []struct {
			FeelsLikeC       string `json:"FeelsLikeC"`
			FeelsLikeF       string `json:"FeelsLikeF"`
			Cloudcover       string `json:"cloudcover"`
			Humidity         string `json:"humidity"`
			LocalObsDateTime string `json:"localObsDateTime"`
			Observation_time string `json:"observation_time"`
		} `json:"current_condition"`
	}

	err := pbgo.HttpGet("http://wttr.in/wuhan?format=j1", nil, &reply)
	if err != nil {
		log.Fatal(err)
	}

	json, err := json.MarshalIndent(reply, "", "  ")
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(string(json))
}

Form Example

Create example/form_pb/comment.proto:

syntax = "proto3";
package form_pb;

message Comment {
	string user = 1;
	string email = 2;
	string url = 3;
	string text = 4;
	string next = 5; // redirect
}

generate proto code:

$ protoc -I=. --go_out=. comment.proto

create web server:

package main

import (
	"github.com/chai2010/pbgo"
	"github.com/chai2010/pbgo/examples/form_pb"
	"github.com/julienschmidt/httprouter"
)

func main() {
	router := httprouter.New()

	// http://localhost:8080
	router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
		w.Header().Add("Content-Type", "text/html; charset=utf-8")

		fmt.Fprint(w, `
			<form method="POST" action="/api/comment">
				<input type="text" name="user" value="user name">
				<input type="email" name="email" value="123@gmail.com">
				<input name="url" value="http://chai2010.cn"></textarea>
				<textarea name="text" value="text">www</textarea>
				<input type="text" name="next" value="/thanks">
				<button type="submit">Send Test</button>
			</form>
		`)
	})

	router.GET("/thanks", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
		fmt.Fprintln(w, "Thanks")
	})

	// curl -d "user=chai&email=11@qq.com&text=hello&next=http://github.com" localhost:8080/api/comment
	router.POST("/api/comment", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
		var form form_pb.Comment
		if err := pbgo.BindForm(r, &form); err != nil {
			http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
			log.Println("err:", err)
			return
		}

		if form.Next != "" {
			http.Redirect(w, r, form.Next, http.StatusFound)
		}

		log.Println("form:", form)
	})

	log.Fatal(http.ListenAndServe(":8080", router))
}
$ curl -d "user=chai&email=11@qq.com&text=hello&next=http://github.com" localhost:8080/api/comment-form
{"user":"chai","email":"11@qq.com","text":"hello","next":"http://github.com"}

gRPC & Rest Example

See https://github.com/chai2010/pbgo-grpc

BUGS

Report bugs to chaishushan@gmail.com.

Thanks!

Documentation

Overview

Package pbgo provides mini rpc/rest framework based on Protobuf.

Index

Examples

Constants

This section is empty.

Variables

View Source
var E_RestApi = &proto.ExtensionDesc{
	ExtendedType:  (*descriptor.MethodOptions)(nil),
	ExtensionType: (*HttpRule)(nil),
	Field:         20180715,
	Name:          "pbgo.rest_api",
	Tag:           "bytes,20180715,opt,name=rest_api",
	Filename:      "pbgo.proto",
}
View Source
var E_ServiceOpt = &proto.ExtensionDesc{
	ExtendedType:  (*descriptor.ServiceOptions)(nil),
	ExtensionType: (*ServiceOptions)(nil),
	Field:         20180715,
	Name:          "pbgo.service_opt",
	Tag:           "bytes,20180715,opt,name=service_opt",
	Filename:      "pbgo.proto",
}
View Source
var ProtoFiles = map[string]string{
	"pbgo.proto": `// Copyright 2018 <chaishushan{AT}gmail.com>. All rights reserved.
// Use of this source code is governed by a Apache
// license that can be found in the LICENSE file.

syntax = "proto3";

package pbgo;

option go_package = "github.com/chai2010/pbgo;pbgo";

import "google/protobuf/descriptor.proto";

extend google.protobuf.ServiceOptions {
	ServiceOptions service_opt = 20180715;
}
extend google.protobuf.MethodOptions {
	HttpRule rest_api = 20180715;
}

message ServiceOptions {
	string rename = 1;
}

message HttpRule {
	string get = 1;
	string put = 2;
	string post = 3;
	string delete = 4;
	string patch = 5;

	repeated CustomHttpRule additional_bindings = 6;
}

message CustomHttpRule {
	string method = 1;
	string url = 2;

	string content_type = 3;  // must be 'string' type
	string content_body = 4;  // must be 'bytes' type
	string custom_header = 5; // must be 'map<string,string>' type
	string request_body = 6;  // must be 'bytes' type
}
`,
}

Functions

func BindBody added in v1.3.0

func BindBody(r *http.Request, req proto.Message) error

func BindForm added in v1.3.0

func BindForm(r *http.Request, req proto.Message) error

func BindNamedParams added in v1.3.0

func BindNamedParams(
	r *http.Request, req proto.Message,
	params interface{ ByName(name string) string },
	name ...string,
) error

func BindQuery added in v1.3.0

func BindQuery(r *http.Request, req proto.Message) error

func Bool

func Bool(val string) (bool, error)

Bool converts the given string representation of a boolean value into bool.

func BuildUrlValues added in v1.3.0

func BuildUrlValues(msg interface{}) (m url.Values, err error)
Example
var msg = &hello_pb.Message{
	Value: "chai2010",
	Array: []int32{1, 2, 3},
	Dict: map[string]string{
		"aaa": "111",
		"bbb": "222",
	},
	Subfiled: &hello_pb.String{
		Value: "value",
	},
}

x, err := pbgo.BuildUrlValues(msg)
if err != nil {
	panic(err)
}

fmt.Println(x.Encode())
Output:

array=1&array=2&array=3&dict.aaa=111&dict.bbb=222&subfiled.value=value&value=chai2010

func Bytes

func Bytes(val string) ([]byte, error)

Bytes converts the given string representation of a byte sequence into a slice of bytes A bytes sequence is encoded in URL-safe base64 without padding

func Duration

func Duration(val string) (*duration.Duration, error)

Duration converts the given string into a timestamp.Duration.

func Float32

func Float32(val string) (float32, error)

Float32 converts the given string representation of a floating point number into float32.

func Float64

func Float64(val string) (float64, error)

Float64 converts the given string representation into representation of a floating point number into float64.

func HttpDo added in v1.3.0

func HttpDo(method, urlpath string, input, output interface{}) error

func HttpGet added in v1.3.1

func HttpGet(urlpath string, input, output interface{}) error

func HttpPost added in v1.3.1

func HttpPost(urlpath string, input, output interface{}) error

func Int32

func Int32(val string) (int32, error)

Int32 converts the given string representation of an integer into int32.

func Int64

func Int64(val string) (int64, error)

Int64 converts the given string representation of an integer into int64.

func JSON added in v1.3.0

func JSON(w http.ResponseWriter, code int, obj interface{})

func NewHttpRequest added in v1.4.0

func NewHttpRequest(method, urlpath string, input interface{}) (*http.Request, error)

func PopulateFieldFromPath

func PopulateFieldFromPath(msg proto.Message, fieldPathString string, value string) error

PopulateFieldFromPath sets a value in a nested Protobuf structure. It instantiates missing protobuf fields as it goes.

func PopulateQueryParameters

func PopulateQueryParameters(msg proto.Message, values url.Values) error

PopulateQueryParameters populates "values" into "msg".

func PopulateQueryParametersEx added in v1.3.0

func PopulateQueryParametersEx(msg proto.Message, values url.Values, ignoreUnknownParam bool) error

PopulateQueryParametersEx populates "values" into "msg".

func String

func String(val string) (string, error)

String just returns the given string. It is just for compatibility to other types.

func Timestamp

func Timestamp(val string) (*timestamp.Timestamp, error)

Timestamp converts the given RFC3339 formatted string into a timestamp.Timestamp.

func Uint32

func Uint32(val string) (uint32, error)

Uint32 converts the given string representation of an integer into uint32.

func Uint64

func Uint64(val string) (uint64, error)

Uint64 converts the given string representation of an integer into uint64.

Types

type CustomHttpRule

type CustomHttpRule struct {
	Method               string   `protobuf:"bytes,1,opt,name=method,proto3" json:"method,omitempty"`
	Url                  string   `protobuf:"bytes,2,opt,name=url,proto3" json:"url,omitempty"`
	ContentType          string   `protobuf:"bytes,3,opt,name=content_type,json=contentType,proto3" json:"content_type,omitempty"`
	ContentBody          string   `protobuf:"bytes,4,opt,name=content_body,json=contentBody,proto3" json:"content_body,omitempty"`
	CustomHeader         string   `protobuf:"bytes,5,opt,name=custom_header,json=customHeader,proto3" json:"custom_header,omitempty"`
	RequestBody          string   `protobuf:"bytes,6,opt,name=request_body,json=requestBody,proto3" json:"request_body,omitempty"`
	XXX_NoUnkeyedLiteral struct{} `json:"-"`
	XXX_unrecognized     []byte   `json:"-"`
	XXX_sizecache        int32    `json:"-"`
}

func (*CustomHttpRule) Descriptor

func (*CustomHttpRule) Descriptor() ([]byte, []int)

func (*CustomHttpRule) GetContentBody

func (m *CustomHttpRule) GetContentBody() string

func (*CustomHttpRule) GetContentType

func (m *CustomHttpRule) GetContentType() string

func (*CustomHttpRule) GetCustomHeader

func (m *CustomHttpRule) GetCustomHeader() string

func (*CustomHttpRule) GetMethod

func (m *CustomHttpRule) GetMethod() string

func (*CustomHttpRule) GetRequestBody

func (m *CustomHttpRule) GetRequestBody() string

func (*CustomHttpRule) GetUrl

func (m *CustomHttpRule) GetUrl() string

func (*CustomHttpRule) ProtoMessage

func (*CustomHttpRule) ProtoMessage()

func (*CustomHttpRule) Reset

func (m *CustomHttpRule) Reset()

func (*CustomHttpRule) String

func (m *CustomHttpRule) String() string

func (*CustomHttpRule) XXX_DiscardUnknown

func (m *CustomHttpRule) XXX_DiscardUnknown()

func (*CustomHttpRule) XXX_Marshal

func (m *CustomHttpRule) XXX_Marshal(b []byte, deterministic bool) ([]byte, error)

func (*CustomHttpRule) XXX_Merge

func (m *CustomHttpRule) XXX_Merge(src proto.Message)

func (*CustomHttpRule) XXX_Size

func (m *CustomHttpRule) XXX_Size() int

func (*CustomHttpRule) XXX_Unmarshal

func (m *CustomHttpRule) XXX_Unmarshal(b []byte) error

type Error

type Error interface {
	HttpStatus() int
	Text() string
	error
	// contains filtered or unexported methods
}

func NewError

func NewError(httpStatus int, text string) Error

func NewErrorf

func NewErrorf(httpStatus int, fromat string, args ...interface{}) Error

type HttpRule

type HttpRule struct {
	Get                  string            `protobuf:"bytes,1,opt,name=get,proto3" json:"get,omitempty"`
	Put                  string            `protobuf:"bytes,2,opt,name=put,proto3" json:"put,omitempty"`
	Post                 string            `protobuf:"bytes,3,opt,name=post,proto3" json:"post,omitempty"`
	Delete               string            `protobuf:"bytes,4,opt,name=delete,proto3" json:"delete,omitempty"`
	Patch                string            `protobuf:"bytes,5,opt,name=patch,proto3" json:"patch,omitempty"`
	AdditionalBindings   []*CustomHttpRule `protobuf:"bytes,6,rep,name=additional_bindings,json=additionalBindings,proto3" json:"additional_bindings,omitempty"`
	XXX_NoUnkeyedLiteral struct{}          `json:"-"`
	XXX_unrecognized     []byte            `json:"-"`
	XXX_sizecache        int32             `json:"-"`
}

func (*HttpRule) Descriptor

func (*HttpRule) Descriptor() ([]byte, []int)

func (*HttpRule) GetAdditionalBindings

func (m *HttpRule) GetAdditionalBindings() []*CustomHttpRule

func (*HttpRule) GetDelete

func (m *HttpRule) GetDelete() string

func (*HttpRule) GetGet

func (m *HttpRule) GetGet() string

func (*HttpRule) GetPatch

func (m *HttpRule) GetPatch() string

func (*HttpRule) GetPost

func (m *HttpRule) GetPost() string

func (*HttpRule) GetPut

func (m *HttpRule) GetPut() string

func (*HttpRule) ProtoMessage

func (*HttpRule) ProtoMessage()

func (*HttpRule) Reset

func (m *HttpRule) Reset()

func (*HttpRule) String

func (m *HttpRule) String() string

func (*HttpRule) XXX_DiscardUnknown

func (m *HttpRule) XXX_DiscardUnknown()

func (*HttpRule) XXX_Marshal

func (m *HttpRule) XXX_Marshal(b []byte, deterministic bool) ([]byte, error)

func (*HttpRule) XXX_Merge

func (m *HttpRule) XXX_Merge(src proto.Message)

func (*HttpRule) XXX_Size

func (m *HttpRule) XXX_Size() int

func (*HttpRule) XXX_Unmarshal

func (m *HttpRule) XXX_Unmarshal(b []byte) error

type ServiceOptions added in v1.2.0

type ServiceOptions struct {
	Rename               string   `protobuf:"bytes,1,opt,name=rename,proto3" json:"rename,omitempty"`
	XXX_NoUnkeyedLiteral struct{} `json:"-"`
	XXX_unrecognized     []byte   `json:"-"`
	XXX_sizecache        int32    `json:"-"`
}

func (*ServiceOptions) Descriptor added in v1.2.0

func (*ServiceOptions) Descriptor() ([]byte, []int)

func (*ServiceOptions) GetRename added in v1.2.0

func (m *ServiceOptions) GetRename() string

func (*ServiceOptions) ProtoMessage added in v1.2.0

func (*ServiceOptions) ProtoMessage()

func (*ServiceOptions) Reset added in v1.2.0

func (m *ServiceOptions) Reset()

func (*ServiceOptions) String added in v1.2.0

func (m *ServiceOptions) String() string

func (*ServiceOptions) XXX_DiscardUnknown added in v1.2.0

func (m *ServiceOptions) XXX_DiscardUnknown()

func (*ServiceOptions) XXX_Marshal added in v1.2.0

func (m *ServiceOptions) XXX_Marshal(b []byte, deterministic bool) ([]byte, error)

func (*ServiceOptions) XXX_Merge added in v1.2.0

func (m *ServiceOptions) XXX_Merge(src proto.Message)

func (*ServiceOptions) XXX_Size added in v1.2.0

func (m *ServiceOptions) XXX_Size() int

func (*ServiceOptions) XXX_Unmarshal added in v1.2.0

func (m *ServiceOptions) XXX_Unmarshal(b []byte) error

Directories

Path Synopsis
examples
protoc-gen-pbgo is a plugin for the Protobuf compiler to generate rpc/rest code.
protoc-gen-pbgo is a plugin for the Protobuf compiler to generate rpc/rest code.
main
protoc-gen-pbgo is a plugin for the Protobuf compiler to generate rpc/rest code.
protoc-gen-pbgo is a plugin for the Protobuf compiler to generate rpc/rest code.
pbgo
Package pbgo outputs pbgo service descriptions in Go code.
Package pbgo outputs pbgo service descriptions in Go code.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL