gpt4 book ai didi

c# - WCF 自定义 header /自定义行为类不可用

转载 作者:太空宇宙 更新时间:2023-11-03 12:46:25 25 4
gpt4 key购买 nike

我是 WCF 的新手,所以我很肯定这是我做错的事情。我正在关注此链接 Sample link

基本上,我正在尝试实现“IClientMessageInspector”,以便对我的 WCF 的每次调用都必须包含我的 WCF 方法执行正确事件所需的自定义属性。

在我的 Console Consumer 应用程序中,我尝试添加我的自定义端点行为类。但是,当我去添加类(class)时,它不可用。这是我的消费者应用程序,您会注意到我遇到问题的代码行。

程序.cs

class Program
{
static void Main(string[] args)
{
using (Service1Client s1c = new Service1Client())
{
//cannot add 'CustomBehavior' this is my issue
s1c.ChannelFactory.Endpoint.EndpointBehaviors.Add(
}
}
}

我的服务Service1.svc.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;

namespace WCFHeaderCalls
{
// NOTE: You can use the "Rename" command on the "Refactor" menu to change the class name "Service1" in code, svc and config file together.
// NOTE: In order to launch WCF Test Client for testing this service, please select Service1.svc or Service1.svc.cs at the Solution Explorer and start debugging.
[CustomBehavior]
public class Service1 : IService1
{
public string GetData(int value)
{
return string.Format("You entered: {0}", value);
}

public CompositeType GetDataUsingDataContract(CompositeType composite)
{
if (composite == null)
{
throw new ArgumentNullException("composite");
}
if (composite.BoolValue)
{
composite.StringValue += "Suffix";
}
return composite;
}
}
}

IService1.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;

namespace WCFHeaderCalls
{
// NOTE: You can use the "Rename" command on the "Refactor" menu to change the interface name "IService1" in both code and config file together.
[ServiceContract]
public interface IService1
{

[OperationContract]
string GetData(int value);

[OperationContract]
CompositeType GetDataUsingDataContract(CompositeType composite);

// TODO: Add your service operations here
}


// Use a data contract as illustrated in the sample below to add composite types to service operations.
[DataContract]
public class CompositeType
{
bool boolValue = true;
string stringValue = "Hello ";

[DataMember]
public bool BoolValue
{
get { return boolValue; }
set { boolValue = value; }
}

[DataMember]
public string StringValue
{
get { return stringValue; }
set { stringValue = value; }
}
}
}

上下文类.cs

using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Dispatcher;
using System.Runtime.Serialization;
using System.Xml;
using System.Xml.Serialization;
using System.IO;

namespace WCFHeaderCalls
{
/// <summary>
/// This class will act as a custom context in the client side to hold the context information.
/// </summary>
public class ClientContext
{
public static string applicationKey;
public static string methodKey;
public static string accountName;
public static string accountPassword;
}

/// <summary>
/// This class will act as a custom context, an extension to the OperationContext.
/// This class holds all context information for our application.
/// </summary>
public class ServerContext : IExtension<OperationContext>
{
public string EmployeeID;
public string WindowsLogonID;
public string KerberosID;
public string SiteminderToken;

// Get the current one from the extensions that are added to OperationContext.
public static ServerContext Current
{
get
{
return OperationContext.Current.Extensions.Find<ServerContext>();
}
}

#region IExtension<OperationContext> Members
public void Attach(OperationContext owner)
{
}

public void Detach(OperationContext owner)
{
}
#endregion
}


}

MessageInspector.cs

using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Dispatcher;
using System.Runtime.Serialization;
using System.Xml;
using System.Xml.Serialization;
using System.IO;

namespace WCFHeaderCalls
{
/// <summary>
/// This class is used to inspect the message and headers on the server side,
/// This class is also used to intercept the message on the
/// client side, before/after any request is made to the server.
/// </summary>
public class CustomMessageInspector : IClientMessageInspector, IDispatchMessageInspector
{
#region Message Inspector of the Service

/// <summary>
/// This method is called on the server when a request is received from the client.
/// </summary>
/// <param name="request"></param>
/// <param name="channel"></param>
/// <param name="instanceContext"></param>
/// <returns></returns>
public object AfterReceiveRequest(ref Message request,
IClientChannel channel, InstanceContext instanceContext)
{
// Create a copy of the original message so that we can mess with it.
MessageBuffer buffer = request.CreateBufferedCopy(Int32.MaxValue);
request = buffer.CreateMessage();
Message messageCopy = buffer.CreateMessage();

// Read the custom context data from the headers
ServiceHeader customData = CustomHeader.ReadHeader(request);

// Add an extension to the current operation context so
// that our custom context can be easily accessed anywhere.
ServerContext customContext = new ServerContext();

if (customData != null)
{
customContext.KerberosID = customData.accountName;
customContext.SiteminderToken = customData.accountPassword;
}
OperationContext.Current.IncomingMessageProperties.Add(
"CurrentContext", customContext);
return null;
}

/// <summary>
/// This method is called after processing a method on the server side and just
/// before sending the response to the client.
/// </summary>
/// <param name="reply"></param>
/// <param name="correlationState"></param>
public void BeforeSendReply(ref Message reply, object correlationState)
{
// Do some cleanup
OperationContext.Current.Extensions.Remove(ServerContext.Current);
}

#endregion

#region Message Inspector of the Consumer

/// <summary>
/// This method will be called from the client side just before any method is called.
/// </summary>
/// <param name="request"></param>
/// <param name="channel"></param>
/// <returns></returns>
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
// Prepare the request message copy to be modified
MessageBuffer buffer = request.CreateBufferedCopy(Int32.MaxValue);
request = buffer.CreateMessage();

ServiceHeader customData = new ServiceHeader();

customData.accountName = ClientContext.accountName;
customData.accountPassword = ClientContext.accountPassword;

CustomHeader header = new CustomHeader(customData);

// Add the custom header to the request.
request.Headers.Add(header);

return null;
}

/// <summary>
/// This method will be called after completion of a request to the server.
/// </summary>
/// <param name="reply"></param>
/// <param name="correlationState"></param>
public void AfterReceiveReply(ref Message reply, object correlationState)
{

}

#endregion
}
}

