restful

package
v1.0.2 Latest Latest
Warning

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

Go to latest
Published: Nov 30, 2023 License: Apache-2.0, BSD-2-Clause, BSD-3-Clause, + 2 more Imports: 33 Imported by: 3

README

English | 中文

Introduction

The tRPC framework uses PB to define services, but it is still a common requirement to provide REST-style APIs based on the HTTP protocol. Unifying RPC and REST is not an easy task, and the tRPC-Go framework's HTTP RPC protocol aims to define the same set of PB files that can be called through RPC (through the client's NewXXXClientProxy provided by the stub code) or through native HTTP requests. However, such HTTP calls do not comply with the RESTful specification, for example, custom routes cannot be defined, wildcards are not supported, and the response body is empty when an error occurs (the error message can only be placed in the response header). Therefore, trpc additionally support the RESTful protocol and no longer attempt to force RPC and REST together. If the service is specified as RESTful protocol, it does not support the use of stub code calls and only supports HTTP client calls. However, the benefit of this approach is that it can provide APIs that comply with RESTful specification through the protobuf annotation in the same set of PB files and can use various tRPC framework plugins or filters.

Principles

Transcoder

Unlike other protocol plugins in the tRPC-Go framework, the RESTful protocol plugin implements a tRPC and HTTP/JSON transcoder based on the tRPC HttpRule at the Transport layer. This eliminates the need for Codec encoding and decoding processes as PB is directly obtained after transcoding and processed in the REST Stub generated by the trpc tool.

restful-overall-design

Transcoder Core: HttpRule

For a service defined using the same set of PB files, support for both RPC and REST calls requires a set of rules to indicate the mapping between RPC and REST, or more precisely, the transcoding between PB and HTTP/JSON. In the industry, Google has defined such rules, namely HttpRule, which tRPC's implementation also references. tRPC's HttpRule needs to be specified in the PB file as an option: option (trpc.api.http), which means that the same set of PB-defined services support both RPC and REST calls. Now, let's take an example of how to bind HttpRule to the SayHello method in a Greeter service:

service Greeter {
  rpc SayHello(HelloRequest) returns (HelloReply) {
    option (trpc.api.http) = {
      post: "/v1/foobar/{name}"
      body: "*"
      additional_bindings: {
        post: "/v1/foo/{name=/x/y/**}"
        body: "single_nested"
        response_body: "message"
      }
    };
  }
}
message HelloRequest {
  string name = 1;
  Nested single_nested = 2;
  oneof oneof_value {
    google.protobuf.Empty oneof_empty = 3;
    string oneof_string = 4;
  }
}
message Nested {
  string name = 1;
}
message HelloReply {
  string message = 1;
}

Through the above example, it can be seen that HttpRule has the following fields:

  • The "body" field indicates which field of the PB request message is carried in the HTTP request body.
  • The "response_body" field indicates which field of the PB response message is carried in the HTTP response body.
  • The "additional_bindings" field represents additional HttpRule, meaning that an RPC method can be bound to multiple HttpRules.

Combining the specific rules of HttpRule, let's take a look at how the HTTP request/response are mapped to HelloRequest and HelloReply in the above example:

When mapping, the "leaf fields" of the RPC request Proto Message (which refers to the fields that cannot be nested and traversed further, in the above example HelloRequest.Name is a leaf field, while HelloRequest.SingleNested is not, and only HelloRequest.SingleNested.Name is) are mapped in three ways:

  • The leaf fields are referenced by the URL Path of the HttpRule: If the URL Path of the HttpRule references one or more fields in the RPC request message, then these fields are passed through the HTTP request URL Path. However, these fields must be non-array fields of native basic types, and do not support fields of message types or array fields. In the above example, if the HttpRule selector field is defined as post: "/v1/foobar/{name}", then the value of the HelloRequest.Name field is mapped to "xyz" when the HTTP request POST /v1/foobar/xyz is made.
  • The leaf fields are referenced by the Body of the HttpRule: If the field to be mapped is specified in the Body of the HttpRule, then this field in the RPC request message is passed through the HTTP request Body. In the above example, if the HttpRule body field is defined as body: "name", then the value of the HelloRequest.Name field is mapped to "xyz" when the HTTP request Body is "xyz".
  • Other leaf fields: Other leaf fields are automatically turned into URL query parameters, and if they are repeated fields, multiple queries of the same URL query parameter are supported. In the above example, if the selector in the additional_bindings specifies post: "/v1/foo/{name=/x/y/*}", and the body is not specified as body: "", then all fields in HelloRequest except HelloRequest.Name are passed through URL query parameters. For example, if the HTTP request POST /v1/foo/x/y/z/xyz?single_nested.name=abc is made, the value of the HelloRequest.Name field is mapped to "/x/y/z/xyz", and the value of the HelloRequest.SingleNested.Name field is mapped to "abc".

