C# 用于从调用WCF服务的.NET核心应用程序发送cookie的中间件
我目前正在开发一个连接到API的客户端,该API是多个SOAP服务端点的门面。我正在使用.NETCore3.1 SOAP服务是由其他公司编写的,无法更改。我们有多个服务,每个服务都有“登录”方法。成功登录后,会话cookie将以标头的形式返回。为了访问其他方法,需要将Cookie追加到后续的每个调用中 为了实现这一点,我们编写了中间件,假设捕获登录方法的响应,并存储cookie。然后,它应该更改对WCF服务的请求,将cookie添加到头中 不幸的是,中间件仅在调用API路径时触发,而不是在进行SOAP服务调用时触发。假设我在API中调用path“/test”。中间软件已正确启动并执行。之后,我的代码在执行SOAP服务调用时被执行,不幸的是中间件没有被触发 我已经研究了很多话题,比如 但是,我们希望能够全局更改消息,而不是在每次调用时显式地“手动”添加cookie。另外,当会话过期时,我们希望捕捉这种情况并再次登录,而用户不会注意到。这就是为什么编写中间件类如此重要的原因 因此,我有我的连接服务(使用Microsoft WCS Web Service Reference Provider生成的代理),称为:C# 用于从调用WCF服务的.NET核心应用程序发送cookie的中间件,c#,wcf,.net-core,soap,C#,Wcf,.net Core,Soap,我目前正在开发一个连接到API的客户端,该API是多个SOAP服务端点的门面。我正在使用.NETCore3.1 SOAP服务是由其他公司编写的,无法更改。我们有多个服务,每个服务都有“登录”方法。成功登录后,会话cookie将以标头的形式返回。为了访问其他方法,需要将Cookie追加到后续的每个调用中 为了实现这一点,我们编写了中间件,假设捕获登录方法的响应,并存储cookie。然后,它应该更改对WCF服务的请求,将cookie添加到头中 不幸的是,中间件仅在调用API路径时触发,而不是在进行S
MyServiceClient client = new MyServiceClient();
var logged = await client.loginAsync(new loginRequest("login", "password"));
if (logged.@return) {
//doing stuff here (getting cookie, storing in places)
}
var client = new ServiceClient(...);
client.Endpoint.EndpointBehaviors.Add(new MyEndpointBehavior());
loginAsync方法响应的头中有cookie。我们如何注册某种中间件或拦截器来获取响应并从该方法中提取cookie
此外,我们还有服务电话:
var data = await client.getSchedule(new getScheduleRequest(DateTime.Parse("2020-06-01"), DateTime.Parse("2020-06-23")));
现在,我希望我的消息检查器/中间件/拦截器更改请求并添加存储的cookie作为头
中间件已在Startup.cs中注册:
app.UseMiddleware<WCFSessionMiddleware>();
我会感谢任何帮助,无论帮助有多小。这取决于您使用的绑定,但默认情况下,客户端应自动发送以前请求中收到的cookie 例如: 这是因为自动生成的绑定代码类似于:
private static System.ServiceModel.Channels.Binding GetBindingForEndpoint(EndpointConfiguration endpointConfiguration)
{
if ((endpointConfiguration == EndpointConfiguration.BasicHttpBinding_IService))
{
System.ServiceModel.BasicHttpBinding result = new System.ServiceModel.BasicHttpBinding();
result.MaxBufferSize = int.MaxValue;
result.ReaderQuotas = System.Xml.XmlDictionaryReaderQuotas.Max;
result.MaxReceivedMessageSize = int.MaxValue;
result.AllowCookies = true; // <- THIS
return result;
}
throw new System.InvalidOperationException(string.Format("Could not find endpoint with name \'{0}\'.", endpointConfiguration));
}
下面是消息检查器:
// Reads a cookie named RESP_COOKIE from responses and put its value in a cookie named REQ_COOKIE in the requests
public class MyMessageInspector : IClientMessageInspector
{
private const string RESP_COOKIE_NAME = "RESP_COOKIE";
private const string REQ_COOKIE_NAME = "REQ_COOKIE";
private string cookieVal = null;
// Handles the service's responses
public void AfterReceiveReply(ref Message reply, object correlationState)
{
HttpResponseMessageProperty httpReplyMessage;
object httpReplyMessageObject;
// WCF can perform operations with many protocols, not only HTTP, so we need to make sure that we are using HTTP
if (reply.Properties.TryGetValue(HttpResponseMessageProperty.Name, out httpReplyMessageObject))
{
httpReplyMessage = httpReplyMessageObject as HttpResponseMessageProperty;
if (!string.IsNullOrEmpty(httpReplyMessage.Headers["Set-Cookie"]))
{
var cookies = httpReplyMessage.Headers["Set-Cookie"];
cookieVal = cookies.Split(";")
.Select(c => c.Split("="))
.Select(s => new { Name = s[0], Value = s[1] })
.FirstOrDefault(c => c.Name.Equals(RESP_COOKIE_NAME, StringComparison.InvariantCulture))
?.Value;
}
}
}
// Invoked when a request is made
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
HttpRequestMessageProperty httpRequestMessage;
object httpRequestMessageObject;
if (!string.IsNullOrEmpty(cookieVal))
{
var prop = new HttpRequestMessageProperty();
prop.Headers.Add(HttpRequestHeader.Cookie, $"{REQ_COOKIE_NAME}={cookieVal}");
request.Properties.Add(HttpRequestMessageProperty.Name, prop);
}
return null;
}
}
然后我们可以这样配置它:
MyServiceClient client = new MyServiceClient();
var logged = await client.loginAsync(new loginRequest("login", "password"));
if (logged.@return) {
//doing stuff here (getting cookie, storing in places)
}
var client = new ServiceClient(...);
client.Endpoint.EndpointBehaviors.Add(new MyEndpointBehavior());
您可以使用属性将行为应用于界面,下面是一个演示:
public class ClientMessage : IClientMessageInspector
{
public void AfterReceiveReply(ref Message reply, object correlationState)
{
}
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
return null;
}
}
[AttributeUsage(AttributeTargets.Interface, AllowMultiple = false)]
public class MyContractBehaviorAttribute : Attribute, IContractBehavior
{
public void AddBindingParameters(ContractDescription contractDescription, ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
return;
}
public void ApplyClientBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
clientRuntime.ClientMessageInspectors.Add(new ClientMessage());
}
public void ApplyDispatchBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, DispatchRuntime dispatchRuntime)
{
return;
}
public void Validate(ContractDescription contractDescription, ServiceEndpoint endpoint)
{
return;
}
}
最后,我们可以将其直接应用于接口:
感谢您的回复。这就是我现在解决这个问题的方法,。我有一个稍微简单一点的解决方案,但正如我所注意到的,您仍然需要创建客户机并在每次使用它时注册行为。
public class ClientMessage : IClientMessageInspector
{
public void AfterReceiveReply(ref Message reply, object correlationState)
{
}
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
return null;
}
}
[AttributeUsage(AttributeTargets.Interface, AllowMultiple = false)]
public class MyContractBehaviorAttribute : Attribute, IContractBehavior
{
public void AddBindingParameters(ContractDescription contractDescription, ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
return;
}
public void ApplyClientBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
clientRuntime.ClientMessageInspectors.Add(new ClientMessage());
}
public void ApplyDispatchBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, DispatchRuntime dispatchRuntime)
{
return;
}
public void Validate(ContractDescription contractDescription, ServiceEndpoint endpoint)
{
return;
}
}