WCF,从请求中检索信息,找不到时间戳 [英] WCF, information retrieval from a request, timestamp cant be found

查看:95
本文介绍了WCF,从请求中检索信息,找不到时间戳的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我确实有运行良好的WCF静态服务.

I do have a WCF restful service that is running perfectly.

每当有人向我的服务发送GETPOST请求时,我确实希望获取尽可能多的信息

I do want to retrieve as much information as possible whenever someone sends a GET or POST request to my service

我正在使用以下内容来检索我的大部分信息:

I am using the following to retrieve most of my information:

OperationContext context = OperationContext.Current;
MessageProperties messageProperties = context.IncomingMessageProperties;

RemoteEndpointMessageProperty endpointProperty =
          messageProperties[RemoteEndpointMessageProperty.Name] as RemoteEndpointMessageProperty;

在查找请求的时间戳时我确实需要帮助.

I do need help in finding the timestamp of the request.

谢谢

对于某些人来说,这个问题引起了不相关的问题,因此部分问题被删除了.

for some people the question making unrelated questions, hence portion of the question was removed.

推荐答案

我最近在阅读

I found myself in a similar position recently and after reading the great answer from @carlosfigueira, I decided to dig a little deeper and figure out how to make his example work from an IIS web.config file.

事实证明,卡洛斯(Carlos)在Microsoft拥有自己的博客,并且有几篇文章详细介绍了该主题. 此页面顶部,非常有用,并明确指出 MessageEncoder 是通过IIS托管服务时,我最直接从网上直接读取客户端消息的方法.

It turns out that Carlos has his own blog at Microsoft and has a few posts that cover this subject in great detail. The diagram at the top of this page, was very helpful and clearly indicated that the MessageEncoder was the closest I was going to get to reading the client's message directly off the wire when the service is hosted through IIS.

这基本上是使用与Carlos详细描述的相同过程来完成的,但是要在web.config文件中完成它,您必须创建一个从

This is essentially done using the same process Carlos detailed, but to get it done in a web.config file you have to create a class derived from BindingElementExtensionElement to give IIS the required hooks to create your BindingElement/Encoder/Factory as shown in the examples below.

((我讨厌重复卡洛斯已经发布的内容,但是我在弄清所有这些内容的过程中做了一些微调.因此,不要偶然发布与他的示例不兼容的代码,我将在这里重新发布我的调整)


步骤1)创建您的
自定义MessageEncoder (并关联消息原始/包装好的MessageEncoder.


Step 1) Create your custom MessageEncoder (and correlating MessageEncoderFactory) that wraps the original MessageEncoder that your binding would normally use and make the wrapping MessageEncoder add the timestamp to the Message created by the original/wrapped MessageEncoder.

注意::这是三个类中的前两个,它们实际上将包装对象并通过将调用传递给包装的/内部对象(即" return inner")来实现其WCF接口.属性/方法)允许自定义MessageEncoder精确匹配我们实际上试图扩展/更改的MessageEncoder的行为.因此,尽管这是一个冗长的答案,但是一旦您掌握了所有涉及的部分,实际上并没有那么复杂.

NOTE: This is the first two of three classes that will essentially wrap objects and implement their WCF interfaces by passing calls through to the wrapped/inner object (ie. "return inner.property/method") allowing the custom MessageEncoder to precisely match the behavior of the MessageEncoder we're essentially trying to extend/alter. So, although this is a lengthy answer, it's really not that complicated once you have a grasp of all the pieces involved.

using System;
using System.IO;
using System.ServiceModel.Channels;

namespace WCF.Sample
{
    public class TimestampedMsgEncoder : MessageEncoder
    {
        public const String TimestampProp = "MsgReceivedTimestamp";

        private MessageEncoder inner;

        public TimestampedMsgEncoder(MessageEncoder inner) { this.inner = inner; }

