简体   繁体   English

WCF 自定义标题/自定义行为类不可用

[英]WCF Custom Headers/ Custom Behaviors class not available

I'm fairly new at WCF so i'm positive this is something i'm doing wrong.我是 WCF 的新手,所以我很确定这是我做错的事情。 I was following this link Sample link我正在关注此链接示例链接

Basically i'm trying to implement 'IClientMessageInspector' so that every call to my WCF must contain custom attributes that I need for my WCF methods to perform the correct activities.基本上,我正在尝试实现“IClientMessageInspector”,以便对 WCF 的每次调用都必须包含我的 WCF 方法执行正确活动所需的自定义属性。

In my Console Consumer app I'm trying to add my custom endpoint behavior class.在我的 Console Consumer 应用程序中,我试图添加我的自定义端点行为类。 However when I go to add the class it's not available.但是,当我添加课程时,它不可用。 Here is my consumer app you'll notice the line of code i'm having the issue with.这是我的消费者应用程序,您会注意到我遇到问题的代码行。

Program.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(
            }
        }
    }

My Service Service1.svc.cs我的服务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; }
        }
    }
}

ContextClass.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
    }
}

And finally here is my services web.config最后这里是我的服务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>

The message behaviors won't be available via WSDL in generated proxy in your consumer app.消息行为将无法通过WSDL在您的消费者应用程序中生成的代理中使用。 So either you have to add the same code(classes) in client side for message inspector or create a shared project (class library) for contracts between service and client through which the behavior class will be available in both.因此,您要么必须在客户端为消息检查器添加相同的代码(类),要么为服务和客户端之间的合同创建一个shared project (类库),通过该项目,行为类将在两者中都可用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM