我们很高兴地宣布从1.1.0版开始支持gRPC-web中的拦截器(interceptor)。虽然当前的设计基于其他gRPC语言提供的gRPC客户端拦截器,但它也包括gRPC特定于Web的特性,这些特性应该会使拦截器易于采用,并与现代Web框架一起使用。
介绍
与其他gRPC语言类似,gRPC-web支持unary和server-streaming拦截器。对于每种拦截器,我们都定义了一个包含单个intercept()方法的接口:
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()方法接受两个参数:
StreamInterceptor接口声明类似,只是调用器返回类型是ClientReadablaStream而不是Promise。有关实现的详细信息,请参阅interceptor.js。
https://github.com/grpc/grpc-web/blob/master/javascript/net/grpc/web/interceptor.js
注:StreamInteceptor可以应用于任何具有ClientReadableStream返回类型的RPC,无论它是unary还是server-streaming RPC。
我能用拦截器做什么?
拦截器允许你执行以下操作:
接下来你将看到一些示例。
Unary拦截器示例
下面给出的代码说明了执行以下操作的unary拦截器:
这个简单的一元拦截器被定义为实现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;
});
};
Stream截取器示例 在使用StreamInterceptor拦截来自ClientReadableStream的服务器流响应时需要更加小心。以下是要遵循的主要步骤:
下面的示例stream-wrapper类截取响应,并在响应消息前面附加一个字符串:
/**
* 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()方法返回一个wrapped stream:
/**
* @constructor
* @implements {StreamInterceptor}
*/
const TestStreamInterceptor = function() {};
/** @override */
TestStreamInterceptor.prototype.intercept = function(request, invoker) {
return new InterceptedStream(invoker(request));
};
绑定拦截器
通过使用适当的Option键传递拦截器实例数组,可以在实例化客户端时将拦截器绑定到客户端:
const promiseClient = new MyServicePromiseClient(
host, creds, {'unaryInterceptors': [interceptor1, interceptor2, interceptor3]});
const client = new MyServiceClient(
host, creds, {'streamInterceptors': [interceptor1, interceptor2, interceptor3]});
注:拦截器的执行顺序与请求处理和响应处理的顺序相反,如下所示:
反馈
发现grpc-web有问题还是需要功能?通过grpc-web仓库提交问题。如果你有一般性的问题或意见,请考虑发布到gRPC邮件列表或向我们发送电子邮件至gRPC-web-team@google.com。
https://github.com/grpc/grpc-web/issues/new
https://groups.google.com/forum/#!forum/grpc-io