This is an automated email from the ASF dual-hosted git repository.
Baoyuantop pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/apisix.git
The following commit(s) were added to refs/heads/master by this push:
new 28c7639d2 docs(grpc-transcode, grpc-web): re-port with improved
descriptions, examples, and structure (#13296)
28c7639d2 is described below
commit 28c7639d25bc75be2c7e9d7e43f6f010c31e79ac
Author: Yilia Lin <[email protected]>
AuthorDate: Tue Apr 28 15:23:39 2026 +0800
docs(grpc-transcode, grpc-web): re-port with improved descriptions,
examples, and structure (#13296)
---
docs/en/latest/plugins/grpc-transcode.md | 514 ++++++++++++++++--------------
docs/en/latest/plugins/grpc-web.md | 226 +++++++++++---
docs/zh/latest/plugins/grpc-transcode.md | 521 +++++++++++++++++--------------
docs/zh/latest/plugins/grpc-web.md | 227 +++++++++++---
4 files changed, 950 insertions(+), 538 deletions(-)
diff --git a/docs/en/latest/plugins/grpc-transcode.md
b/docs/en/latest/plugins/grpc-transcode.md
index b733056c9..425fc9520 100644
--- a/docs/en/latest/plugins/grpc-transcode.md
+++ b/docs/en/latest/plugins/grpc-transcode.md
@@ -6,7 +6,7 @@ keywords:
- Plugin
- gRPC Transcode
- grpc-transcode
-description: This document contains information about the Apache APISIX
grpc-transcode Plugin.
+description: The grpc-transcode Plugin transforms between HTTP requests and
gRPC requests, as well as their corresponding responses.
---
<!--
@@ -28,25 +28,27 @@ description: This document contains information about the
Apache APISIX grpc-tra
#
-->
-## Description
+<head>
+ <link rel="canonical" href="https://docs.api7.ai/hub/grpc-transcode" />
+</head>
-The `grpc-transcode` Plugin converts between HTTP and gRPC requests.
+## Description
-APISIX takes in an HTTP request, transcodes it and forwards it to a gRPC
service, gets the response and returns it back to the client in HTTP format.
+The `grpc-transcode` Plugin transforms between HTTP requests and gRPC
requests, as well as their corresponding responses.
-<!-- TODO: use an image here to explain the concept better -->
+With this Plugin enabled, APISIX accepts an HTTP request from the client,
transcodes and forwards it to an upstream gRPC service. When APISIX receives
the gRPC response, it will transform the response back to an HTTP response and
send it to the client.
## Attributes
-| Name | Type |
Required | Default | Description |
-| --------- | ------------------------------------------------------ |
-------- | ------- | ------------------------------------ |
-| proto_id | string/integer | True
| | id of the the proto content. |
-| service | string | True
| | Name of the gRPC service. |
-| method | string | True
| | Method name of the gRPC service. |
-| deadline | number | False
| 0 | Deadline for the gRPC service in ms. |
-| pb_option | array[string([pb_option_def](#options-for-pb_option))] | False
| | protobuf options. |
-| show_status_in_body | boolean | False
| false | Whether to display the parsed `grpc-status-details-bin` in the
response body |
-| status_detail_type | string | False
| | The message type corresponding to the
[details](https://github.com/googleapis/googleapis/blob/b7cb84f5d42e6dba0fdcc2d8689313f6a8c9d7b9/google/rpc/status.proto#L46)
part of `grpc-status-details-bin`, if not specified, this part will not be
decoded |
+| Name | Type
| Required | Default
| Description
|
+|----------------------|--------------------------------------------------------|----------|-----------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| proto_id | string/integer
| True |
| ID of the proto resource, which contains the protocol buffer
definitions.
|
+| service | string
| True |
| Name of the gRPC service.
|
+| method | string
| True |
| Method name of the gRPC service.
|
+| deadline | number
| False | 0
| Deadline for the gRPC service in ms. This is the time APISIX will
wait for a gRPC call to complete.
|
+| pb_option |
array[string([pb_option_def](#options-for-pb_option))] | False |
`["enum_as_name","int64_as_number","auto_default_values","disable_hooks"]` |
Encoder and decoder
[options](https://github.com/starwing/lua-protobuf?tab=readme-ov-file#options).
|
+| show_status_in_body | boolean
| False | false
| If `true`, display the parsed `grpc-status-details-bin` in the
response body.
|
+| status_detail_type | string
| False |
| The message type corresponding to the
[details](https://github.com/googleapis/googleapis/blob/master/google/rpc/status.proto#L46)
part of `grpc-status-details-bin`. If not specified, the error message will
not be decoded. |
### Options for pb_option
@@ -57,13 +59,12 @@ APISIX takes in an HTTP request, transcodes it and forwards
it to a gRPC service
| default values | `auto_default_values`, `no_default_values`,
`use_default_values`, `use_default_metatable` |
| hooks | `enable_hooks`, `disable_hooks`
|
-## Enable Plugin
-
-Before enabling the Plugin, you have to add the content of your `.proto` or
`.pb` files to APISIX.
+## Examples
-You can use the `/admin/protos/id` endpoint and add the contents of the file
to the `content` field:
+The examples below demonstrate how you can configure the `grpc-transcode`
Plugin for different scenarios.
:::note
+
You can fetch the `admin_key` from `config.yaml` and save to an environment
variable with the following command:
```bash
@@ -72,320 +73,381 @@ admin_key=$(yq '.deployment.admin.admin_key[0].key'
conf/config.yaml | sed 's/"/
:::
+To follow along the examples, start an [example gRPC
server](https://github.com/api7/grpc_server_example):
+
```shell
-curl http://127.0.0.1:9180/apisix/admin/protos/1 -H "X-API-KEY: $admin_key" -X
PUT -d '
-{
- "content" : "syntax = \"proto3\";
- package helloworld;
- service Greeter {
- rpc SayHello (HelloRequest) returns (HelloReply) {}
- }
- message HelloRequest {
- string name = 1;
- }
- message HelloReply {
- string message = 1;
- }"
-}'
+docker run -d \
+ --name grpc-example-server \
+ -p 50051:50051 \
+ api7/grpc-server-example:1.0.2
```
-If your proto file contains imports, or if you want to combine multiple proto
files, you can generate a `.pb` file and use it in APISIX.
-
-For example, if we have a file called `proto/helloworld.proto` which imports
another proto file:
-
-```proto
-syntax = "proto3";
+### Transform between HTTP and gRPC Requests
-package helloworld;
-import "proto/import.proto";
-...
-```
+The following example demonstrates how to configure protobuf in APISIX and
transform between HTTP and gRPC requests using the `grpc-transcode` Plugin.
-We first generate a `.pb` file from the proto files:
+Create a proto resource to store the protobuf:
```shell
-protoc --include_imports --descriptor_set_out=proto.pb proto/helloworld.proto
+curl "http://127.0.0.1:9180/apisix/admin/protos/echo-proto" -H "X-API-KEY:
$admin_key" -X PUT -d '
+{
+ "content": "syntax = \"proto3\";
+ package echo;
+ service EchoService {
+ rpc Echo (EchoMsg) returns (EchoMsg);
+ }
+ message EchoMsg {
+ string msg = 1;
+ }"
+}'
```
-The output binary file, `proto.pb` will contain both `helloworld.proto` and
`import.proto`.
-
-We can now use the content of `proto.pb` in the `content` field of the API
request.
-
-As the content of the proto is binary, we encode it in `base64` and configure
the content in APISIX:
+Create a Route with the `grpc-transcode` Plugin:
```shell
-curl http://127.0.0.1:9180/apisix/admin/protos/1 \
--H "X-API-KEY: $admin_key" -X PUT -d '
+curl "http://127.0.0.1:9180/apisix/admin/routes/grpc-transcode-route" -H
"X-API-KEY: $admin_key" -X PUT -d '
{
- "content" : "'"$(base64 -w0 /path/to/proto.pb)"'"
+ "methods": ["GET"],
+ "uri": "/echo",
+ "plugins": {
+ "grpc-transcode": {
+ "proto_id": "echo-proto",
+ "service": "echo.EchoService",
+ "method": "Echo"
+ }
+ },
+ "upstream": {
+ "scheme": "grpc",
+ "type": "roundrobin",
+ "nodes": {
+ "127.0.0.1:50051": 1
+ }
+ }
}'
```
-You should see an `HTTP/1.1 201 Created` response with the following:
+To verify, send an HTTP request to the Route with parameters defined in
`EchoMsg`:
-```
-{"node":{"value":{"create_time":1643879753,"update_time":1643883085,"content":"CmgKEnByb3RvL2ltcG9ydC5wcm90bxIDcGtnIhoKBFVzZXISEgoEbmFtZRgBIAEoCVIEbmFtZSIeCghSZXNwb25zZRISCgRib2R5GAEgASgJUgRib2R5QglaBy4vcHJvdG9iBnByb3RvMwq9AQoPcHJvdG8vc3JjLnByb3RvEgpoZWxsb3dvcmxkGhJwcm90by9pbXBvcnQucHJvdG8iPAoHUmVxdWVzdBIdCgR1c2VyGAEgASgLMgkucGtnLlVzZXJSBHVzZXISEgoEYm9keRgCIAEoCVIEYm9keTI5CgpUZXN0SW1wb3J0EisKA1J1bhITLmhlbGxvd29ybGQuUmVxdWVzdBoNLnBrZy5SZXNwb25zZSIAQglaBy4vcHJvdG9iBnByb3RvMw=="},"key":"\/a
[...]
+```shell
+curl "http://127.0.0.1:9080/echo?msg=Hello"
```
-Now, we can enable the `grpc-transcode` Plugin to a specific Route:
+You should receive the following response:
-```shell
-curl http://127.0.0.1:9180/apisix/admin/routes/111 -H "X-API-KEY: $admin_key"
-X PUT -d '
-{
- "methods": ["GET"],
- "uri": "/grpctest",
- "plugins": {
- "grpc-transcode": {
- "proto_id": "1",
- "service": "helloworld.Greeter",
- "method": "SayHello"
- }
- },
- "upstream": {
- "scheme": "grpc",
- "type": "roundrobin",
- "nodes": {
- "127.0.0.1:50051": 1
- }
- }
-}'
+```text
+{"msg":"Hello"}
```
-:::note
+### Configure Protobuf with .pb File
-The Upstream service used here should be a gRPC service. Note that the
`scheme` is set to `grpc`.
+The following example demonstrates how to configure protobuf with a `.pb` file
and transform between HTTP and gRPC requests using the `grpc-transcode` Plugin.
-You can use the
[grpc_server_example](https://github.com/api7/grpc_server_example) for testing.
+If your proto file contains imports, or if you want to combine multiple proto
files, you can generate a `.pb` file using the
[protoc](https://google.github.io/proto-lens/installing-protoc.html) utility
and use it in APISIX.
-:::
+Save the protocol buffer definition to a file called `echo.proto`:
-## Example usage
+```proto title="echo.proto"
+syntax = "proto3";
-Once you configured the Plugin as mentioned above, you can make a request to
APISIX to get a response back from the gRPC service (through APISIX):
+package echo;
-```shell
-curl -i http://127.0.0.1:9080/grpctest?name=world
+service EchoService {
+ rpc Echo (EchoMsg) returns (EchoMsg);
+}
+
+message EchoMsg {
+ string msg = 1;
+}
```
-Response:
+Generate the `.pb` file with the
[protoc](https://google.github.io/proto-lens/installing-protoc.html) utility:
```shell
-HTTP/1.1 200 OK
-Date: Fri, 16 Aug 2019 11:55:36 GMT
-Content-Type: application/json
-Transfer-Encoding: chunked
-Connection: keep-alive
-Server: APISIX web server
-Proxy-Connection: keep-alive
+protoc --include_imports --descriptor_set_out=echo_proto.pb echo.proto
+```
+
+Convert the `.pb` file from binary to base64 and configure it in APISIX:
-{"message":"Hello world"}
+```shell
+curl "http://127.0.0.1:9180/apisix/admin/protos/echo-proto" -H "X-API-KEY:
$admin_key" -X PUT -d '
+{
+ "content" : "'"$(base64 -w0 /path/to/echo_proto.pb)"'"
+}'
```
-You can also configure the `pb_option` as shown below:
+Create a Route with the `grpc-transcode` Plugin:
```shell
-curl http://127.0.0.1:9180/apisix/admin/routes/23 -H "X-API-KEY: $admin_key"
-X PUT -d '
+curl "http://127.0.0.1:9180/apisix/admin/routes/grpc-transcode-route" -H
"X-API-KEY: $admin_key" -X PUT -d '
{
- "methods": ["GET"],
- "uri": "/zeebe/WorkflowInstanceCreate",
- "plugins": {
- "grpc-transcode": {
- "proto_id": "1",
- "service": "gateway_protocol.Gateway",
- "method": "CreateWorkflowInstance",
- "pb_option":["int64_as_string"]
- }
- },
- "upstream": {
- "scheme": "grpc",
- "type": "roundrobin",
- "nodes": {
- "127.0.0.1:26500": 1
- }
+ "methods": ["GET"],
+ "uri": "/echo",
+ "plugins": {
+ "grpc-transcode": {
+ "proto_id": "echo-proto",
+ "service": "echo.EchoService",
+ "method": "Echo"
+ }
+ },
+ "upstream": {
+ "scheme": "grpc",
+ "type": "roundrobin",
+ "nodes": {
+ "127.0.0.1:50051": 1
}
+ }
}'
```
-Now if you check the configured Route:
+To verify, send an HTTP request to the Route:
```shell
-curl -i
"http://127.0.0.1:9080/zeebe/WorkflowInstanceCreate?bpmnProcessId=order-process&version=1&variables=\{\"orderId\":\"7\",\"ordervalue\":99\}"
+curl "http://127.0.0.1:9080/echo?msg=Hello"
```
-```
-HTTP/1.1 200 OK
-Date: Wed, 13 Nov 2019 03:38:27 GMT
-Content-Type: application/json
-Transfer-Encoding: chunked
-Connection: keep-alive
-grpc-encoding: identity
-grpc-accept-encoding: gzip
-Server: APISIX web server
-Trailer: grpc-status
-Trailer: grpc-message
+You should receive the following response:
-{"workflowKey":"#2251799813685260","workflowInstanceKey":"#2251799813688013","bpmnProcessId":"order-process","version":1}
+```text
+{"msg":"Hello"}
```
-## Show `grpc-status-details-bin` in response body
+### Display Error Details in Response Body
-If the gRPC service returns an error, there may be a `grpc-status-details-bin`
field in the response header describing the error, which you can decode and
display in the response body.
+The following example demonstrates how to configure the `grpc-transcode`
Plugin to include the `grpc-status-details-bin` field in the response header
for error reporting, when made available by the gRPC server; and decode the
message to be displayed in the response body.
-Upload the proto file:
+Create a proto resource to store the protobuf:
```shell
-curl http://127.0.0.1:9180/apisix/admin/protos/1 \
--H "X-API-KEY: $admin_key" -X PUT -d '
+curl "http://127.0.0.1:9180/apisix/admin/protos/hello-proto" -H "X-API-KEY:
$admin_key" -X PUT -d '
{
- "content" : "syntax = \"proto3\";
- package helloworld;
- service Greeter {
- rpc GetErrResp (HelloRequest) returns (HelloReply) {}
- }
- message HelloRequest {
- string name = 1;
- repeated string items = 2;
- }
- message HelloReply {
- string message = 1;
- repeated string items = 2;
- }"
+ "content": "syntax = \"proto3\";
+ package helloworld;
+ service Greeter {
+ rpc GetErrResp (HelloRequest) returns (HelloReply) {}
+ }
+ message HelloRequest {
+ string name = 1;
+ repeated string items = 2;
+ }
+ message HelloReply {
+ string message = 1;
+ repeated string items = 2;
+ }"
}'
```
-Enable the `grpc-transcode` plugin,and set the option `show_status_in_body` to
`true`:
+Create a Route with the `grpc-transcode` Plugin and set `show_status_in_body`
to `true`:
```shell
-curl http://127.0.0.1:9180/apisix/admin/routes/1 \
--H "X-API-KEY: $admin_key" -X PUT -d '
+curl "http://127.0.0.1:9180/apisix/admin/routes/grpc-transcode-route" -H
"X-API-KEY: $admin_key" -X PUT -d '
{
- "methods": ["GET"],
- "uri": "/grpctest",
- "plugins": {
- "grpc-transcode": {
- "proto_id": "1",
- "service": "helloworld.Greeter",
- "method": "GetErrResp",
- "show_status_in_body": true
- }
- },
- "upstream": {
- "scheme": "grpc",
- "type": "roundrobin",
- "nodes": {
- "127.0.0.1:50051": 1
- }
+ "uri": "/hello",
+ "plugins": {
+ "grpc-transcode": {
+ "proto_id": "hello-proto",
+ "service": "helloworld.Greeter",
+ "method": "GetErrResp",
+ "show_status_in_body": true
+ }
+ },
+ "upstream": {
+ "scheme": "grpc",
+ "type": "roundrobin",
+ "nodes": {
+ "127.0.0.1:50051": 1
}
+ }
}'
```
-Access the route configured above:
+Send a request to the Route:
```shell
-curl -i http://127.0.0.1:9080/grpctest?name=world
+curl -i "http://127.0.0.1:9080/hello?name=world"
```
-Response:
+You should see an error response similar to the following:
-```Shell
+```shell
HTTP/1.1 503 Service Temporarily Unavailable
-Date: Wed, 10 Aug 2022 08:59:46 GMT
+Date: Wed, 21 Feb 2024 03:08:30 GMT
Content-Type: application/json
Transfer-Encoding: chunked
Connection: keep-alive
grpc-status: 14
grpc-message: Out of service
grpc-status-details-bin:
CA4SDk91dCBvZiBzZXJ2aWNlGlcKKnR5cGUuZ29vZ2xlYXBpcy5jb20vaGVsbG93b3JsZC5FcnJvckRldGFpbBIpCAESHFRoZSBzZXJ2ZXIgaXMgb3V0IG9mIHNlcnZpY2UaB3NlcnZpY2U
-Server: APISIX web server
+Server: APISIX/3.8.0
-{"error":{"details":[{"type_url":"type.googleapis.com\/helloworld.ErrorDetail","value":"\b\u0001\u0012\u001cThe
server is out of service\u001a\u0007service"}],"message":"Out of
service","code":14}}
+{"error":{"message":"Out of
service","code":14,"details":[{"value":"\b\u0001\u0012\u001cThe server is out
of
service\u001a\u0007service","type_url":"type.googleapis.com/helloworld.ErrorDetail"}]}}
```
-Note that there is an undecoded field in the return body. If you need to
decode the field, you need to add the `message type` of the field in the
uploaded proto file.
+Note that certain information is not fully decoded in the error response
message.
+
+To decode the message, update the protobuf definition to add the `ErrorDetail`
message type:
```shell
-curl http://127.0.0.1:9180/apisix/admin/protos/1 \
--H "X-API-KEY: $admin_key" -X PUT -d '
+curl "http://127.0.0.1:9180/apisix/admin/protos/hello-proto" -H "X-API-KEY:
$admin_key" -X PUT -d '
{
- "content" : "syntax = \"proto3\";
- package helloworld;
- service Greeter {
- rpc GetErrResp (HelloRequest) returns (HelloReply) {}
- }
- message HelloRequest {
- string name = 1;
- repeated string items = 2;
- }
- message HelloReply {
- string message = 1;
- repeated string items = 2;
- }
- message ErrorDetail {
- int64 code = 1;
- string message = 2;
- string type = 3;
- }"
+ "content": "syntax = \"proto3\";
+ package helloworld;
+ service Greeter {
+ rpc GetErrResp (HelloRequest) returns (HelloReply) {}
+ }
+ message HelloRequest {
+ string name = 1;
+ repeated string items = 2;
+ }
+ message HelloReply {
+ string message = 1;
+ repeated string items = 2;
+ }
+ message ErrorDetail {
+ int64 code = 1;
+ string message = 2;
+ string type = 3;
+ }"
}'
```
-Also configure the option `status_detail_type` to `helloworld.ErrorDetail`.
+Update the Route to configure `status_detail_type`:
```shell
-curl http://127.0.0.1:9180/apisix/admin/routes/1 \
--H "X-API-KEY: $admin_key" -X PUT -d '
+curl "http://127.0.0.1:9180/apisix/admin/routes/grpc-transcode-route" -H
"X-API-KEY: $admin_key" -X PUT -d '
{
- "methods": ["GET"],
- "uri": "/grpctest",
- "plugins": {
- "grpc-transcode": {
- "proto_id": "1",
- "service": "helloworld.Greeter",
- "method": "GetErrResp",
- "show_status_in_body": true,
- "status_detail_type": "helloworld.ErrorDetail"
- }
- },
- "upstream": {
- "scheme": "grpc",
- "type": "roundrobin",
- "nodes": {
- "127.0.0.1:50051": 1
- }
+ "uri": "/hello",
+ "plugins": {
+ "grpc-transcode": {
+ "proto_id": "hello-proto",
+ "service": "helloworld.Greeter",
+ "method": "GetErrResp",
+ "show_status_in_body": true,
+ "status_detail_type": "helloworld.ErrorDetail"
+ }
+ },
+ "upstream": {
+ "scheme": "grpc",
+ "type": "roundrobin",
+ "nodes": {
+ "127.0.0.1:50051": 1
}
+ }
}'
```
-The fully decoded result is returned.
+Send another request to the Route:
+
+```shell
+curl -i "http://127.0.0.1:9080/hello?name=world"
+```
+
+You should see a response with error message fully decoded:
-```Shell
+```shell
HTTP/1.1 503 Service Temporarily Unavailable
-Date: Wed, 10 Aug 2022 09:02:46 GMT
+Date: Wed, 21 Feb 2024 03:11:43 GMT
Content-Type: application/json
Transfer-Encoding: chunked
Connection: keep-alive
grpc-status: 14
grpc-message: Out of service
grpc-status-details-bin:
CA4SDk91dCBvZiBzZXJ2aWNlGlcKKnR5cGUuZ29vZ2xlYXBpcy5jb20vaGVsbG93b3JsZC5FcnJvckRldGFpbBIpCAESHFRoZSBzZXJ2ZXIgaXMgb3V0IG9mIHNlcnZpY2UaB3NlcnZpY2U
-Server: APISIX web server
+Server: APISIX/3.8.0
-{"error":{"details":[{"type":"service","message":"The server is out of
service","code":1}],"message":"Out of service","code":14}}
+{"error":{"message":"Out of service","code":14,"details":[{"message":"The
server is out of service","code":1,"type":"service"}]}}
```
-## Delete Plugin
+### Configure Encoder/Decoder Options
+
+The following example demonstrates how to configure encoder and decoder
[options](https://github.com/starwing/lua-protobuf?tab=readme-ov-file#options)
for the `grpc-transcode` Plugin. Specifically, you will apply the
`int64_as_string` option to a method that performs an addition operation to
observe its effect.
-To remove the `grpc-transcode` Plugin, you can delete the corresponding JSON
configuration from the Plugin configuration. APISIX will automatically reload
and you do not have to restart for this to take effect.
+Create a proto resource to store the protobuf:
```shell
-curl http://127.0.0.1:9180/apisix/admin/routes/111 -H "X-API-KEY: $admin_key"
-X PUT -d '
+curl "http://127.0.0.1:9180/apisix/admin/protos/plus-proto" -H "X-API-KEY:
$admin_key" -X PUT -d '
{
- "uri": "/grpctest",
- "plugins": {},
- "upstream": {
- "scheme": "grpc",
- "type": "roundrobin",
- "nodes": {
- "127.0.0.1:50051": 1
- }
+ "content": "syntax = \"proto3\";
+ package helloworld;
+ service Greeter {
+ rpc Plus (PlusRequest) returns (PlusReply) {}
+ }
+ message PlusRequest {
+ int64 a = 1;
+ int64 b = 2;
+ }
+ message PlusReply {
+ int64 result = 1;
+ }"
+}'
+```
+
+Create a Route with the `grpc-transcode` Plugin:
+
+```shell
+curl "http://127.0.0.1:9180/apisix/admin/routes/grpc-transcode-route" -H
"X-API-KEY: $admin_key" -X PUT -d '
+{
+ "uri": "/plus",
+ "plugins": {
+ "grpc-transcode": {
+ "proto_id": "plus-proto",
+ "service": "helloworld.Greeter",
+ "method": "Plus"
}
+ },
+ "upstream": {
+ "scheme": "grpc",
+ "type": "roundrobin",
+ "nodes": {
+ "127.0.0.1:50051": 1
+ }
+ }
}'
```
+
+Send a request to the Route:
+
+```shell
+curl "http://127.0.0.1:9080/plus?a=1237528374197491&b=1237528374197491"
+```
+
+You should see a response showing a sum of the two numbers:
+
+```text
+{"result":2.475056748395e+15}
+```
+
+Note that the result loses precision when returned as a number. Update the
Route to use the `int64_as_string` option:
+
+```shell
+curl "http://127.0.0.1:9180/apisix/admin/routes/grpc-transcode-route" -H
"X-API-KEY: $admin_key" -X PUT -d '
+{
+ "uri": "/plus",
+ "plugins": {
+ "grpc-transcode": {
+ "proto_id": "plus-proto",
+ "service": "helloworld.Greeter",
+ "method": "Plus",
+ "pb_option":["int64_as_string"]
+ }
+ },
+ "upstream": {
+ "scheme": "grpc",
+ "type": "roundrobin",
+ "nodes": {
+ "127.0.0.1:50051": 1
+ }
+ }
+}'
+```
+
+Send another request to the Route:
+
+```shell
+curl "http://127.0.0.1:9080/plus?a=1237528374197491&b=1237528374197491"
+```
+
+You should see a response showing a sum of the two numbers with full precision:
+
+```text
+{"result":"#2475056748394982"}
+```
diff --git a/docs/en/latest/plugins/grpc-web.md
b/docs/en/latest/plugins/grpc-web.md
index ea796b513..ff2e4c1a5 100644
--- a/docs/en/latest/plugins/grpc-web.md
+++ b/docs/en/latest/plugins/grpc-web.md
@@ -6,7 +6,7 @@ keywords:
- Plugin
- gRPC Web
- grpc-web
-description: This document contains information about the Apache APISIX
grpc-web Plugin.
+description: The grpc-web Plugin enables the gateway to handle gRPC-Web
requests from browsers and JavaScript clients by translating them into standard
gRPC calls and forwarding them to upstream gRPC services.
---
<!--
@@ -28,21 +28,61 @@ description: This document contains information about the
Apache APISIX grpc-web
#
-->
+<head>
+ <link rel="canonical" href="https://docs.api7.ai/hub/grpc-web" />
+</head>
+
## Description
-The `grpc-web` Plugin is a proxy Plugin that can process [gRPC
Web](https://github.com/grpc/grpc-web) requests from JavaScript clients to a
gRPC service.
+gRPC is a high-performance RPC framework based on HTTP/2 and Protocol Buffers,
but it is not natively supported by browsers. gRPC-Web defines a
browser-compatible protocol for sending gRPC requests over HTTP/1.1 or HTTP/2.
+
+The `grpc-web` Plugin translates gRPC-Web requests into native gRPC calls and
forwards them to upstream gRPC services.
## Attributes
-| Name | Type | Required | Default
| Description
|
-|-------------------------|---------|----------|-----------------------------------------|----------------------------------------------------------------------------------------------------------|
-| cors_allow_headers | string | False |
"content-type,x-grpc-web,x-user-agent" | Headers in the request allowed when
accessing a cross-origin resource. Use `,` to add multiple headers. |
+| Name | Type | Required | Default
| Description
|
+|----------------------|---------|----------|-------------------------------------------|---------------------------------------------------------------------------------------------------------|
+| cors_allow_headers | string | False |
`content-type,x-grpc-web,x-user-agent` | Comma-separated list of request
headers allowed for cross-origin requests. |
+
+## Request Handling
+
+The `grpc-web` Plugin processes client requests with specific HTTP methods,
content types, and CORS rules.
+
+### Supported HTTP Methods
+
+The Plugin supports:
+
+- `POST` for gRPC-Web requests
+- `OPTIONS` for CORS preflight checks
+
+See [CORS
support](https://github.com/grpc/grpc-web/blob/master/doc/browser-features.md#cors-support)
for details.
+
+### Supported Content Types
+
+The Plugin recognizes the following content types:
+
+- `application/grpc-web`
+- `application/grpc-web-text`
+- `application/grpc-web+proto`
+- `application/grpc-web-text+proto`
+
+It automatically decodes messages in binary or base64 text format and
translates them into standard gRPC for the upstream server. See [Protocol
differences vs gRPC over
HTTP2](https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-WEB.md#protocol-differences-vs-grpc-over-http2)
for more details.
+
+### CORS Handling
+
+The Plugin automatically handles cross-origin requests. By default:
-## Enable Plugin
+- All origins (`*`) are allowed
+- `POST` requests are permitted
+- Accepted request headers: `content-type`, `x-grpc-web`, `x-user-agent`
+- Exposed response headers: `grpc-status`, `grpc-message`
-You can enable the `grpc-web` Plugin on a specific Route as shown below:
+## Examples
+
+The following examples demonstrate how to configure and use the `grpc-web`
Plugin with a gRPC-Web client.
:::note
+
You can fetch the `admin_key` from `config.yaml` and save to an environment
variable with the following command:
```bash
@@ -51,52 +91,160 @@ admin_key=$(yq '.deployment.admin.admin_key[0].key'
conf/config.yaml | sed 's/"/
:::
+### Prerequisites
+
+Before proceeding with the examples, complete the following steps to set up an
upstream server and gRPC-Web client.
+
+#### Start an Upstream Server
+
+Start a [grpcbin server](https://github.com/moul/grpcbin) to serve as the
example upstream:
+
```shell
-curl http://127.0.0.1:9180/apisix/admin/routes/1 -H "X-API-KEY: $admin_key" -X
PUT -d '
-{
- "uri":"/grpc/web/*",
- "plugins":{
- "grpc-web":{}
- },
- "upstream":{
- "scheme":"grpc",
- "type":"roundrobin",
- "nodes":{
- "127.0.0.1:1980":1
- }
- }
-}'
+docker run -d \
+ --name grpcbin \
+ -p 9000:9000 \
+ moul/grpcbin
```
-## Example usage
+#### Generate gRPC-Web Client Code
-Refer to [gRPC-Web Client Runtime
Library](https://www.npmjs.com/package/grpc-web) or [Apache APISIX gRPC Web
Test Framework](https://github.com/apache/apisix/tree/master/t/plugin/grpc-web)
to learn how to setup your web client.
+Download the Protocol Buffer definition `hello.proto`:
-Once you have your gRPC Web client running, you can make a request to APISIX
from the browser or through Node.js.
+```shell
+curl -O https://raw.githubusercontent.com/moul/pb/master/hello/hello.proto
+```
-:::note
+Install [`protobuf`](https://github.com/protocolbuffers/protobuf/releases) and
[`protoc-gen-grpc-web`](https://github.com/grpc/grpc-web/releases).
-The supported request methods are `POST` and `OPTIONS`. See [CORS
support](https://github.com/grpc/grpc-web/blob/master/doc/browser-features.md#cors-support).
+Generate the gRPC-Web client code from `hello.proto`:
-The supported `Content-Type` includes `application/grpc-web`,
`application/grpc-web-text`, `application/grpc-web+proto`, and
`application/grpc-web-text+proto`. See [Protocol differences vs gRPC over
HTTP2](https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-WEB.md#protocol-differences-vs-grpc-over-http2).
+```shell
+protoc \
+ --js_out=import_style=commonjs:. \
+ --grpc-web_out=import_style=commonjs,mode=grpcwebtext:. \
+ hello.proto
+```
-:::
+You should see two files generated in the current directory: `hello_pb.js` for
Protocol Buffers message classes and `hello_grpc_web_pb.js` for gRPC-Web client
stubs.
-## Delete Plugin
+#### Create a Client
-To remove the `grpc-web` Plugin, you can delete the corresponding JSON
configuration from the Plugin configuration. APISIX will automatically reload
and you do not have to restart for this to take effect.
+Create a Node.js project and install the required dependencies:
```shell
-curl http://127.0.0.1:9180/apisix/admin/routes/1 -H "X-API-KEY: $admin_key" -X
PUT -d '
+npm init -y
+npm install xhr2 grpc-web google-protobuf
+```
+
+Create a client file:
+
+```js title="client.js"
+const XMLHttpRequest = require('xhr2');
+const { HelloServiceClient } = require('./hello_grpc_web_pb');
+const { HelloRequest } = require('./hello_pb');
+
+global.XMLHttpRequest = XMLHttpRequest;
+
+function sayHello(){
+ const client = new HelloServiceClient('http://127.0.0.1:9080/grpc/web',
null, {
+ format: 'text',
+ });
+ const req = new HelloRequest();
+ req.setGreeting('jack');
+
+ const call = client.sayHello(req, {}, (err, resp) => {
+ if (err) {
+ console.error('grpc error:', err.code, err.message);
+ } else {
+ console.log('reply:', resp.getReply());
+ }
+ });
+
+ call.on('metadata', (metadata) => {
+ console.log('Response headers:', metadata);
+ });
+}
+
+function lotsOfReplies() {
+ const client = new HelloServiceClient('http://127.0.0.1:9080/grpc/web',
null, {
+ format: 'text',
+ });
+ const req = new HelloRequest();
+ req.setGreeting('rep');
+ const stream = client.lotsOfReplies(req, {});
+
+ stream.on('metadata', (metadata) => {
+ console.log('Response headers:', metadata);
+ });
+
+ stream.on('data', (response) => {
+ console.log('Reply:', response.getReply());
+ });
+
+ stream.on('end', () => {
+ console.log('Stream ended');
+ });
+
+ stream.on('error', (err) => {
+ console.error('Error:', err);
+ });
+}
+
+lotsOfReplies()
+sayHello()
+```
+
+You can run the client with `node client.js` to send both unary and
server-streaming requests to your gRPC server via the gateway.
+
+### Proxy gRPC-Web (Prefix Match Route)
+
+The following example demonstrates how to configure and use the `grpc-web`
Plugin with the gRPC-Web client set up previously.
+
+Create a Route with the `grpc-web` Plugin:
+
+```shell
+curl "http://127.0.0.1:9180/apisix/admin/routes/grpc-web-route" -H "X-API-KEY:
$admin_key" -X PUT -d '
{
- "uri":"/grpc/web/*",
- "plugins":{},
- "upstream":{
- "scheme":"grpc",
- "type":"roundrobin",
- "nodes":{
- "127.0.0.1:1980":1
- }
+ "uri": "/grpc/web/*",
+ "plugins": {
+ "grpc-web": {}
+ },
+ "upstream": {
+ "scheme": "grpc",
+ "type": "roundrobin",
+ "nodes": {
+ "127.0.0.1:9000": 1
}
+ }
}'
```
+
+:::note
+
+In APISIX versions prior to 3.15.0, the Route URI must use a prefix match
because gRPC-Web clients include the package name, service name, and method
name in the request URI. Using an absolute URI match in these versions will
prevent the request from matching the Route.
+
+In this example, the Route URI must be configured as `/grpc/web/*` to
correctly match client requests such as
`/grpc/web/hello.HelloService/SayHello`. Using a broader prefix like `/grpc/*`
would prevent the gateway from correctly extracting the full service path,
resulting in errors such as `unknown service web/hello.HelloService`.
+
+:::
+
+Run the client to send requests to the gateway Route:
+
+```shell
+node client.js
+```
+
+You should see a reply from the upstream gRPC server:
+
+```text
+Response headers: {
+ ...
+ 'access-control-allow-origin': '*',
+ 'access-control-expose-headers': 'grpc-message,grpc-status'
+}
+Response headers: {
+ ...
+ 'access-control-allow-origin': '*',
+ 'access-control-expose-headers': 'grpc-message,grpc-status'
+}
+reply: hello jack
+```
diff --git a/docs/zh/latest/plugins/grpc-transcode.md
b/docs/zh/latest/plugins/grpc-transcode.md
index 10052d7d0..a44c3e97c 100644
--- a/docs/zh/latest/plugins/grpc-transcode.md
+++ b/docs/zh/latest/plugins/grpc-transcode.md
@@ -4,9 +4,9 @@ keywords:
- Apache APISIX
- API 网关
- Plugin
- - gRPC Web
- - grpc-web
-description: 本文介绍了关于 Apache APISIX `grpc-transcode` 插件的基本信息及使用方法。
+ - gRPC Transcode
+ - grpc-transcode
+description: grpc-transcode 插件在 HTTP 请求与 gRPC 请求及其对应响应之间进行转换。
---
<!--
@@ -28,25 +28,27 @@ description: 本文介绍了关于 Apache APISIX `grpc-transcode` 插件的基
#
-->
-## 描述
+<head>
+ <link rel="canonical" href="https://docs.api7.ai/hub/grpc-transcode" />
+</head>
-使用 `grpc-transcode` 插件可以在 HTTP 和 gRPC 请求之间进行转换。
+## 描述
-APISIX 接收 HTTP 请求后,首先对请求进行转码,并将转码后的请求转发到 gRPC 服务,获取响应并以 HTTP 格式将其返回给客户端。
+`grpc-transcode` 插件在 HTTP 请求与 gRPC 请求及其对应响应之间进行转换。
-<!-- TODO: use an image here to explain the concept better -->
+启用此插件后,APISIX 接收来自客户端的 HTTP 请求,转码后转发给上游 gRPC 服务。当 APISIX 收到 gRPC 响应时,会将其转换回
HTTP 响应并发送给客户端。
## 属性
-| 名称 | 类型
| 必选项 | 默认值 | 描述 |
-| --------- | ------------------------------------------------- | ----- |
------ ------------------------------ |
-| proto_id | string/integer | 是 |
| `.proto` 内容的 id。 |
-| service | string | 是 |
| gRPC 服务名。 |
-| method | string | 是 |
| gRPC 服务中要调用的方法名。 |
-| deadline | number | 否 | 0
| gRPC 服务的 deadline,单位为:ms。 |
-| pb_option | array[string([pb_option_def](#pb_option-的选项))] | 否 |
| proto 编码过程中的转换选项。 |
-| show_status_in_body | boolean | 否 |
false | 是否在返回体中展示解析过的 `grpc-status-details-bin` |
-| status_detail_type | string | 否 |
| `grpc-status-details-bin` 中
[details](https://github.com/googleapis/googleapis/blob/b7cb84f5d42e6dba0fdcc2d8689313f6a8c9d7b9/google/rpc/status.proto#L46)
部分对应的 message type,如果不指定,此部分不进行解码 |
+| 名称 | 类型 |
必选项 | 默认值 |
描述
|
+|----------------------|--------------------------------------------------------|--------|----------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| proto_id | string/integer
| 是 |
| proto 资源的 ID,包含 Protocol Buffer 定义。
|
+| service | string
| 是 |
| gRPC 服务名称。
|
+| method | string
| 是 |
| gRPC 服务的方法名称。
|
+| deadline | number
| 否 | 0
| gRPC 服务的超时时间,单位为毫秒。即 APISIX 等待 gRPC 调用完成的时间。
|
+| pb_option | array[string([pb_option_def](#pb_option-的选项))] |
否 |
`["enum_as_name","int64_as_number","auto_default_values","disable_hooks"]` |
编码器和解码器[选项](https://github.com/starwing/lua-protobuf?tab=readme-ov-file#options)。
|
+| show_status_in_body | boolean
| 否 | false
| 若为 `true`,则在响应体中展示解析后的 `grpc-status-details-bin`。
|
+| status_detail_type | string
| 否 |
| `grpc-status-details-bin` 中
[details](https://github.com/googleapis/googleapis/blob/master/google/rpc/status.proto#L46)
部分对应的消息类型。若未指定,错误消息将不会被解码。 |
### pb_option 的选项
@@ -57,11 +59,9 @@ APISIX 接收 HTTP 请求后,首先对请求进行转码,并将转码后的
| default values | `auto_default_values`, `no_default_values`,
`use_default_values`, `use_default_metatable` |
| hooks | `enable_hooks`, `disable_hooks`
|
-## 启用插件
-
-在启用插件之前,你必须将 `.proto` 或 `.pb` 文件的内容添加到 APISIX。
+## 示例
-可以使用 `/admin/protos/id` 接口将文件的内容添加到 `content` 字段:
+以下示例演示了如何针对不同场景配置 `grpc-transcode` 插件。
:::note
@@ -73,324 +73,381 @@ admin_key=$(yq '.deployment.admin.admin_key[0].key'
conf/config.yaml | sed 's/"/
:::
+在跟随示例操作之前,请先启动一个[示例 gRPC 服务器](https://github.com/api7/grpc_server_example):
+
```shell
-curl http://127.0.0.1:9180/apisix/admin/protos/1 \
--H "X-API-KEY: $admin_key" -X PUT -d '
-{
- "content" : "syntax = \"proto3\";
- package helloworld;
- service Greeter {
- rpc SayHello (HelloRequest) returns (HelloReply) {}
- }
- message HelloRequest {
- string name = 1;
- }
- message HelloReply {
- string message = 1;
- }"
-}'
+docker run -d \
+ --name grpc-example-server \
+ -p 50051:50051 \
+ api7/grpc-server-example:1.0.2
```
-如果你的 `.proto` 文件包含 `import`,或者想要把多个 `.proto` 文件合并成一个 proto,你可以生成一个 `.pb` 文件并在
APISIX 中使用它。
-
-假设已经有一个 `.proto` 文件(`proto/helloworld.proto`),它导入了另一个 `proto` 文件:
-
-```proto
-syntax = "proto3";
+### 在 HTTP 和 gRPC 请求之间转换
-package helloworld;
-import "proto/import.proto";
-...
-```
+以下示例演示了如何在 APISIX 中配置 protobuf,并使用 `grpc-transcode` 插件在 HTTP 和 gRPC 请求之间进行转换。
-首先,让我们从 `.proto` 文件创建一个 `.pb` 文件。
+创建 proto 资源以存储 protobuf:
```shell
-protoc --include_imports --descriptor_set_out=proto.pb proto/helloworld.proto
+curl "http://127.0.0.1:9180/apisix/admin/protos/echo-proto" -H "X-API-KEY:
$admin_key" -X PUT -d '
+{
+ "content": "syntax = \"proto3\";
+ package echo;
+ service EchoService {
+ rpc Echo (EchoMsg) returns (EchoMsg);
+ }
+ message EchoMsg {
+ string msg = 1;
+ }"
+}'
```
-输出的二进制文件 `proto.pb` 将同时包含 `helloworld.proto` 和 `import.proto`。
-
-然后将 `proto.pb` 的内容作为 proto 的 `content` 字段提交。
-
-由于 proto 的内容是二进制的,我们需要使用以下 shell 命令将其转换成 `base64`:
+创建启用 `grpc-transcode` 插件的路由:
```shell
-curl http://127.0.0.1:9180/apisix/admin/protos/1 \
--H "X-API-KEY: $admin_key" -X PUT -d '
+curl "http://127.0.0.1:9180/apisix/admin/routes/grpc-transcode-route" -H
"X-API-KEY: $admin_key" -X PUT -d '
{
- "content" : "'"$(base64 -w0 /path/to/proto.pb)"'"
+ "methods": ["GET"],
+ "uri": "/echo",
+ "plugins": {
+ "grpc-transcode": {
+ "proto_id": "echo-proto",
+ "service": "echo.EchoService",
+ "method": "Echo"
+ }
+ },
+ "upstream": {
+ "scheme": "grpc",
+ "type": "roundrobin",
+ "nodes": {
+ "127.0.0.1:50051": 1
+ }
+ }
}'
```
-返回 `HTTP/1.1 201 Created` 结果如下:
+验证时,使用 `EchoMsg` 中定义的参数向路由发送 HTTP 请求:
-```
-{"node":{"value":{"create_time":1643879753,"update_time":1643883085,"content":"CmgKEnByb3RvL2ltcG9ydC5wcm90bxIDcGtnIhoKBFVzZXISEgoEbmFtZRgBIAEoCVIEbmFtZSIeCghSZXNwb25zZRISCgRib2R5GAEgASgJUgRib2R5QglaBy4vcHJvdG9iBnByb3RvMwq9AQoPcHJvdG8vc3JjLnByb3RvEgpoZWxsb3dvcmxkGhJwcm90by9pbXBvcnQucHJvdG8iPAoHUmVxdWVzdBIdCgR1c2VyGAEgASgLMgkucGtnLlVzZXJSBHVzZXISEgoEYm9keRgCIAEoCVIEYm9keTI5CgpUZXN0SW1wb3J0EisKA1J1bhITLmhlbGxvd29ybGQuUmVxdWVzdBoNLnBrZy5SZXNwb25zZSIAQglaBy4vcHJvdG9iBnByb3RvMw=="},"key":"\/a
[...]
+```shell
+curl "http://127.0.0.1:9080/echo?msg=Hello"
```
-现在我们可以在指定路由中启用 `grpc-transcode` 插件:
+您应该收到以下响应:
-```shell
-curl http://127.0.0.1:9180/apisix/admin/routes/1 \
--H "X-API-KEY: $admin_key" -X PUT -d '
-{
- "methods": ["GET"],
- "uri": "/grpctest",
- "plugins": {
- "grpc-transcode": {
- "proto_id": "1",
- "service": "helloworld.Greeter",
- "method": "SayHello"
- }
- },
- "upstream": {
- "scheme": "grpc",
- "type": "roundrobin",
- "nodes": {
- "127.0.0.1:50051": 1
- }
- }
-}'
+```text
+{"msg":"Hello"}
```
-:::note
+### 使用 .pb 文件配置 Protobuf
-此处使用的 Upstream 应该是 gRPC 服务。请注意,`scheme` 需要设置为 `grpc`。
+以下示例演示了如何使用 `.pb` 文件在 APISIX 中配置 protobuf,并使用 `grpc-transcode` 插件在 HTTP 和 gRPC
请求之间进行转换。
-可以使用 [grpc_server_example](https://github.com/api7/grpc_server_example) 进行测试。
+如果您的 proto 文件包含 import,或者想合并多个 proto 文件,可以使用
[protoc](https://google.github.io/proto-lens/installing-protoc.html) 工具生成 `.pb`
文件并在 APISIX 中使用。
-:::
+将 Protocol Buffer 定义保存到名为 `echo.proto` 的文件中:
-## 测试插件
+```proto title="echo.proto"
+syntax = "proto3";
-通过上述示例配置插件后,你可以向 APISIX 发出请求以从 gRPC 服务(通过 APISIX)获得响应:
+package echo;
-访问上面配置的 route:
+service EchoService {
+ rpc Echo (EchoMsg) returns (EchoMsg);
+}
-```shell
-curl -i http://127.0.0.1:9080/grpctest?name=world
+message EchoMsg {
+ string msg = 1;
+}
```
-返回结果
+使用 [protoc](https://google.github.io/proto-lens/installing-protoc.html) 工具生成
`.pb` 文件:
-```Shell
-HTTP/1.1 200 OK
-Date: Fri, 16 Aug 2019 11:55:36 GMT
-Content-Type: application/json
-Transfer-Encoding: chunked
-Connection: keep-alive
-Server: APISIX web server
-Proxy-Connection: keep-alive
+```shell
+protoc --include_imports --descriptor_set_out=echo_proto.pb echo.proto
+```
-{"message":"Hello world"}
+将 `.pb` 文件从二进制转换为 base64 并在 APISIX 中配置:
+
+```shell
+curl "http://127.0.0.1:9180/apisix/admin/protos/echo-proto" -H "X-API-KEY:
$admin_key" -X PUT -d '
+{
+ "content" : "'"$(base64 -w0 /path/to/echo_proto.pb)"'"
+}'
```
-你也可以配置 `pb_option`,如下所示:
+创建启用 `grpc-transcode` 插件的路由:
```shell
-curl http://127.0.0.1:9180/apisix/admin/routes/23 -H "X-API-KEY: $admin_key"
-X PUT -d '
+curl "http://127.0.0.1:9180/apisix/admin/routes/grpc-transcode-route" -H
"X-API-KEY: $admin_key" -X PUT -d '
{
- "methods": ["GET"],
- "uri": "/zeebe/WorkflowInstanceCreate",
- "plugins": {
- "grpc-transcode": {
- "proto_id": "1",
- "service": "gateway_protocol.Gateway",
- "method": "CreateWorkflowInstance",
- "pb_option":["int64_as_string"]
- }
- },
- "upstream": {
- "scheme": "grpc",
- "type": "roundrobin",
- "nodes": {
- "127.0.0.1:26500": 1
- }
+ "methods": ["GET"],
+ "uri": "/echo",
+ "plugins": {
+ "grpc-transcode": {
+ "proto_id": "echo-proto",
+ "service": "echo.EchoService",
+ "method": "Echo"
+ }
+ },
+ "upstream": {
+ "scheme": "grpc",
+ "type": "roundrobin",
+ "nodes": {
+ "127.0.0.1:50051": 1
}
+ }
}'
```
-可以通过如下命令检查刚刚配置的路由:
+验证时,向路由发送 HTTP 请求:
```shell
-curl -i
"http://127.0.0.1:9080/zeebe/WorkflowInstanceCreate?bpmnProcessId=order-process&version=1&variables=\{\"orderId\":\"7\",\"ordervalue\":99\}"
+curl "http://127.0.0.1:9080/echo?msg=Hello"
```
-```Shell
-HTTP/1.1 200 OK
-Date: Wed, 13 Nov 2019 03:38:27 GMT
-Content-Type: application/json
-Transfer-Encoding: chunked
-Connection: keep-alive
-grpc-encoding: identity
-grpc-accept-encoding: gzip
-Server: APISIX web server
-Trailer: grpc-status
-Trailer: grpc-message
+您应该收到以下响应:
-{"workflowKey":"#2251799813685260","workflowInstanceKey":"#2251799813688013","bpmnProcessId":"order-process","version":1}
+```text
+{"msg":"Hello"}
```
-## 在返回体中展示 `grpc-status-details-bin`
+### 在响应体中显示错误详情
-如果 gRPC 服务返回了错误,返回头中可能存在 `grpc-status-details-bin` 字段对错误进行描述,你可以解码该字段,并展示在返回体中。
+以下示例演示了如何配置 `grpc-transcode` 插件,使其在 gRPC 服务器提供 `grpc-status-details-bin`
字段时,将其包含在响应头中用于错误报告,并将消息解码后展示在响应体中。
-上传 proto 文件:
+创建 proto 资源以存储 protobuf:
```shell
-curl http://127.0.0.1:9180/apisix/admin/protos/1 \
--H "X-API-KEY: $admin_key" -X PUT -d '
+curl "http://127.0.0.1:9180/apisix/admin/protos/hello-proto" -H "X-API-KEY:
$admin_key" -X PUT -d '
{
- "content" : "syntax = \"proto3\";
- package helloworld;
- service Greeter {
- rpc GetErrResp (HelloRequest) returns (HelloReply) {}
- }
- message HelloRequest {
- string name = 1;
- repeated string items = 2;
- }
- message HelloReply {
- string message = 1;
- repeated string items = 2;
- }"
+ "content": "syntax = \"proto3\";
+ package helloworld;
+ service Greeter {
+ rpc GetErrResp (HelloRequest) returns (HelloReply) {}
+ }
+ message HelloRequest {
+ string name = 1;
+ repeated string items = 2;
+ }
+ message HelloReply {
+ string message = 1;
+ repeated string items = 2;
+ }"
}'
```
-启用 `grpc-transcode` 插件,并设置选项 `show_status_in_body` 为 `true`:
+创建启用 `grpc-transcode` 插件的路由并将 `show_status_in_body` 设为 `true`:
```shell
-curl http://127.0.0.1:9180/apisix/admin/routes/1 \
--H "X-API-KEY: $admin_key" -X PUT -d '
+curl "http://127.0.0.1:9180/apisix/admin/routes/grpc-transcode-route" -H
"X-API-KEY: $admin_key" -X PUT -d '
{
- "methods": ["GET"],
- "uri": "/grpctest",
- "plugins": {
- "grpc-transcode": {
- "proto_id": "1",
- "service": "helloworld.Greeter",
- "method": "GetErrResp",
- "show_status_in_body": true
- }
- },
- "upstream": {
- "scheme": "grpc",
- "type": "roundrobin",
- "nodes": {
- "127.0.0.1:50051": 1
- }
+ "uri": "/hello",
+ "plugins": {
+ "grpc-transcode": {
+ "proto_id": "hello-proto",
+ "service": "helloworld.Greeter",
+ "method": "GetErrResp",
+ "show_status_in_body": true
}
+ },
+ "upstream": {
+ "scheme": "grpc",
+ "type": "roundrobin",
+ "nodes": {
+ "127.0.0.1:50051": 1
+ }
+ }
}'
```
-访问上面配置的 route:
+向路由发送请求:
```shell
-curl -i http://127.0.0.1:9080/grpctest?name=world
+curl -i "http://127.0.0.1:9080/hello?name=world"
```
-返回结果
+您应看到类似以下的错误响应:
-```Shell
+```shell
HTTP/1.1 503 Service Temporarily Unavailable
-Date: Wed, 10 Aug 2022 08:59:46 GMT
+Date: Wed, 21 Feb 2024 03:08:30 GMT
Content-Type: application/json
Transfer-Encoding: chunked
Connection: keep-alive
grpc-status: 14
grpc-message: Out of service
grpc-status-details-bin:
CA4SDk91dCBvZiBzZXJ2aWNlGlcKKnR5cGUuZ29vZ2xlYXBpcy5jb20vaGVsbG93b3JsZC5FcnJvckRldGFpbBIpCAESHFRoZSBzZXJ2ZXIgaXMgb3V0IG9mIHNlcnZpY2UaB3NlcnZpY2U
-Server: APISIX web server
+Server: APISIX/3.8.0
-{"error":{"details":[{"type_url":"type.googleapis.com\/helloworld.ErrorDetail","value":"\b\u0001\u0012\u001cThe
server is out of service\u001a\u0007service"}],"message":"Out of
service","code":14}}
+{"error":{"message":"Out of
service","code":14,"details":[{"value":"\b\u0001\u0012\u001cThe server is out
of
service\u001a\u0007service","type_url":"type.googleapis.com/helloworld.ErrorDetail"}]}}
```
-注意返回体中还存在未解码的字段,如果需要解码该字段,需要在上传的 proto 文件中加上该字段对应的 `message type`。
+注意响应中某些信息未被完全解码。
+
+要解码消息,请更新 protobuf 定义以添加 `ErrorDetail` 消息类型:
```shell
-curl http://127.0.0.1:9180/apisix/admin/protos/1 \
--H "X-API-KEY: $admin_key" -X PUT -d '
+curl "http://127.0.0.1:9180/apisix/admin/protos/hello-proto" -H "X-API-KEY:
$admin_key" -X PUT -d '
{
- "content" : "syntax = \"proto3\";
- package helloworld;
- service Greeter {
- rpc GetErrResp (HelloRequest) returns (HelloReply) {}
- }
- message HelloRequest {
- string name = 1;
- repeated string items = 2;
- }
- message HelloReply {
- string message = 1;
- repeated string items = 2;
- }
- message ErrorDetail {
- int64 code = 1;
- string message = 2;
- string type = 3;
- }"
+ "content": "syntax = \"proto3\";
+ package helloworld;
+ service Greeter {
+ rpc GetErrResp (HelloRequest) returns (HelloReply) {}
+ }
+ message HelloRequest {
+ string name = 1;
+ repeated string items = 2;
+ }
+ message HelloReply {
+ string message = 1;
+ repeated string items = 2;
+ }
+ message ErrorDetail {
+ int64 code = 1;
+ string message = 2;
+ string type = 3;
+ }"
}'
```
-同时配置选项 `status_detail_type` 为 `helloworld.ErrorDetail`:
+更新路由以配置 `status_detail_type`:
```shell
-curl http://127.0.0.1:9180/apisix/admin/routes/1 \
--H "X-API-KEY: $admin_key" -X PUT -d '
+curl "http://127.0.0.1:9180/apisix/admin/routes/grpc-transcode-route" -H
"X-API-KEY: $admin_key" -X PUT -d '
{
- "methods": ["GET"],
- "uri": "/grpctest",
- "plugins": {
- "grpc-transcode": {
- "proto_id": "1",
- "service": "helloworld.Greeter",
- "method": "GetErrResp",
- "show_status_in_body": true,
- "status_detail_type": "helloworld.ErrorDetail"
- }
- },
- "upstream": {
- "scheme": "grpc",
- "type": "roundrobin",
- "nodes": {
- "127.0.0.1:50051": 1
- }
+ "uri": "/hello",
+ "plugins": {
+ "grpc-transcode": {
+ "proto_id": "hello-proto",
+ "service": "helloworld.Greeter",
+ "method": "GetErrResp",
+ "show_status_in_body": true,
+ "status_detail_type": "helloworld.ErrorDetail"
}
+ },
+ "upstream": {
+ "scheme": "grpc",
+ "type": "roundrobin",
+ "nodes": {
+ "127.0.0.1:50051": 1
+ }
+ }
}'
```
-此时就能返回完全解码后的结果
+再次向路由发送请求:
+
+```shell
+curl -i "http://127.0.0.1:9080/hello?name=world"
+```
+
+您应看到错误消息已完全解码的响应:
-```Shell
+```shell
HTTP/1.1 503 Service Temporarily Unavailable
-Date: Wed, 10 Aug 2022 09:02:46 GMT
+Date: Wed, 21 Feb 2024 03:11:43 GMT
Content-Type: application/json
Transfer-Encoding: chunked
Connection: keep-alive
grpc-status: 14
grpc-message: Out of service
grpc-status-details-bin:
CA4SDk91dCBvZiBzZXJ2aWNlGlcKKnR5cGUuZ29vZ2xlYXBpcy5jb20vaGVsbG93b3JsZC5FcnJvckRldGFpbBIpCAESHFRoZSBzZXJ2ZXIgaXMgb3V0IG9mIHNlcnZpY2UaB3NlcnZpY2U
-Server: APISIX web server
+Server: APISIX/3.8.0
+
+{"error":{"message":"Out of service","code":14,"details":[{"message":"The
server is out of service","code":1,"type":"service"}]}}
+```
+
+### 配置编码器/解码器选项
+
+以下示例演示了如何为 `grpc-transcode`
插件配置编码器和解码器[选项](https://github.com/starwing/lua-protobuf?tab=readme-ov-file#options)。具体来说,您将对执行加法运算的方法应用
`int64_as_string` 选项,以观察其效果。
+
+创建 proto 资源以存储 protobuf:
+
+```shell
+curl "http://127.0.0.1:9180/apisix/admin/protos/plus-proto" -H "X-API-KEY:
$admin_key" -X PUT -d '
+{
+ "content": "syntax = \"proto3\";
+ package helloworld;
+ service Greeter {
+ rpc Plus (PlusRequest) returns (PlusReply) {}
+ }
+ message PlusRequest {
+ int64 a = 1;
+ int64 b = 2;
+ }
+ message PlusReply {
+ int64 result = 1;
+ }"
+}'
+```
+
+创建启用 `grpc-transcode` 插件的路由:
-{"error":{"details":[{"type":"service","message":"The server is out of
service","code":1}],"message":"Out of service","code":14}}
+```shell
+curl "http://127.0.0.1:9180/apisix/admin/routes/grpc-transcode-route" -H
"X-API-KEY: $admin_key" -X PUT -d '
+{
+ "uri": "/plus",
+ "plugins": {
+ "grpc-transcode": {
+ "proto_id": "plus-proto",
+ "service": "helloworld.Greeter",
+ "method": "Plus"
+ }
+ },
+ "upstream": {
+ "scheme": "grpc",
+ "type": "roundrobin",
+ "nodes": {
+ "127.0.0.1:50051": 1
+ }
+ }
+}'
```
-## 删除插件
+向路由发送请求:
-当你需要禁用 `grpc-transcode` 插件时,可以通过以下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务:
+```shell
+curl "http://127.0.0.1:9080/plus?a=1237528374197491&b=1237528374197491"
+```
+
+您应看到显示两数之和的响应:
+
+```text
+{"result":2.475056748395e+15}
+```
+
+注意当结果以数字形式返回时会损失精度。更新路由以使用 `int64_as_string` 选项:
```shell
-curl http://127.0.0.1:9180/apisix/admin/routes/111 -H "X-API-KEY: $admin_key"
-X PUT -d '
+curl "http://127.0.0.1:9180/apisix/admin/routes/grpc-transcode-route" -H
"X-API-KEY: $admin_key" -X PUT -d '
{
- "uri": "/grpctest",
- "plugins": {},
- "upstream": {
- "scheme": "grpc",
- "type": "roundrobin",
- "nodes": {
- "127.0.0.1:50051": 1
- }
+ "uri": "/plus",
+ "plugins": {
+ "grpc-transcode": {
+ "proto_id": "plus-proto",
+ "service": "helloworld.Greeter",
+ "method": "Plus",
+ "pb_option":["int64_as_string"]
+ }
+ },
+ "upstream": {
+ "scheme": "grpc",
+ "type": "roundrobin",
+ "nodes": {
+ "127.0.0.1:50051": 1
}
+ }
}'
```
+
+再次向路由发送请求:
+
+```shell
+curl "http://127.0.0.1:9080/plus?a=1237528374197491&b=1237528374197491"
+```
+
+您应看到精度完整的两数之和:
+
+```text
+{"result":"#2475056748394982"}
+```
diff --git a/docs/zh/latest/plugins/grpc-web.md
b/docs/zh/latest/plugins/grpc-web.md
index 6135c6114..7e613f9b4 100644
--- a/docs/zh/latest/plugins/grpc-web.md
+++ b/docs/zh/latest/plugins/grpc-web.md
@@ -6,7 +6,7 @@ keywords:
- Plugin
- gRPC Web
- grpc-web
-description: 本文介绍了关于 Apache APISIX `grpc-web` 插件的基本信息及使用方法。
+description: grpc-web 插件使网关能够处理来自浏览器和 JavaScript 客户端的 gRPC-Web 请求,将其转换为标准 gRPC
调用并转发给上游 gRPC 服务。
---
<!--
@@ -28,19 +28,58 @@ description: 本文介绍了关于 Apache APISIX `grpc-web` 插件的基本信
#
-->
+<head>
+ <link rel="canonical" href="https://docs.api7.ai/hub/grpc-web" />
+</head>
+
## 描述
-`grpc-web` 插件是一个代理插件,可以处理从 JavaScript 客户端到 gRPC Service 的 [gRPC
Web](https://github.com/grpc/grpc-web) 请求。
+gRPC 是一个基于 HTTP/2 和 Protocol Buffers 的高性能 RPC 框架,但浏览器原生不支持它。gRPC-Web
定义了一种浏览器兼容协议,可通过 HTTP/1.1 或 HTTP/2 发送 gRPC 请求。
+
+`grpc-web` 插件将 gRPC-Web 请求转换为原生 gRPC 调用,并转发给上游 gRPC 服务。
## 属性
-| 名称 | 类型 | 必选项 | 默认值
| 描述 |
-|---------------------| -------
|----|-----------------------------------------|----------------------------------------------------------------|
-| cors_allow_headers | string | 否 | "content-type,x-grpc-web,x-user-agent"
| 允许跨域访问时请求方携带哪些非 `CORS 规范` 以外的 Header。如果你有多个 Header,请使用 `,` 分割。 |
+| 名称 | 类型 | 必选项 | 默认值 |
描述 |
+|----------------------|---------|--------|-------------------------------------------|------------------------------------------------|
+| cors_allow_headers | string | 否 |
`content-type,x-grpc-web,x-user-agent` | 跨域请求中允许携带的请求头,多个请求头用 `,` 分隔。 |
+
+## 请求处理
+
+`grpc-web` 插件使用特定的 HTTP 方法、内容类型和 CORS 规则处理客户端请求。
+
+### 支持的 HTTP 方法
+
+该插件支持:
+
+- `POST`:用于 gRPC-Web 请求
+- `OPTIONS`:用于 CORS 预检请求
+
+详情请参阅 [CORS
support](https://github.com/grpc/grpc-web/blob/master/doc/browser-features.md#cors-support)。
+
+### 支持的内容类型
+
+该插件识别以下内容类型:
+
+- `application/grpc-web`
+- `application/grpc-web-text`
+- `application/grpc-web+proto`
+- `application/grpc-web-text+proto`
+
+它会自动解码二进制或 base64 文本格式的消息,并将其转换为标准 gRPC 格式转发给上游服务器。详情请参阅 [Protocol differences
vs gRPC over
HTTP2](https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-WEB.md#protocol-differences-vs-grpc-over-http2)。
+
+### CORS 处理
+
+该插件自动处理跨域请求。默认情况下:
-## 启用插件
+- 允许所有来源(`*`)
+- 允许 `POST` 请求
+- 接受的请求头:`content-type`、`x-grpc-web`、`x-user-agent`
+- 暴露的响应头:`grpc-status`、`grpc-message`
-你可以通过如下命令在指定路由上启用 `gRPC-web` 插件:
+## 示例
+
+以下示例演示了如何配置并使用带有 gRPC-Web 客户端的 `grpc-web` 插件。
:::note
@@ -52,54 +91,160 @@ admin_key=$(yq '.deployment.admin.admin_key[0].key'
conf/config.yaml | sed 's/"/
:::
+### 前提条件
+
+在进行示例操作之前,请完成以下步骤来设置上游服务器和 gRPC-Web 客户端。
+
+#### 启动上游服务器
+
+启动一个 [grpcbin 服务器](https://github.com/moul/grpcbin)作为示例上游:
+
```shell
-curl http://127.0.0.1:9180/apisix/admin/routes/1 \
--H "X-API-KEY: $admin_key" -X PUT -d '
-{
- "uri":"/grpc/web/*",
- "plugins":{
- "grpc-web":{}
- },
- "upstream":{
- "scheme":"grpc",
- "type":"roundrobin",
- "nodes":{
- "127.0.0.1:1980":1
- }
- }
-}'
+docker run -d \
+ --name grpcbin \
+ -p 9000:9000 \
+ moul/grpcbin
```
-## 测试插件
+#### 生成 gRPC-Web 客户端代码
+
+下载 Protocol Buffer 定义文件 `hello.proto`:
+
+```shell
+curl -O https://raw.githubusercontent.com/moul/pb/master/hello/hello.proto
+```
-请参考 [gRPC-Web Client Runtime Library](https://www.npmjs.com/package/grpc-web)
或 [Apache APISIX gRPC Web Test
Framework](https://github.com/apache/apisix/tree/master/t/plugin/grpc-web)
了解如何配置你的 Web 客户端。
+安装 [`protobuf`](https://github.com/protocolbuffers/protobuf/releases) 和
[`protoc-gen-grpc-web`](https://github.com/grpc/grpc-web/releases)。
-运行 gRPC Web 客户端后,你可以从浏览器或通过 Node.js 向 APISIX 发出请求。
+从 `hello.proto` 生成 gRPC-Web 客户端代码:
-:::note
+```shell
+protoc \
+ --js_out=import_style=commonjs:. \
+ --grpc-web_out=import_style=commonjs,mode=grpcwebtext:. \
+ hello.proto
+```
-请求方式仅支持 `POST` 和 `OPTIONS`,详细信息请参考:[CORS
support](https://github.com/grpc/grpc-web/blob/master/doc/browser-features.md#cors-support)
。
+您应在当前目录看到两个生成的文件:`hello_pb.js`(Protocol Buffers 消息类)和
`hello_grpc_web_pb.js`(gRPC-Web 客户端存根)。
-内容类型支持
`application/grpc-web`、`application/grpc-web-text`、`application/grpc-web+proto`、`application/grpc-web-text+proto`,详细信息请参考:[Protocol
differences vs gRPC over
HTTP2](https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-WEB.md#protocol-differences-vs-grpc-over-http2)
。
+#### 创建客户端
-:::
+创建 Node.js 项目并安装所需依赖:
+
+```shell
+npm init -y
+npm install xhr2 grpc-web google-protobuf
+```
+
+创建客户端文件:
+
+```js title="client.js"
+const XMLHttpRequest = require('xhr2');
+const { HelloServiceClient } = require('./hello_grpc_web_pb');
+const { HelloRequest } = require('./hello_pb');
+
+global.XMLHttpRequest = XMLHttpRequest;
+
+function sayHello(){
+ const client = new HelloServiceClient('http://127.0.0.1:9080/grpc/web',
null, {
+ format: 'text',
+ });
+ const req = new HelloRequest();
+ req.setGreeting('jack');
+
+ const call = client.sayHello(req, {}, (err, resp) => {
+ if (err) {
+ console.error('grpc error:', err.code, err.message);
+ } else {
+ console.log('reply:', resp.getReply());
+ }
+ });
+
+ call.on('metadata', (metadata) => {
+ console.log('Response headers:', metadata);
+ });
+}
+
+function lotsOfReplies() {
+ const client = new HelloServiceClient('http://127.0.0.1:9080/grpc/web',
null, {
+ format: 'text',
+ });
+ const req = new HelloRequest();
+ req.setGreeting('rep');
+ const stream = client.lotsOfReplies(req, {});
+
+ stream.on('metadata', (metadata) => {
+ console.log('Response headers:', metadata);
+ });
+
+ stream.on('data', (response) => {
+ console.log('Reply:', response.getReply());
+ });
+
+ stream.on('end', () => {
+ console.log('Stream ended');
+ });
+
+ stream.on('error', (err) => {
+ console.error('Error:', err);
+ });
+}
+
+lotsOfReplies()
+sayHello()
+```
+
+您可以通过 `node client.js` 运行客户端,向 gRPC 服务器通过网关发送一元请求和服务端流式请求。
-## 删除插件
+### 代理 gRPC-Web(前缀匹配路由)
-当你需要禁用 `grpc-web` 插件时,可以通过如下命令删除相应的 `JSON` 配置,APISIX 将会自动重新加载相关配置,无需重启服务:
+以下示例演示如何使用前面设置的 gRPC-Web 客户端配置并使用 `grpc-web` 插件。
+
+创建启用 `grpc-web` 插件的路由:
```shell
-curl http://127.0.0.1:9180/apisix/admin/routes/1 \
--H "X-API-KEY: $admin_key" -X PUT -d '
+curl "http://127.0.0.1:9180/apisix/admin/routes/grpc-web-route" -H "X-API-KEY:
$admin_key" -X PUT -d '
{
- "uri":"/grpc/web/*",
- "plugins":{},
- "upstream":{
- "scheme":"grpc",
- "type":"roundrobin",
- "nodes":{
- "127.0.0.1:1980":1
- }
+ "uri": "/grpc/web/*",
+ "plugins": {
+ "grpc-web": {}
+ },
+ "upstream": {
+ "scheme": "grpc",
+ "type": "roundrobin",
+ "nodes": {
+ "127.0.0.1:9000": 1
}
+ }
}'
```
+
+:::note
+
+在 APISIX 3.15.0 之前的版本中,路由 URI 必须使用前缀匹配,因为 gRPC-Web 客户端会在请求 URI
中包含包名、服务名和方法名。在这些版本中使用绝对 URI 匹配会导致请求无法匹配路由。
+
+在本示例中,路由 URI 必须配置为 `/grpc/web/*`,才能正确匹配如
`/grpc/web/hello.HelloService/SayHello` 这样的客户端请求。使用更宽泛的前缀(如
`/grpc/*`)会导致网关无法正确提取完整的服务路径,从而产生 `unknown service web/hello.HelloService` 等错误。
+
+:::
+
+运行客户端向网关路由发送请求:
+
+```shell
+node client.js
+```
+
+您应看到来自上游 gRPC 服务器的回复:
+
+```text
+Response headers: {
+ ...
+ 'access-control-allow-origin': '*',
+ 'access-control-expose-headers': 'grpc-message,grpc-status'
+}
+Response headers: {
+ ...
+ 'access-control-allow-origin': '*',
+ 'access-control-expose-headers': 'grpc-message,grpc-status'
+}
+reply: hello jack
+```