Supplement:

  • If the field is not specified in the Body of the HttpRule and is defined as "", then each field of the request message that is not bound by the URL path is passed through the Body of the HTTP request. That is, the URL query parameters are invalid.
  • If the Body of the HttpRule is empty, then every field of the request message that is not bound by the URL path becomes a URL query parameter. That is, the Body is invalid.
  • If the response_body of the HttpRule is empty, then the entire PB response message will be serialized into the HTTP response Body. In the above example, if response_body is "", then the serialized HelloReply is the HTTP response Body.
  • HttpRule body and response_body fields can reference fields of the PB Message, which may or may not be leaf fields, but must be first-level fields in the PB Message. For example, for HelloRequest, HttpRule body can be defined as "name" or "single_nested", but not as "single_nested.name".

Now let's take a look at a few more examples to better understand how to use HttpRule.

1. Take the content that matches "messages/*" inside the URL Path as the value of the "name" field:

service Messaging {
  rpc GetMessage(GetMessageRequest) returns (Message) {
    option (trpc.api.http) = {
        get: "/v1/{name=messages/*}"
    };
  }
}
message GetMessageRequest {
  string name = 1; // Mapped to URL path.
}
message Message {
  string text = 1; // The resource content.
}

The HttpRule above results in the following mapping:

HTTP tRPC
GET /v1/messages/123456 GetMessage(name: "messages/123456")

2. A more complex nested message construction, using "123456" in the URL Path as the value of "message_id", and the value of "sub.subfield" in the URL Path as the value of the "subfield" field in the nested message:

service Messaging {
  rpc GetMessage(GetMessageRequest) returns (Message) {
    option (trpc.api.http) = {
        get:"/v1/messages/{message_id}"
    };
  }
}
message GetMessageRequest {
  message SubMessage {
    string subfield = 1;
  }
  string message_id = 1; // Mapped to URL path.
  int64 revision = 2;    // Mapped to URL query parameter `revision`.
  SubMessage sub = 3;    // Mapped to URL query parameter `sub.subfield`.
}

The HttpRule above results in the following mapping:

HTTP tRPC
GET /v1/messages/123456?revision=2&sub.subfield=foo GetMessage(message_id: "123456" revision: 2 sub: SubMessage(subfield: "foo"))

3. Parse the entire HTTP Body as a Message type, i.e. use "Hi!" as the value of "message.text":

service Messaging {
  rpc UpdateMessage(UpdateMessageRequest) returns (Message) {
    option (trpc.api.http) = {
      post: "/v1/messages/{message_id}"
      body: "message"
    };
  }
}
message UpdateMessageRequest {
  string message_id = 1; // mapped to the URL
  Message message = 2;   // mapped to the body
}

The HttpRule above results in the following mapping:

HTTP tRPC
POST /v1/messages/123456 { "text": "Hi!" } UpdateMessage(message_id: "123456" message { text: "Hi!" })

4. Parse the field in the HTTP Body as the "text" field of the Message:

service Messaging {
  rpc UpdateMessage(Message) returns (Message) {
    option (trpc.api.http) = {
      post: "/v1/messages/{message_id}"
      body: "*"
    };
  }
}
message Message {
  string message_id = 1;
  string text = 2;
}

The HttpRule above results in the following mapping:

HTTP tRPC
POST/v1/messages/123456 { "text": "Hi!" } UpdateMessage(message_id: "123456" text: "Hi!")

5. Using additional_bindings to indicate APIs with additional bindings:

service Messaging {
  rpc GetMessage(GetMessageRequest) returns (Message) {
    option (trpc.api.http) = {
      get: "/v1/messages/{message_id}"
      additional_bindings {
        get: "/v1/users/{user_id}/messages/{message_id}"
      }
    };
  }
}
message GetMessageRequest {
  string message_id = 1;
  string user_id = 2;
}

The HttpRule above results in the following mapping:

HTTP tRPC
GET /v1/messages/123456 GetMessage(message_id: "123456")
GET /v1/users/me/messages/123456 GetMessage(user_id: "me" message_id: "123456")

Implementation

Please refer to the trpc-go/restful.

Examples

After understanding HttpRule, let's take a look at how to enable tRPC-Go's RESTful service.

1. PB Definition

First, update the trpc-cmdline tool to the latest version. To use the trpc.api.http annotation, you need to import a proto file:

import "trpc/api/annotations.proto";

Let's define a PB for a Greeter service:

...
import "trpc/api/annotations.proto";
service Greeter {
  rpc SayHello(HelloRequest) returns (HelloReply) {
    option (trpc.api.http) = {
      post: "/v1/foobar"
      body: "*"
      additional_bindings: {
        post: "/v1/foo/{name}"
      }
    };
  }
}
message HelloRequest {
  string name = 1;
  ...
}  
...

2. Generating Stub Code

Use the trpc create command to generate stub code directly.

3. Configuration

Just like configuring other protocols, set the protocol configuration of the service in trpc_go.yaml to restful.

