使用 Azure 服务结构的默认客户端时,如何将消息 header 添加到请求中?
How to add message header to the request when using default client of Azure service fabric?
我想知道是否可以将自定义消息 header 注入传出请求以携带附加信息,而无需反序列化有效负载以完成诸如身份验证、验证或请求关联之类的功能,例如通过 messagesinspector 提供的 wcf ?
几周前我问过 the same question on the MSDN forum,但我没有得到回复。
我查看了客户端库的源代码,没有找到添加的方法headers。恐怕唯一的方法是将它们添加为方法调用的一部分。这可以通过使用 request-classes 作为方法参数并为它们使用继承来完成。 (例如 RequestBase class 和 headers [Authorization, ClientInfo, ...])。然后,您必须通过包装所有调用或手动设置来确保为每个请求设置这些 headers。
非常感谢 Service Fabric 团队的进一步说明。
更新
借助 SDK v2,您现在可以(相对)轻松地修改 Reliable Services 和 Actors 的 headers。请注意,在下面的示例中,为简洁起见,省略了一些包装器成员。
客户端
我们使用 ServiceProxyFactory
创建代理而不是静态 ServiceProxy
。然后我们可以包装 IServiceRemotingClientFactory
和 IServiceRemotingClient
并拦截服务调用。 ActorProxyFactory
也可以这样做。请注意,这会覆盖 WcfServiceRemotingProviderAttribute
等属性的行为,因为我们自己明确指定了客户端工厂。
_proxyFactory = new ServiceProxyFactory(c => new ServiceRemotingClientFactoryWrapper(
// we can use any factory here
new WcfServiceRemotingClientFactory(callbackClient: c)));
private class ServiceRemotingClientFactoryWrapper : IServiceRemotingClientFactory
{
private readonly IServiceRemotingClientFactory _inner;
public ServiceRemotingClientFactoryWrapper(IServiceRemotingClientFactory inner)
{
_inner = inner;
}
public async Task<IServiceRemotingClient> GetClientAsync(Uri serviceUri, ServicePartitionKey partitionKey, TargetReplicaSelector targetReplicaSelector,
string listenerName, OperationRetrySettings retrySettings, CancellationToken cancellationToken)
{
var client = await _inner.GetClientAsync(serviceUri, partitionKey, targetReplicaSelector, listenerName, retrySettings, cancellationToken).ConfigureAwait(false);
return new ServiceRemotingClientWrapper(client);
}
}
private class ServiceRemotingClientWrapper : IServiceRemotingClient
{
private readonly IServiceRemotingClient _inner;
public ServiceRemotingClientWrapper(IServiceRemotingClient inner)
{
_inner = inner;
}
public Task<byte[]> RequestResponseAsync(ServiceRemotingMessageHeaders messageHeaders, byte[] requestBody)
{
// use messageHeaders.AddHeader() here
return _inner.RequestResponseAsync(messageHeaders, requestBody);
}
public void SendOneWay(ServiceRemotingMessageHeaders messageHeaders, byte[] requestBody)
{
// use messageHeaders.AddHeader() here
_inner.SendOneWay(messageHeaders, requestBody);
}
}
服务器
继承ServiceRemotingDispatcher
和ActorServiceRemotingDispatcher
检查headers。
class CustomServiceRemotingDispatcher : ServiceRemotingDispatcher
{
public override async Task<byte[]> RequestResponseAsync(IServiceRemotingRequestContext requestContext, ServiceRemotingMessageHeaders messageHeaders, byte[] requestBody)
{
// read messageHeaders here
// or alternatively put them in an AsyncLocal<T> scope
// so they can be accessed down the call chain
return base.RequestResponseAsync(requestContext, messageHeaders, requestBody);
}
}
要使用此 class,我们需要再次通过直接创建通信侦听器来覆盖 ServiceRemotingProviderAttribute
:
class MyService : StatelessService
{
protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
{
yield return new ServiceInstanceListener(context => new WcfServiceRemotingListener(context, new CustomServiceRemotingDispatcher());
}
}
我想知道是否可以将自定义消息 header 注入传出请求以携带附加信息,而无需反序列化有效负载以完成诸如身份验证、验证或请求关联之类的功能,例如通过 messagesinspector 提供的 wcf ?
几周前我问过 the same question on the MSDN forum,但我没有得到回复。
我查看了客户端库的源代码,没有找到添加的方法headers。恐怕唯一的方法是将它们添加为方法调用的一部分。这可以通过使用 request-classes 作为方法参数并为它们使用继承来完成。 (例如 RequestBase class 和 headers [Authorization, ClientInfo, ...])。然后,您必须通过包装所有调用或手动设置来确保为每个请求设置这些 headers。
非常感谢 Service Fabric 团队的进一步说明。
更新
借助 SDK v2,您现在可以(相对)轻松地修改 Reliable Services 和 Actors 的 headers。请注意,在下面的示例中,为简洁起见,省略了一些包装器成员。
客户端
我们使用 ServiceProxyFactory
创建代理而不是静态 ServiceProxy
。然后我们可以包装 IServiceRemotingClientFactory
和 IServiceRemotingClient
并拦截服务调用。 ActorProxyFactory
也可以这样做。请注意,这会覆盖 WcfServiceRemotingProviderAttribute
等属性的行为,因为我们自己明确指定了客户端工厂。
_proxyFactory = new ServiceProxyFactory(c => new ServiceRemotingClientFactoryWrapper(
// we can use any factory here
new WcfServiceRemotingClientFactory(callbackClient: c)));
private class ServiceRemotingClientFactoryWrapper : IServiceRemotingClientFactory
{
private readonly IServiceRemotingClientFactory _inner;
public ServiceRemotingClientFactoryWrapper(IServiceRemotingClientFactory inner)
{
_inner = inner;
}
public async Task<IServiceRemotingClient> GetClientAsync(Uri serviceUri, ServicePartitionKey partitionKey, TargetReplicaSelector targetReplicaSelector,
string listenerName, OperationRetrySettings retrySettings, CancellationToken cancellationToken)
{
var client = await _inner.GetClientAsync(serviceUri, partitionKey, targetReplicaSelector, listenerName, retrySettings, cancellationToken).ConfigureAwait(false);
return new ServiceRemotingClientWrapper(client);
}
}
private class ServiceRemotingClientWrapper : IServiceRemotingClient
{
private readonly IServiceRemotingClient _inner;
public ServiceRemotingClientWrapper(IServiceRemotingClient inner)
{
_inner = inner;
}
public Task<byte[]> RequestResponseAsync(ServiceRemotingMessageHeaders messageHeaders, byte[] requestBody)
{
// use messageHeaders.AddHeader() here
return _inner.RequestResponseAsync(messageHeaders, requestBody);
}
public void SendOneWay(ServiceRemotingMessageHeaders messageHeaders, byte[] requestBody)
{
// use messageHeaders.AddHeader() here
_inner.SendOneWay(messageHeaders, requestBody);
}
}
服务器
继承ServiceRemotingDispatcher
和ActorServiceRemotingDispatcher
检查headers。
class CustomServiceRemotingDispatcher : ServiceRemotingDispatcher
{
public override async Task<byte[]> RequestResponseAsync(IServiceRemotingRequestContext requestContext, ServiceRemotingMessageHeaders messageHeaders, byte[] requestBody)
{
// read messageHeaders here
// or alternatively put them in an AsyncLocal<T> scope
// so they can be accessed down the call chain
return base.RequestResponseAsync(requestContext, messageHeaders, requestBody);
}
}
要使用此 class,我们需要再次通过直接创建通信侦听器来覆盖 ServiceRemotingProviderAttribute
:
class MyService : StatelessService
{
protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
{
yield return new ServiceInstanceListener(context => new WcfServiceRemotingListener(context, new CustomServiceRemotingDispatcher());
}
}