Note that nginx grpc gateway accepts only grpcweb mode, not grpcwebtext.
So usually you should compile protobuf with --grpc-web_out=import_style=xxx,mode=grpcweb:$OUT_DIR.
But this package also support grpcwebtext mode for simply using gRPC-Web proxy ✌️ If you want to use this mode, use polyfill.
-- load protobuf wrapper and request transformerlocal proto =require("grpc-gateway.proto")
local request =require("grpc-gateway.request")
-- First, instantiate protobuf wrapper with destination pb filelocal p, err = proto.new("/path/to/proto.file")
if err thenprint(err) -- err is not null if file not found or somethingend-- Second, instatiate request with protobuf instancelocal r = request.new(p)
-- Third, call transform() method. transform() method arguments are:-- first argument is service name (contains package name if you defined)-- second argument is RPC method name-- In this case, package will transform to helloworld.Greeter/SayHello request format of HelloRequest
err = r:transform("helloworld.Greeter", "SayHello")
if err thenprint(err) -- err is not null if failed to transform requestend
REST to gRPC request transformation supports GET and POST request methods, it means gRPC message is built from either of:
GET: use query string
POST: use post fields
JSON POST: use decoded JSON request body
For instance:
message HelloRequest {
string name = 1;
}
For above message structure, name field will be assigned by either of following way:
GET /?name=example
POST /
name=example
POST /
Content-Type: application/json
{"name":"example"}
You DO NOT specify all fields as empty, otherwise gateway will respond error.
GET /
>> error
Response transforming
-- load protobuf wrapper and response transformerlocal proto =require("grpc-gateway.proto")
local response =require("grpc-gateway.response")
-- First, instantiate protobuf wrapper with destination pb filelocal p, err = proto.new("/path/to/proto.file")
if err thenprint(err) -- err is not null if file not found or somethingend-- Second, instatiate response with protobuf instancelocal r = response.new(p)
-- Third, call transform() method as same as request:-- first argument is service name (contains package name if you defined)-- second argument is RPC method name-- In this case, package will transform to helloworld.Greeter/SayHello response format of HelloReply
err = r:transform("helloworld.Greeter", "SayHello")
if err thenprint(err) -- err is not null if failed to transform responseend
And, to pass a request to gRPC backend, nginx need to set Content-Type as application/grpc, then this header will be kept to REST response.
To avoid it, you need to swap this header on header_filter_by_lua_* phase:
Otherwise, REST HTTP response's Content-Type becomes application/grpc. Normally it's a bad way of process response (e.g. show download dialog on browser)
Import paths
One of important thing, in this package, you should define import_paths which is set when load exteral/additional proto files in your proto file.
For instance:
syntax="proto3";
packagehelloworld;
// import dependent proto fileimport"google/protobuf/timestamp.proto";
serviceGreeter {
rpcSayHello (HelloRequest) returns (HelloReply) {}
}
messageHelloRequest {
stringname=1;
}
messageHelloReply {
stringmessage=1;
// message which defiened at imported packagegoogle.protobuf.Timestampreply_at=2;
}
import google/protobuf/timestamp.proto and use google.protobuf.Timestamp message struct on above. Then, you need to defined import_path by following ways:
define PROTOC_IMPORT_PATHS as global table
This package will use if PROTOC_IMPORT_PATHS variable is declared as global. we recommend that init_by_lua_block is good for you.
If you add more import_paths for specific package or temporarily, you can pass second or after argument on protoc.new.
local p = protoc.new("/etc/proto/helloworld.proto", "/usr/local/include", ...)
...
These two cases will works fine. imported packages resolved automatically by following import_paths. Example also uses import statment, please check it.
CORS support
This package includes sending CORS headers for grpc-web request from other origin.
local cors = require("grpc-gateway.cors")
cors("http://localhost:8080") -- or cors() to set "*"
Polyfill grpc-web-text mode
When you compiled protobuf with --grpc-web_out=import_style=xxx,mode=grpcwebtext:$OUT_DIR, grpc-web will reqeust as grpc-web-text mode.
In default, nginx can proxy only application/grpc-web+proto which means request body will come as binary,
but nginx cannot proxy application/grpc-web-text Content-Type because request body will come as base64-encoded string, then it cannot decode in nginx itself.
POST /
Content-Type: application/json
{"grades":[97,98,99]}
Nested message types
Since everything in gRPC is built using the message construct, naturally we want to be able to define several and then nest them to create more complex messages.
Note: you can use either the enumerated value as a String or it's equivalent Int. For example: GREEN or 1. If you use a value that is not defined by the enum, the lua-resty-grpc package simply ignores it.
The corresponding GET and POST requests to the gateway using an enum is as follows.
GET /?color=GREEN
OR
GET /?color=1
POST /
Content-Type: application/json
{"color":"BLUE"}
OR
POST /
Content-Type: application/json
{"color":2}
Testing using cURL
For quick testing of the lua-resty-grpc-gateway once it is up and running
The underlying lua-protobuf library is used to encode and decode the lua tables, as such anything that this library does not support consequently this package can not support it either.
One currently known limitation is the use of annotations/options in the proto files. Specfically inside the rpc
请发表评论