        public override Message ReadMessage(ArraySegment<Byte> buffer, BufferManager bufferManager, String contentType)
        {
            DateTime MsgReceived = DateTime.Now;
            Message Msg = inner.ReadMessage(buffer, bufferManager, contentType);
            Msg.Properties.Add(TimestampProp, MsgReceived);
            return Msg;
        }

        public override Message ReadMessage(Stream stream, Int32 maxSizeOfHeaders, String contentType)
        {
            DateTime MsgReceived = DateTime.Now;
            Message Msg = inner.ReadMessage(stream, maxSizeOfHeaders, contentType);
            Msg.Properties.Add(TimestampProp, MsgReceived);
            return Msg;
        }

        #region Pass-through MessageEncoder implementations

        public override String ContentType { get { return inner.ContentType; } }

        public override String MediaType { get { return inner.MediaType; } }

        public override MessageVersion MessageVersion { get { return inner.MessageVersion; } }

        public override Boolean IsContentTypeSupported(String contentType) { return inner.IsContentTypeSupported(contentType); }

        public override void WriteMessage(Message message, Stream stream) { inner.WriteMessage(message, stream); }

        public override ArraySegment<Byte> WriteMessage(Message message, Int32 maxMessageSize, BufferManager bufferManager, Int32 messageOffset) { return inner.WriteMessage(message, maxMessageSize, bufferManager, messageOffset); }

        #endregion Pass-through MessageEncoder implementations
    }

    public class TimestampedMsgEncoderFactory : MessageEncoderFactory
    {
        protected readonly MessageEncoderFactory inner;

        protected TimestampedMsgEncoderFactory() { }

        public TimestampedMsgEncoderFactory(MessageEncoderFactory inner)
        {
            this.inner = inner;
        }

        public override MessageEncoder Encoder { get { return new TimestampedMsgEncoder(inner.Encoder); } }

        public override MessageVersion MessageVersion { get { return inner.MessageVersion; } }
    }
}


步骤2)创建一个从 MessageEncodingBindingElement 派生的类,将会添加到您的 CustomBinding 中,并且(再次)包装一个内部"对象,该对象是您通常的绑定将使用的对象的类型(对于 BasicHttpBinding ).


Step 2) Create a class derived from MessageEncodingBindingElement that will be added to your CustomBinding and will (once again) wrap an "inner" object that is the type of object your usual binding would use (TextMessageEncodingBindingElement in the case of the BasicHttpBinding).

using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Configuration;
using System.Configuration;

namespace WCF.Sample
{
    public class TimestampedTextMsgEncodingBindingElement : MessageEncodingBindingElement, IWsdlExportExtension, IPolicyExportExtension
    {
        private readonly TextMessageEncodingBindingElement inner;

        public TimestampedTextMsgEncodingBindingElement(TextMessageEncodingBindingElement inner)
        {
            this.inner = inner;
        }

        public TimestampedTextMsgEncodingBindingElement()
        {
            inner = new TextMessageEncodingBindingElement();
        }

        public TimestampedTextMsgEncodingBindingElement(MessageVersion messageVersion, Encoding writeEnconding)
        {
            inner = new TextMessageEncodingBindingElement(messageVersion, writeEnconding);
        }

        public override MessageEncoderFactory CreateMessageEncoderFactory()
        {
            return new TimestampedMsgEncoderFactory(inner.CreateMessageEncoderFactory());
        }

        #region Pass-through MessageEncoderBindingElement implementations

        public override BindingElement Clone()
        {
            return new TimestampedTextMsgEncodingBindingElement((TextMessageEncodingBindingElement)inner.Clone());
        }

        public override MessageVersion MessageVersion { get { return inner.MessageVersion; } set { inner.MessageVersion = value; } }
        public Encoding WriteEncoding { get { return inner.WriteEncoding; } set { inner.WriteEncoding = value; } }
        public Int32 MaxReadPoolSize { get { return inner.MaxReadPoolSize; } set { inner.MaxReadPoolSize = value; } }
        public Int32 MaxWritePoolSize { get { return inner.MaxWritePoolSize; } set { inner.MaxWritePoolSize = value; } }
        public XmlDictionaryReaderQuotas ReaderQuotas { get { return inner.ReaderQuotas; } set { inner.ReaderQuotas = value; } }