server: 
  ...
  service:                                         
    - name: trpc.test.helloworld.Greeter      
      ip: 127.0.0.1                            
      # nic: eth0
      port: 8080                
      network: tcp                             
      protocol: restful              
      timeout: 1000

A more common scenario is to configure a tRPC protocol service and add a RESTful protocol service, so that one set of PB files can simultaneously support providing both RPC services and RESTful services.

server: 
  ...
  service:                                         
    - name: trpc.test.helloworld.Greeter1      
      ip: 127.0.0.1                            
      # nic: eth0
      port: 12345                
      network: tcp                             
      protocol: trpc              
      timeout: 1000
    - name: trpc.test.helloworld.Greeter2      
      ip: 127.0.0.1                            
      # nic: eth0
      port: 54321                
      network: tcp                             
      protocol: restful              
      timeout: 1000

Note: Each service in tRPC must be configured with a different port number.

4. starting the Service

Starting the service is the same as other protocols:

package main
import (
    ...
    pb "trpc.group/trpc-go/trpc-go/examples/restful/helloworld"
)
func main() {
    s := trpc.NewServer()
    pb.RegisterGreeterService(s, &greeterServerImpl{})
    // Start
    if err := s.Serve(); err != nil {
        ...
    }
}

5. Calling

Since you are building a RESTful service, please use any REST client to make calls. It is not supported to use the RPC method of calling using NewXXXClientProxy.

package main
import "net/http"
func main() {
    ...
    // native HTTP invocation
    req, err := http.NewRequest("POST", "http://127.0.0.1:8080/v1/foobar", bytes.Newbuffer([]byte(`{"name": "xyz"}`)))
    if err != nil {
        ...
    }
    cli := http.Client{}
    resp, err := cli.Do(req)
    if err != nil {
        ...
    }
    ...
}

Of course, if you have configured a tRPC protocol service in step 3 [Configuration], you can still call the tRPC protocol service using the RPC method of NewXXXClientProxy, but be sure to distinguish the port.

6. Mapping Custom HTTP Headers to RPC Context

HttpRule resolves the transcoding between tRPC message body and HTTP/JSON, but how can HTTP requests pass the RPC call context? This requires defining the mapping of HTTP headers to RPC context.

The HeaderMatcher for RESTful service is defined as follows:

type HeaderMatcher func(
    ctx context.Context,
    w http.ResponseWriter,
    r *http.Request,
    serviceName, methodName string,
) (context.Context, error)

The default handling of HeaderMatcher is as follows:

var defaultHeaderMatcher = func(
    ctx context.Context,
    w http.ResponseWriter,
    req *http.Request,
    serviceName, methodName string,
) (context.Context, error) {
    // It is recommended to customize and pass the codec.Msg in the ctx, and specify the target service and method name.
    ctx, msg := codec.WithNewMessage(ctx)
    msg.WithCalleeServiceName(service)
    msg.WithServerRPCName(method)
    msg.WithSerializationType(codec.SerializationTypePB)
    return ctx, nil
}

You can set the HeaderMatcher through the WithOptions method:

service := server.New(server.WithRESTOptions(restful.WithHeaderMatcher(xxx)))

7. Customize the Response Handling [Set the Return Code for Successful Request Handling]

The "response_body" field in HttpRule specifies the RPC response, for example, in the above example, the "HelloReply" needs to be serialized into the HTTP Response Body as a whole or for a specific field. However, users may want to perform additional custom operations, such as setting the response code for successful requests.

The custom response handling function for RESTful services is defined as follows:

type CustomResponseHandler func(
    ctx context.Context,
    w http.ResponseWriter,
    r *http.Request,
    resp proto.Message,
    body []byte,
) error

The "trpc-go/restful" package provides a function that allows users to set the response code for successful request handling:

func SetStatusCodeOnSucceed(ctx context.Context, code int) {}

The default custom response handling function is as follows:

var defaultResponseHandler = func(
    ctx context.Context,
    w http.ResponseWriter,
    r *http.Request,
    resp proto.Message,
    body []byte,
) error {
    // compress
    var writer io.Writer = w
    _, compressor := compressorForRequest(r)
    if compressor != nil {
        writeCloser, err := compressor.Compress(w)
        if err != nil {
            return fmt.Errorf("failed to compress resp body: %w", err)
        }
        defer writeCloser.Close()
        w.Header().Set(headerContentEncoding, compressor.ContentEncoding())
        writer = writeCloser
    }
    // Set StatusCode
    statusCode := GetStatusCodeOnSucceed(ctx)
    w.WriteHeader(statusCode)
    // Set body
    if statusCode != http.StatusNoContent && statusCode != http.StatusNotModified {
        writer.Write(body)
    }
    return nil
}

If using the default custom response handling function, users can set the return code in their own RPC handling functions (if not set, it will return 200 for success):

