gRPC-Web 中的拦截器
我们很高兴地宣布,自版本 1.1.0 起,gRPC-web 已支持*拦截器*(interceptors)。虽然当前的设计基于其他 gRPC 语言中提供的 gRPC 客户端拦截器,但它也包含了 gRPC-web 特有的功能,这些功能应使拦截器易于采用并与现代 Web 框架一起使用。
简介
与其他 gRPC 语言类似,gRPC-web 支持*一元*(unary) 和服务器*流式*(server-streaming) 拦截器。对于每种拦截器,我们都定义了一个接口,其中包含一个单独的 intercept()
方法
UnaryInterceptor
StreamInterceptor
以下是 UnaryInterceptor
接口的声明方式
/*
* @interface
*/
const UnaryInterceptor = function() {};
/**
* @template REQUEST, RESPONSE
* @param {!Request<REQUEST, RESPONSE>} request
* @param {function(!Request<REQUEST,RESPONSE>):!Promise<!UnaryResponse<RESPONSE>>}
* invoker
* @return {!Promise<!UnaryResponse<RESPONSE>>}
*/
UnaryInterceptor.prototype.intercept = function(request, invoker) {};
intercept()
方法接收两个参数
- 一个类型为 grpc.web.Request 的
request
- 一个
invoker
,它在被调用时执行实际的 RPC
StreamInterceptor
接口的声明类似,但 invoker
的返回类型是 ClientReadableStream
而不是 Promise
。有关实现详情,请参阅 interceptor.js。
注意
StreamInterceptor
可以应用于任何返回类型为 ClientReadableStream
的 RPC,无论它是一元 RPC 还是服务器流式 RPC。拦截器可以做什么?
拦截器允许您执行以下操作
- 在传递原始 gRPC 请求之前对其进行更新 — 例如,您可以注入额外的认证头等信息
- 操纵原始 invoker 函数的行为,例如绕过调用以便您可以使用缓存结果
- 在将响应返回给客户端之前对其进行更新
接下来您将看到一些示例。
一元拦截器示例
下面的代码演示了一个执行以下操作的一元拦截器
- 在 RPC 之前,它会在 gRPC 请求消息前添加一个字符串。
- 在接收到响应消息后,它会在 gRPC 响应消息前添加一个字符串。
这个简单的一元拦截器被定义为一个实现了 UnaryInterceptor
接口的类
/**
* @constructor
* @implements {UnaryInterceptor}
*/
const SimpleUnaryInterceptor = function() {};
/** @override */
SimpleUnaryInterceptor.prototype.intercept = function(request, invoker) {
// Update the request message before the RPC.
const reqMsg = request.getRequestMessage();
reqMsg.setMessage('[Intercept request]' + reqMsg.getMessage());
// After the RPC returns successfully, update the response.
return invoker(request).then((response) => {
// You can also do something with response metadata here.
console.log(response.getMetadata());
// Update the response message.
const responseMsg = response.getResponseMessage();
responseMsg.setMessage('[Intercept response]' + responseMsg.getMessage());
return response;
});
};
流式拦截器示例
在使用 StreamInterceptor
拦截来自 ClientReadableStream
的服务器流式响应时,需要更加小心。以下是需要遵循的主要步骤
- 创建一个
ClientReadableStream
包装类,并使用它来拦截流事件,例如接收服务器响应。 - 创建一个实现
StreamInterceptor
并使用流包装类的类。
以下示例流包装类拦截响应并在响应消息前添加一个字符串
/**
* A ClientReadableStream wrapper.
*
* @template RESPONSE
* @implements {ClientReadableStream}
* @constructor
* @param {!ClientReadableStream<RESPONSE>} stream
*/
const InterceptedStream = function(stream) {
this.stream = stream;
};
/** @override */
InterceptedStream.prototype.on = function(eventType, callback) {
if (eventType == 'data') {
const newCallback = (response) => {
// Update the response message.
const msg = response.getMessage();
response.setMessage('[Intercept response]' + msg);
// Pass along the updated response.
callback(response);
};
// Register the new callback.
this.stream.on(eventType, newCallback);
} else {
// You can also override 'status', 'end', and 'error' eventTypes.
this.stream.on(eventType, callback);
}
return this;
};
/** @override */
InterceptedStream.prototype.cancel = function() {
this.stream.cancel();
return this;
};
示例拦截器的 intercept()
方法返回一个包装过的流
/**
* @constructor
* @implements {StreamInterceptor}
*/
const TestStreamInterceptor = function() {};
/** @override */
TestStreamInterceptor.prototype.intercept = function(request, invoker) {
return new InterceptedStream(invoker(request));
};
绑定拦截器
通过使用适当的选项键传递拦截器实例数组,您可以在实例化客户端时将拦截器绑定到客户端
const promiseClient = new MyServicePromiseClient(
host, creds, {'unaryInterceptors': [interceptor1, interceptor2, interceptor3]});
const client = new MyServiceClient(
host, creds, {'streamInterceptors': [interceptor1, interceptor2, interceptor3]});
注意
拦截器在处理请求时按逆序执行,在处理响应时按正序执行,如下所示

反馈
在使用 grpc-web
时遇到问题或需要新功能?请在 grpc-web 仓库中提交一个 issue。如果您有一般性问题或意见,可以考虑发布到 gRPC 邮件列表,或者发送邮件至 grpc-web-team@google.com。