        public override Boolean CanBuildChannelListener<TChannel>(BindingContext context)
        {
            return context.CanBuildInnerChannelListener<TChannel>();
            //return inner.CanBuildChannelFactory<TChannel>(context);
        }

        public override IChannelListener<TChannel> BuildChannelListener<TChannel>(BindingContext context)
        {
            context.BindingParameters.Add(this);
            return context.BuildInnerChannelListener<TChannel>();
            //return inner.BuildChannelListener<TChannel>(context);         
        }

        public void ExportContract(WsdlExporter exporter, WsdlContractConversionContext context)
        {
            ((IWsdlExportExtension)inner).ExportContract(exporter, context);
        }

        public void ExportEndpoint(WsdlExporter exporter, WsdlEndpointConversionContext context)
        {
            ((IWsdlExportExtension)inner).ExportEndpoint(exporter, context);
        }

        public void ExportPolicy(MetadataExporter exporter, PolicyConversionContext context)
        {
            ((IPolicyExportExtension)inner).ExportPolicy(exporter, context);
        }

        #endregion Pass-through MessageEncoderBindingElement implementations
    }
}


步骤3)创建一个从 BindingElementExtensionElement 派生的类将允许您使用我们刚刚在web.config文件中创建(在上方)的 TimestampedTextMsgEncodingBindingElement 类.


Step 3) Create a class derived from BindingElementExtensionElement that will allow you to use the TimestampedTextMsgEncodingBindingElement class we just created (above) in the web.config file.

这是我们正在创建的第一个(唯一的)类,其目的不是包装一些标准框架类以有效地扩展/更改它.不过,这是一个非常简单的实现类.最小实现仅具有 BindingElementType 属性以及 CreateBindingElement 方法.

This is the first (only) class we're creating that has a purpose other than wrapping some standard framework class to effectively extend/alter it. Still, it's a pretty simple class to implement. A minimal implementation only has the BindingElementType property and the CreateBindingElement method.

但是,如果要使用属性从web.config文件中自定义MessageEncoder行为,则它确实需要具有特殊属性,并用

However, if you will want to use attributes to customize the MessageEncoder behavior from the web.config file then it does need to have special properties decorated with ConfigurationProperty attributes to let IIS know that they accept values from element attributes in the web.config file ... which must then be marshaled to the inner MessageEncoder accordingly.

(如果您知道消息编码器将始终使用相同的配置,那么仅对诸如UTF8和SOAP11之类的硬代码进行编码可能会更容易-我只是在此处提供了一种实现属性的方法作为示例)

using System;
using System.Xml;
using System.Text;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Configuration;
using System.Configuration;

namespace WCF.Sample
{
    public class TimestampedTextMsgEncodingExtension : BindingElementExtensionElement
    {
        private MessageVersion _MessageVersion = MessageVersion.Soap11;
        private Encoding _Encoding = Encoding.UTF8;
        private Int32 _MaxReadPoolSize = -1;
        private Int32 _MaxWritePoolSize = -1;
        private XmlDictionaryReaderQuotas _ReaderQuotas = null;

        public override Type BindingElementType { get { return typeof(TimestampedTextMsgEncodingBindingElement); } }

        protected override BindingElement CreateBindingElement()
        {
            TimestampedTextMsgEncodingBindingElement eb = new TimestampedTextMsgEncodingBindingElement(_MessageVersion, _Encoding);
            if (_MaxReadPoolSize > -1) eb.MaxReadPoolSize = _MaxReadPoolSize;
            if (_MaxWritePoolSize > -1) eb.MaxWritePoolSize = MaxWritePoolSize;
            if (_ReaderQuotas != null) eb.ReaderQuotas = _ReaderQuotas;

            return eb;
        }