func (s *greeterServerImpl) SayHello(ctx context.Context, req *pb.HelloRequest) (*pb.HelloReply, error) {   
    ...
    restful.SetStatusCodeOnSucceed(ctx, 200) // Set the return code for success.
    return rsp, nil
}

You can set the HeaderMatcher through the WithOptions method:

var xxxResponseHandler = func(
    ctx context.Context,
    w http.ResponseWriter,
    r *http.Request,
    resp proto.Message,
    body []byte,
) error {
    reply, ok := resp.(*pb.HelloReply)
    if !ok {
        return errors.New("xxx")
    }
    ...
    w.Header().Set("x", "y")
    expiration := time.Now()
    expiration := expiration.AddDate(1, 0, 0)
    cookie := http.Cookie{Name: "abc", Value: "def", Expires: expiration}
    http.SetCookie(w, &cookie)
    w.Write(body)
    return nil
}
...
service := server.New(server.WithRESTOptions(restful.WithResponseHandler(xxxResponseHandler)))

8. Custom Error Handling [Error Code]

The definition of the error handling function for RESTful services is as follows:

type ErrorHandler func(context.Context, http.ResponseWriter, *http.Request, error)

You can set the HeaderMatcher through the WithOptions method:

var xxxErrorHandler = func(ctx context.Context, w http.ResponseWriter, r *http.Request, err error) {
    if err == errors.New("say hello failed") {
        w.WriteHeader(500)
    }
    ...
}
service := server.New(server.WithRESTOptions(restful.WithErrorHandler(xxxErrorHandler)))

Recommend using the default error handling function of the trpc-go/restful package or referring to the implementation to create your own error handling function.

Regarding error codes:

If an error of the type defined in the "trpc-go/errs" package is returned during RPC processing, the default error handling function of "trpc-go/restful" will map tRPC's error codes to HTTP error codes. If users want to decide what error code is used for a specific error, they can use WithStatusCode defined in the "trpc-go/restful" package.

type WithStatusCode struct {
    StatusCode int
    Err        error
}

Wrap your own error in a function and return it, such as:

func (s *greeterServerImpl) SayHello(ctx context.Context, req *hpb.HelloRequest, rsp *hpb.HelloReply) (err error) {
    if req.Name != "xyz" {
        return &restful.WithStatusCode{
            StatusCode: 400,
            Err:        errors.New("test error"),
        }
    }
    return nil
}

If the error type is not the Error type defined by "trpc-go/errs" and is not wrapped with WithStatusCode defined in the "trpc-go/restful" package, the default error code 500 will be returned.

9. Body Serialization and Compression

Like normal REST requests, it's specified through HTTP headers and supports several popular formats.

Supported Content-Type (or Accept) for serialization: application/json, application/x-www-form-urlencoded, application/octet-stream. By default it is application/json.

Serialization interface is defined as follows:

type Serializer interface {
    // Marshal serializes the tRPC message or one of its fields into the HTTP body. 
    Marshal(v interface{}) ([]byte, error)
    // Unmarshal deserializes the HTTP body into the tRPC message or one of its fields. 
    Unmarshal(data []byte, v interface{}) error
    // Name Serializer Name
    Name() string
    // ContentType  is set when returning the HTTP response.
    ContentType() string
}

Users can implement their own serializer and register it using the restful.RegisterSerializer() function.

Compression is supported through Content-Encoding (or Accept-Encoding): gzip. By default, there is no compression.

Compression interface is defined as follows:

type Compressor interface {
    // Compress 
    Compress(w io.Writer) (io.WriteCloser, error)
    // Decompress 
    Decompress(r io.Reader) (io.Reader, error)
    // Name represents the name of the compressor.
    Name() string
    // ContentEncoding represents the Content-Encoding that is set when returning the HTTP response.
    ContentEncoding() string
}

Users can implement their own serializer and register it using the restful.RegisterSerializer() function.

10. Cross-Origin Requests

RESTful also supports trpc-filter/cors cross-origin requests plugin. To use it, you need to add the HTTP OPTIONS method in pb by using custom, for example:

service HelloTrpcGo {
  rpc Hello(HelloReq) returns (HelloRsp) {
    option (trpc.api.http) = {
      post: "/hello"
      body: "*"
      additional_bindings: {
        get: "/hello/{name}"
      }
      additional_bindings: {
        custom: { // use custom verb
          kind: "OPTIONS"
          path: "/hello"
        }
      }
    };
  }
}

Next, regenerate the stub code using the trpc-cmdline command-line tool. Finally, add the CORS plugin to the service interceptors.

If you do not want to modify the protobuf file, RESTful also provides a code-based custom method for cross-origin requests.

The RESTful protocol plugin will generate a corresponding http.Handler for each Service. You can retrieve it before starting to listen, and replace it with you own custom http.Handler:

func allowCORS(h http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        if origin := r.Header.Get("Origin"); origin != "" {
            w.Header().Set("Access-Control-Allow-Origin", origin)
            if r.Method == "OPTIONS" && r.Header.Get("Access-Control-Request-Method") != "" {
                preflightHandler(w, r)
                return
            }
        }
        h.ServeHTTP(w, r)
    })
}
func main() {
    // set custom header matcher
    s := trpc.NewServer()
    //  register service implementation
    pb.RegisterPingService(s, &pingServiceImpl{})
    // retrieve restful.Router
    router := restful.GetRouter(pb.PingServer_ServiceDesc.ServiceName)
    // wrap it up and re-register it again
    restful.RegisterRouter(pb.PingServer_ServiceDesc.ServiceName, allowCORS(router))
    // start
    if err := s.Serve(); err != nil {
        log.Fatal(err)
    }
}

Performance

To improve performance, the RESTful protocol plugin also supports handling HTTP packets based on fasthttp. The performance of the RESTful protocol plugin is related to the complexity of the registered URL path and the method of passing PB Message fields. Here is a comparison between the two modes in the simplest echo test scenario:

Test PB:

service Greeter {
  rpc SayHello(HelloRequest) returns (HelloReply) {
    option (trpc.api.http) = {
      get: "/v1/foobar/{name}"
    };
  }
}
message HelloRequest {
  string name = 1;
}
message HelloReply {
  string message = 1;
}

Greeter implementation

type greeterServiceImpl struct{}
func (s *greeterServiceImpl) SayHello(ctx context.Context, req *pb.HelloRequest) (*pb.HelloReply, error) {
    return &pb.HelloReply{Message: Name}, nil
}

Test machine: 8 cores

mode QPS when P99 < 10ms
based on net/http 16w
base on fasthttp 25w
  • To enable fasthttp, add one line of code before trpc.NewServer() as follows:
package main
import (
    "trpc.group/trpc-go/trpc-go/transport"
    thttp "trpc.group/trpc-go/trpc-go/http"
)
func main() {
    transport.RegisterServerTransport("restful", thttp.NewRESTServerTransport(true))
    s := trpc.NewServer()
    ...
}

Documentation

Overview

Package restful provides RESTful API for tRPC-Go.

Experimental

Index

Constants

View Source
const (
	// MarshalErrorContent is the content of http response body indicating error marshaling failure.
	MarshalErrorContent = `{"code": 11, "message": "failed to marshal error"}`
)

Variables

View Source
var DefaultErrorHandler = func(ctx context.Context, w http.ResponseWriter, r *http.Request, err error) {

	_, s := serializerForTranscoding(r.Header[headerContentType],
		r.Header[headerAccept])
	w.Header().Set(headerContentType, s.ContentType())

	buf, merr := marshalError(err, s)
	if merr != nil {
		w.WriteHeader(http.StatusInternalServerError)
		w.Write([]byte(MarshalErrorContent))
		return
	}

	w.WriteHeader(statusCodeFromError(err))
	w.Write(buf)
}

DefaultErrorHandler is the default ErrorHandler.

View Source
var DefaultFastHTTPErrorHandler = func(ctx context.Context, requestCtx *fasthttp.RequestCtx, err error) {

	_, s := serializerForTranscoding(
		[]string{bytes2str(requestCtx.Request.Header.Peek(headerContentType))},
		[]string{bytes2str(requestCtx.Request.Header.Peek(headerAccept))},
	)
	requestCtx.Response.Header.Set(headerContentType, s.ContentType())

	buf, merr := marshalError(err, s)
	if merr != nil {
		requestCtx.Response.SetStatusCode(http.StatusInternalServerError)
		requestCtx.Write([]byte(MarshalErrorContent))
		return
	}

	requestCtx.SetStatusCode(statusCodeFromError(err))
	requestCtx.Write(buf)
}

DefaultFastHTTPErrorHandler is the default FastHTTPErrorHandler.

View Source
var DefaultFastHTTPHeaderMatcher = func(
	ctx context.Context,
	requestCtx *fasthttp.RequestCtx,
	serviceName, methodName string,
) (context.Context, error) {
	return withNewMessage(ctx, serviceName, methodName), nil
}

DefaultFastHTTPHeaderMatcher is the default FastHTTPHeaderMatcher.

View Source
var DefaultHeaderMatcher = func(
	ctx context.Context,
	w http.ResponseWriter,
	req *http.Request,
	serviceName, methodName string,
) (context.Context, error) {

	return withNewMessage(ctx, serviceName, methodName), nil
}

DefaultHeaderMatcher is the default HeaderMatcher.