CustomHeader.cs

using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.Runtime.Serialization;
using System.Xml;
using System.Xml.Serialization;
using System.IO;

namespace WCFHeaderCalls
{
[DataContract]
public class ServiceHeader
{
/// <summary>
/// this is the application-GUID specify this per app (OI,office,etc..)
/// </summary>
[DataMember]
public string applicationKey { get; set; }

/// <summary>
/// this is the method key you wish to implament
/// </summary>
[DataMember]
public string methodKey { get; set; }

/// <summary>
/// this is your account name
/// </summary>
[DataMember]
public string accountName { get; set; }

/// <summary>
/// this is your account password
/// </summary>
[DataMember]
public string accountPassword { get; set; }
}

public class CustomHeader : MessageHeader
{
private const string CUSTOM_HEADER_NAME = "HeaderName";
private const string CUSTOM_HEADER_NAMESPACE = "YourNameSpace";

private ServiceHeader _customData;

public ServiceHeader CustomData
{
get
{
return _customData;
}
}

public CustomHeader()
{
}

public CustomHeader(ServiceHeader customData)
{
_customData = customData;
}

public override string Name
{
get { return (CUSTOM_HEADER_NAME); }
}

public override string Namespace
{
get { return (CUSTOM_HEADER_NAMESPACE); }
}

protected override void OnWriteHeaderContents(
System.Xml.XmlDictionaryWriter writer, MessageVersion messageVersion)
{
XmlSerializer serializer = new XmlSerializer(typeof(ServiceHeader));
StringWriter textWriter = new StringWriter();
serializer.Serialize(textWriter, _customData);
textWriter.Close();

string text = textWriter.ToString();

writer.WriteElementString(CUSTOM_HEADER_NAME, "Key", text.Trim());
}

public static ServiceHeader ReadHeader(Message request)
{
Int32 headerPosition = request.Headers.FindHeader(CUSTOM_HEADER_NAME, CUSTOM_HEADER_NAMESPACE);
if (headerPosition == -1)
return null;

MessageHeaderInfo headerInfo = request.Headers[headerPosition];

XmlNode[] content = request.Headers.GetHeader<XmlNode[]>(headerPosition);

string text = content[0].InnerText;

XmlSerializer deserializer = new XmlSerializer(typeof(ServiceHeader));
TextReader textReader = new StringReader(text);
ServiceHeader customData = (ServiceHeader)deserializer.Deserialize(textReader);
textReader.Close();

return customData;
}
}
}

CustomBehavior.cs

using System;
using System.Collections.ObjectModel;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using System.Runtime.Serialization;
using System.Xml;
using System.Xml.Serialization;
using System.IO;

namespace WCFHeaderCalls
{
/// <summary>
/// This custom behavior class is used to add both client and server inspectors to
/// the corresponding WCF end points.
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
public class CustomBehavior : Attribute, IServiceBehavior, IEndpointBehavior
{
#region IEndpointBehavior Members

void IEndpointBehavior.AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
{
}

void IEndpointBehavior.ApplyClientBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime)
{
CustomMessageInspector inspector = new CustomMessageInspector();
clientRuntime.MessageInspectors.Add(inspector);
}

void IEndpointBehavior.ApplyDispatchBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher)
{
ChannelDispatcher channelDispatcher = endpointDispatcher.ChannelDispatcher;
if (channelDispatcher != null)
{
foreach (EndpointDispatcher ed in channelDispatcher.Endpoints)
{
CustomMessageInspector inspector = new CustomMessageInspector();
ed.DispatchRuntime.MessageInspectors.Add(inspector);
}
}
}

void IEndpointBehavior.Validate(ServiceEndpoint endpoint) { }

#endregion

#region IServiceBehavior Members

void IServiceBehavior.AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
{
}

void IServiceBehavior.ApplyDispatchBehavior(ServiceDescription desc, ServiceHostBase host)
{
foreach (ChannelDispatcher cDispatcher in host.ChannelDispatchers)
foreach (EndpointDispatcher eDispatcher in cDispatcher.Endpoints)
eDispatcher.DispatchRuntime.MessageInspectors.Add(new CustomMessageInspector());
}

void IServiceBehavior.Validate(ServiceDescription desc, ServiceHostBase host) { }

#endregion
}
}

最后这是我的服务 web.config

<?xml version="1.0"?>
<configuration>

<appSettings>
<add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" />
</appSettings>
<system.web>
<compilation debug="true" targetFramework="4.5" />
<httpRuntime targetFramework="4.5"/>
</system.web>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior>
<!-- To avoid disclosing metadata information, set the values below to false before deployment -->
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
<!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
</serviceBehaviors>
</behaviors>
<protocolMapping>
<add binding="basicHttpsBinding" scheme="https" />
</protocolMapping>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
<!--
To browse web app root directory during debugging, set the value below to true.
Set to false before deployment to avoid disclosing web app folder information.
-->
<directoryBrowse enabled="true"/>
</system.webServer>

</configuration>

最佳答案

在您的消费者应用中生成的代理中,消息行为将无法通过 WSDL 获得。因此,您要么必须在客户端为消息检查器添加相同的代码(类),要么为服务和客户端之间的契约(Contract)创建一个共享项目(类库),通过它行为类在两者中都可用.

关于c# - WCF 自定义 header /自定义行为类不可用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37169550/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com