        [ConfigurationProperty("messageVersion", DefaultValue = "Soap11", IsRequired = false)]
        public String messageVersion
        {
            set
            {
                switch (value)
                {
                    case "Soap11":
                        _MessageVersion = MessageVersion.Soap11;
                        break;

                    case "Soap12":
                        _MessageVersion = MessageVersion.Soap12;
                        break;

                    case "Soap11WSAddressing10":
                        _MessageVersion = MessageVersion.Soap11WSAddressing10;
                        break;

                    case "Soap12WSAddressing10":
                        _MessageVersion = MessageVersion.Soap12WSAddressing10;
                        break;

                    case "Soap11WSAddressingAugust2004":
                        _MessageVersion = MessageVersion.Soap11WSAddressingAugust2004;
                        break;

                    case "Soap12WSAddressingAugust2004":
                        _MessageVersion = MessageVersion.Soap12WSAddressingAugust2004;
                        break;

                    default:
                        throw new NotSupportedException("\"" + value + "\" is not a supported message version.");
                }
            }
        }

        [ConfigurationProperty("writeEncoding", DefaultValue = "Utf8TextEncoding", IsRequired = false)]
        public String writeEncoding
        {
            set
            {
                switch (value)
                {
                    case "Utf8TextEncoding":
                        _Encoding = Encoding.UTF8;
                        break;

                    case "Utf16TextEncoding":
                        _Encoding = Encoding.Unicode;
                        break;

                    case "UnicodeFffeTextEncoding":
                        _Encoding = Encoding.BigEndianUnicode;
                        break;

                    default:
                        _Encoding = Encoding.GetEncoding(value);
                        break;
                }
            }
        }

        [ConfigurationProperty("maxReadPoolSize", IsRequired = false)]
        public Int32 MaxReadPoolSize { get { return _MaxReadPoolSize; } set { _MaxReadPoolSize = value; } }

        [ConfigurationProperty("maxWritePoolSize", IsRequired = false)]
        public Int32 MaxWritePoolSize { get { return _MaxWritePoolSize; } set { _MaxWritePoolSize = value; } }

        [ConfigurationProperty("readerQuotas", IsRequired = false)]
        public XmlDictionaryReaderQuotas ReaderQuotas { get { return _ReaderQuotas; } set { _ReaderQuotas = value; } }
    }
}


步骤4)更新您的web.config文件:


Step 4) Update your web.config file:

  • 添加/更新配置\ system.serviceModel \ extensions \ bindingElementExtensions 您的web.config部分中包含对我们在上面的代码中创建的 TimestampedTextMsgEncodingExtension 的完全限定的引用
  • 创建/更新具有绑定的" customBinding 的终结点指向bindingConfiguration( HttpNoAuthTimestampEncoding )
  • 在bindings \ customBinding下添加/更新bindingConfiguration部分( HttpNoAuthTimestampEncoding ),以使用具有我们为自定义MessageEncoder扩展名( timestampedTextMsgEncoding )分配的名称的元素. bindingElementExtensions"部分,以指示流量应流经该编码器.
  • Add/Update the configuration\system.serviceModel\extensions\bindingElementExtensions section of your web.config to include a fully qualified reference to the TimestampedTextMsgEncodingExtension we just created in the code above
  • Create/update an endpoint with a binding of "customBinding" that points to a bindingConfiguration (HttpNoAuthTimestampEncoding)
  • Add/update the bindingConfiguration section (HttpNoAuthTimestampEncoding) under bindings\customBinding to use an element with the name we assigned to our custom MessageEncoder extension (timestampedTextMsgEncoding) in the "bindingElementExtensions" section to indicate that traffic should flow through that encoder.