View Source
var DefaultResponseHandler = func(
	ctx context.Context,
	w http.ResponseWriter,
	r *http.Request,
	resp proto.Message,
	body []byte,
) error {
	// compress
	var writer io.Writer = w
	_, c := compressorForTranscoding(r.Header[headerContentEncoding],
		r.Header[headerAcceptEncoding])
	if c != nil {
		writeCloser, err := c.Compress(w)
		if err != nil {
			return fmt.Errorf("failed to compress resp body: %w", err)
		}
		defer writeCloser.Close()
		w.Header().Set(headerContentEncoding, c.ContentEncoding())
		writer = writeCloser
	}

	_, s := serializerForTranscoding(r.Header[headerContentType],
		r.Header[headerAccept])
	w.Header().Set(headerContentType, s.ContentType())

	statusCode := GetStatusCodeOnSucceed(ctx)
	w.WriteHeader(statusCode)

	if statusCode != http.StatusNoContent && statusCode != http.StatusNotModified {
		writer.Write(body)
	}

	return nil
}

DefaultResponseHandler is the default CustomResponseHandler.

View Source
var (
	// ErrTraverseNotFound is the error which indicates the field is
	// not found after traversing the proto message.
	ErrTraverseNotFound = errors.New("field not found")
)

JSONAPI is a copy of jsoniter.ConfigCompatibleWithStandardLibrary. github.com/json-iterator/go is faster than Go's standard json library.

View Source
var Marshaller = protojson.MarshalOptions{EmitUnpopulated: true}

Marshaller is a configurable protojson marshaler.

View Source
var Unmarshaller = protojson.UnmarshalOptions{DiscardUnknown: true}

Unmarshaller is a configurable protojson unmarshaler.

Functions

func DefaultFastHTTPRespHandler

func DefaultFastHTTPRespHandler(stubCtx context.Context, requestCtx *fasthttp.RequestCtx,
	protoResp proto.Message, body []byte) error

DefaultFastHTTPRespHandler is the default FastHTTPRespHandler.

func GetRouter

func GetRouter(name string) http.Handler

GetRouter returns a Router which corresponds to a tRPC Service.

func GetStatusCodeOnSucceed

func GetStatusCodeOnSucceed(ctx context.Context) int

GetStatusCodeOnSucceed returns status code on succeed. SetStatusCodeOnSucceed must be called first in tRPC method.

func PopulateMessage

func PopulateMessage(msg proto.Message, fieldPath []string, values []string) error

PopulateMessage populates a proto message.

func RegisterCompressor

func RegisterCompressor(c Compressor)

RegisterCompressor registers a Compressor. This function is not thread-safe, it should only be called in init() function.

func RegisterRouter

func RegisterRouter(name string, router http.Handler)

RegisterRouter registers a Router which corresponds to a tRPC Service.

func RegisterSerializer

func RegisterSerializer(s Serializer)

RegisterSerializer registers a Serializer. This function is not thread-safe, it should only be called in init() function.

func SetCtxForCompatibility

func SetCtxForCompatibility(f func(context.Context, http.ResponseWriter, *http.Request) context.Context)

SetCtxForCompatibility is used only for compatibility with thttp.

func SetDefaultSerializer

func SetDefaultSerializer(s Serializer)

SetDefaultSerializer sets the default Serializer. This function is not thread-safe, it should only be called in init() function.

func SetStatusCodeOnSucceed

func SetStatusCodeOnSucceed(ctx context.Context, code int)

SetStatusCodeOnSucceed sets status code on succeed, should be 2XX. It's not supposed to call this function but use WithStatusCode in restful/errors.go to set status code on error.

Types

type Binding

type Binding struct {
	Name         string
	Input        Initializer
	Output       Initializer
	Filter       HandleFunc
	HTTPMethod   string
	Pattern      *Pattern
	Body         BodyLocator
	ResponseBody ResponseBodyLocator
}

Binding is the binding of tRPC method and HttpRule.

type BodyLocator

type BodyLocator interface {
	Body() string
	Locate(ProtoMessage) interface{}
}

BodyLocator locates which fields of the proto message would be populated according to HttpRule body.

type Compressor

type Compressor interface {
	// Compress compresses http body.
	Compress(w io.Writer) (io.WriteCloser, error)
	// Decompress decompresses http body.
	Decompress(r io.Reader) (io.Reader, error)
	// Name returns name of the Compressor.
	Name() string
	// ContentEncoding returns the encoding indicated by Content-Encoding response header.
	ContentEncoding() string
}

Compressor is the interface for http body compression/decompression.

func GetCompressor

func GetCompressor(name string) Compressor

GetCompressor returns a Compressor by name.

type CustomResponseHandler

type CustomResponseHandler func(
	ctx context.Context,
	w http.ResponseWriter,
	r *http.Request,
	resp proto.Message,
	body []byte,
) error

CustomResponseHandler is the custom response handler.

type ErrorHandler

type ErrorHandler func(context.Context, http.ResponseWriter, *http.Request, error)

ErrorHandler handles tRPC errors.

type ExtractFilterFunc

type ExtractFilterFunc func() filter.ServerChain

ExtractFilterFunc extracts tRPC service filter chain.

type FastHTTPErrorHandler

type FastHTTPErrorHandler func(context.Context, *fasthttp.RequestCtx, error)

