Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/334.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 用于从调用WCF服务的.NET核心应用程序发送cookie的中间件_C#_Wcf_.net Core_Soap - Fatal编程技术网

C# 用于从调用WCF服务的.NET核心应用程序发送cookie的中间件

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

我目前正在开发一个连接到API的客户端,该API是多个SOAP服务端点的门面。我正在使用.NETCore3.1

SOAP服务是由其他公司编写的,无法更改。我们有多个服务,每个服务都有“登录”方法。成功登录后,会话cookie将以标头的形式返回。为了访问其他方法,需要将Cookie追加到后续的每个调用中

为了实现这一点,我们编写了中间件,假设捕获登录方法的响应,并存储cookie。然后,它应该更改对WCF服务的请求,将cookie添加到头中

不幸的是,中间件仅在调用API路径时触发,而不是在进行SOAP服务调用时触发。假设我在API中调用path“/test”。中间软件已正确启动并执行。之后,我的代码在执行SOAP服务调用时被执行,不幸的是中间件没有被触发

我已经研究了很多话题,比如

但是,我们希望能够全局更改消息,而不是在每次调用时显式地“手动”添加cookie。另外,当会话过期时,我们希望捕捉这种情况并再次登录,而用户不会注意到。这就是为什么编写中间件类如此重要的原因

因此,我有我的连接服务(使用Microsoft WCS Web Service Reference Provider生成的代理),称为:

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;
        }
    }