<configuration>
  <system.web>
    <compilation strict="false" explicit="true" targetFramework="4.5" />
    <trust level="Full" />
  </system.web>
  <system.webServer>
    <modules runAllManagedModulesForAllRequests="true" />
  </system.webServer>
  <system.serviceModel>
    <extensions>
      <bindingElementExtensions>
        <add name="timestampedTextMsgEncoding" type="WCF.Sample.TimestampedTextMsgEncodingExtension, WCFSample" />
      </bindingElementExtensions>
    </extensions>
    <services>
      <service behaviorConfiguration="WCF.Sample.SampleBehavior" name="WCF.Sample.Service">
        <endpoint address="basic" contract="WCF.Sample.IService" binding="basicHttpBinding" bindingConfiguration="HttpNoAuth" />
        <endpoint address="timestamp" contract="WCF.Sample.IService" binding="customBinding" bindingConfiguration="HttpNoAuthTimestampEncoding" />
      </service>
    </services>
    <bindings>
      <basicHttpBinding>
        <binding name="HttpNoAuth" >
          <security mode="None" >
            <transport clientCredentialType="None" />
          </security>
        </binding>
      </basicHttpBinding>
      <customBinding>
        <binding name="HttpNoAuthTimestampEncoding">
          <timestampedTextMsgEncoding writeEncoding="Utf8TextEncoding" messageVersion="Soap11" />
          <httpTransport authenticationScheme="None" />
        </binding>
      </customBinding>
    </bindings>
    <behaviors>
      <serviceBehaviors>
        <behavior name="WCF.Sample.SampleBehavior">
          <useRequestHeadersForMetadataAddress />
          <serviceMetadata httpGetEnabled="True" />
          <serviceDebug includeExceptionDetailInFaults="False" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>
</configuration>


步骤5)使用代码更新您的WCF服务方法,以提取我们添加到传入消息中的timestamp属性.由于我们是在将客户端的消息发送到内部MessageEncoder之前记录了DateTime的,因此,这实质上应该是自POST消息的最后一位从客户端传输以来,我们最早必须获得DateTime的机会.


Step 5) Update your WCF service method(s) with code to pull the timestamp property we added to the incoming Message. Since we recorded the DateTime before we sent the client's message to the inner MessageEncoder, this should be essentially the earliest opportunity we've had to get a DateTime since the last bit of the POST message was transferred from the client.

DateTime SOAPMsgReceived = (DateTime)System.ServiceModel.OperationContext.Current.IncomingMessageProperties[TimestampedMsgEncoder.TimestampProp];


最后两点.


Two final notes.

  1. 如果您创建了一个新的端点,请确保在进行测试时将客户指向该端点的地址(我追了一下它的尾巴约一个小时,然后才意识到我忽略了这个细节).

  1. If you created a new endpoint, make sure your clients are pointed to the address of that endpoint when you go to test (I chased my tail on this for about an hour before I realized I'd overlooked that detail).

上面的web.config示例几乎没有显示出我难以确定如何使customBinding配置( HttpNoAuthTimestampEncoding )与basicHttpBinding配置( HttpNoAuth)的功能匹配的困难. ). customBinding 元素上的文档有所帮助,但主要是很多网络搜索以及反复试验的结果.需要特别注意的是使用<httpTransport><httpsTransport>在HTTP和HTTPS之间切换.相比之下,basicHttpBinding使用"security \ @ mode = None"和"security \ @ mode = Transport".

The web.config sample above does little to show the difficulty I had figuring out how to make the customBinding configuration (HttpNoAuthTimestampEncoding) match the functionality of the basicHttpBinding configuration (HttpNoAuth). The documentation on the customBinding element helped a little, but mostly it was a lot of web searches and trial and error. Of particular note is using <httpTransport> and <httpsTransport> to flip between HTTP and HTTPS. In contrast, basicHttpBinding uses "security\@mode=None" and "security\@mode=Transport".

这篇关于WCF,从请求中检索信息,找不到时间戳的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