FastHTTPErrorHandler handles tRPC errors when fasthttp is used.

type FastHTTPHeaderMatcher

type FastHTTPHeaderMatcher func(
	ctx context.Context,
	requestCtx *fasthttp.RequestCtx,
	serviceName, methodName string,
) (context.Context, error)

FastHTTPHeaderMatcher matches fasthttp request header to tRPC Stub Context.

type FastHTTPRespHandler

type FastHTTPRespHandler func(
	ctx context.Context,
	requestCtx *fasthttp.RequestCtx,
	resp proto.Message,
	body []byte,
) error

FastHTTPRespHandler is the custom response handler when fasthttp is used.

type FormSerializer

type FormSerializer struct {
	// If DiscardUnknown is set, unknown fields are ignored.
	DiscardUnknown bool
}

FormSerializer is used for Content-Type: application/x-www-form-urlencoded.

func (*FormSerializer) ContentType

func (*FormSerializer) ContentType() string

ContentType implements Serializer. Does the same thing as jsonpb marshaler.

func (*FormSerializer) Marshal

func (*FormSerializer) Marshal(v interface{}) ([]byte, error)

Marshal implements Serializer. It does the same thing as the jsonpb marshaler's Marshal method.

func (*FormSerializer) Name

func (*FormSerializer) Name() string

Name implements Serializer.

func (*FormSerializer) Unmarshal

func (f *FormSerializer) Unmarshal(data []byte, v interface{}) error

Unmarshal implements Serializer

type GZIPCompressor

type GZIPCompressor struct{}

GZIPCompressor is the compressor for Content-Encoding: gzip.

func (*GZIPCompressor) Compress

func (*GZIPCompressor) Compress(w io.Writer) (io.WriteCloser, error)

Compress implements Compressor.

func (*GZIPCompressor) ContentEncoding

func (*GZIPCompressor) ContentEncoding() string

ContentEncoding implements Compressor.

func (*GZIPCompressor) Decompress

func (g *GZIPCompressor) Decompress(r io.Reader) (io.Reader, error)

Decompress implements Compressor.

func (*GZIPCompressor) Name

func (*GZIPCompressor) Name() string

Name implements Compressor.

type HandleFunc

type HandleFunc func(svc interface{}, ctx context.Context, reqBody interface{}) (interface{}, error)

HandleFunc is tRPC method handle function.

type HeaderMatcher

type HeaderMatcher func(
	ctx context.Context,
	w http.ResponseWriter,
	r *http.Request,
	serviceName, methodName string,
) (context.Context, error)

HeaderMatcher matches http request header to tRPC Stub Context.

type Initializer

type Initializer func() ProtoMessage

Initializer initializes a ProtoMessage.

type JSONPBSerializer

type JSONPBSerializer struct {
	AllowUnmarshalNil bool // allow unmarshalling nil body
}

JSONPBSerializer is used for content-Type: application/json. It's based on google.golang.org/protobuf/encoding/protojson.

func (*JSONPBSerializer) ContentType

func (*JSONPBSerializer) ContentType() string

ContentType implements Serializer.

func (*JSONPBSerializer) Marshal

func (*JSONPBSerializer) Marshal(v interface{}) ([]byte, error)

Marshal implements Serializer. Unlike Serializers in trpc-go/codec, Serializers in trpc-go/restful could be used to marshal a field of a tRPC message.

func (*JSONPBSerializer) Name

func (*JSONPBSerializer) Name() string

Name implements Serializer.

func (*JSONPBSerializer) Unmarshal

func (j *JSONPBSerializer) Unmarshal(data []byte, v interface{}) error

Unmarshal implements Serializer.

type Option

type Option func(*Options)

Option sets restful router options.

func WithContainer

func WithContainer(container string) Option

WithContainer returns an Option that sets container name.

func WithDiscardUnknownParams

func WithDiscardUnknownParams(i bool) Option

WithDiscardUnknownParams returns an Option that sets whether to ignore unknown query params for the restful router.

func WithEnvironment

func WithEnvironment(env string) Option

WithEnvironment returns an Option that sets environment name.

func WithErrorHandler

func WithErrorHandler(errorHandler ErrorHandler) Option

WithErrorHandler returns an Option that sets error handler for the restful router.

func WithFastHTTPErrorHandler

func WithFastHTTPErrorHandler(errHandler FastHTTPErrorHandler) Option

WithFastHTTPErrorHandler returns an Option that sets fasthttp error handler for the restful router.

func WithFastHTTPHeaderMatcher

func WithFastHTTPHeaderMatcher(m FastHTTPHeaderMatcher) Option

WithFastHTTPHeaderMatcher returns an Option that sets fasthttp header matcher for the restful router.

func WithFastHTTPRespHandler

func WithFastHTTPRespHandler(h FastHTTPRespHandler) Option

WithFastHTTPRespHandler returns an Option that sets fasthttp custom response handler for the restful router.

func WithFilterFunc

func WithFilterFunc(f ExtractFilterFunc) Option

WithFilterFunc returns an Option that sets tRPC service filter chain extracting function for the restful router.

func WithHeaderMatcher

func WithHeaderMatcher(m HeaderMatcher) Option

WithHeaderMatcher returns an Option that sets header matcher for the restful router.

func WithNamespace

func WithNamespace(namespace string) Option

WithNamespace returns an Option that set namespace.

func WithResponseHandler

func WithResponseHandler(h CustomResponseHandler) Option

WithResponseHandler returns an Option that sets custom response handler for the restful router.

func WithServiceName

func WithServiceName(name string) Option

WithServiceName returns an Option that sets tRPC service name for the restful router.

func WithSet

func WithSet(set string) Option

WithSet returns an Option that sets set name.

func WithTimeout

func WithTimeout(t time.Duration) Option

WithTimeout returns an Option that sets timeout for the restful router.

type Options

type Options struct {
	ServiceName           string                // tRPC service name
	ServiceImpl           interface{}           // tRPC service impl
	FilterFunc            ExtractFilterFunc     // extract tRPC service filter chain
	ErrorHandler          ErrorHandler          // error handler
	HeaderMatcher         HeaderMatcher         // header matcher
	ResponseHandler       CustomResponseHandler // custom response handler
	FastHTTPErrHandler    FastHTTPErrorHandler  // fasthttp error handler
	FastHTTPHeaderMatcher FastHTTPHeaderMatcher // fasthttp header matcher
	FastHTTPRespHandler   FastHTTPRespHandler   // fasthttp custom response handler
	DiscardUnknownParams  bool                  // ignore unknown query params
	Timeout               time.Duration         // timeout
	// contains filtered or unexported fields
}

Options are restful router options.

type Pattern

type Pattern struct {
	*httprule.PathTemplate
}

Pattern makes *httprule.PathTemplate accessible.

func Enforce

func Enforce(urlPath string) *Pattern

Enforce ensures the url path is legal (will panic if illegal) and parses it into a *Pattern.

func Parse

func Parse(urlPath string) (*Pattern, error)

Parse parses the url path into a *Pattern. It should only be used by trpc-cmdline.

type ProtoMessage

type ProtoMessage proto.Message

ProtoMessage is alias of proto.Message.

type ProtoSerializer

type ProtoSerializer struct{}

ProtoSerializer is used for content-Type: application/octet-stream.

func (*ProtoSerializer) ContentType

func (*ProtoSerializer) ContentType() string

ContentType implements Serializer.

func (*ProtoSerializer) Marshal

func (*ProtoSerializer) Marshal(v interface{}) ([]byte, error)

Marshal implements Serializer.

func (*ProtoSerializer) Name

func (*ProtoSerializer) Name() string

Name implements Serializer.

func (*ProtoSerializer) Unmarshal

func (*ProtoSerializer) Unmarshal(data []byte, v interface{}) error

Unmarshal implements Serializer.

type ResponseBodyLocator

type ResponseBodyLocator interface {
	ResponseBody() string
	Locate(ProtoMessage) interface{}
}

ResponseBodyLocator locates which fields of the proto message would be marshaled according to HttpRule response_body.

type Router

type Router struct {
	// contains filtered or unexported fields
}

Router is restful router.

func NewRouter

func NewRouter(opts ...Option) *Router

NewRouter creates a Router.

func (*Router) AddImplBinding

func (r *Router) AddImplBinding(binding *Binding, serviceImpl interface{}) error

AddImplBinding creates a new binding with a specified service implementation.

func (*Router) HandleRequestCtx

func (r *Router) HandleRequestCtx(ctx *fasthttp.RequestCtx)

HandleRequestCtx fasthttp handler

func (*Router) ServeHTTP

func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request)

ServeHTTP implements http.Handler. TODO: better routing handling.

type Serializer

type Serializer interface {
	// Marshal marshals the tRPC message itself or a field of it to http body.
	Marshal(v interface{}) ([]byte, error)
	// Unmarshal unmarshalls http body to the tRPC message itself or a field of it.
	Unmarshal(data []byte, v interface{}) error
	// Name returns name of the Serializer.
	Name() string
	// ContentType returns the original media type indicated by Content-Encoding response header.
	ContentType() string
}

Serializer is the interface for http body marshaling/unmarshalling.

func GetSerializer

func GetSerializer(name string) Serializer

GetSerializer returns a Serializer by its name.

type WithStatusCode

type WithStatusCode struct {
	StatusCode int
	Err        error
}

WithStatusCode is the error that corresponds to an HTTP status code.

func (*WithStatusCode) Error

func (w *WithStatusCode) Error() string

Error implements Go error.

func (*WithStatusCode) Unwrap

func (w *WithStatusCode) Unwrap() error

Unwrap returns the wrapped error.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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