WCF,从请求中检索信息,找不到时间戳

时间:2016-08-22 15:06:38

标签: c# rest wcf http

我确实有一个完美运行的WCF restful服务。

我确实想在有人向我的服务发送<body> <script id="__bs_script__"> //<![CDATA[ document.write("<script async src='/browser-sync/browser-sync-client.2.14.0.js'><\/script>".replace("HOST", location.hostname)); //]]> </script> <script async="" src="/browser-sync/browser-sync-client.2.14.0.js"></script> <pomodoro-app> <nav class="navbar navbar-default navbar-static-top"> <div class="container"> <div class="navbar-header"> <strong class="navbar-brand">My Pomodoro App</strong> </div> <ul class="nav navbar-nav navbar-right"> <li><a></a></li></ul> </div> </nav> </pomodoro-app> </body> GET请求时检索尽可能多的信息

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

POST

我确实需要帮助来查找请求的时间戳。

谢谢

编辑:对某些人提出了无关问题的问题,因此部分问题被删除了。

2 个答案:

答案 0 :(得分:3)

这取决于您的请求时间戳的含义。如果您想知道请求离开客户端的时间,那么您就没有这些信息(即使在具有HttpContext.Timestamp属性的ASP.NET上,也不可用) - 服务器只知道何时它收到请求(and this is what HttpContext.Timestamp gives you, based on the documentation)。

对于WCF本身,没有任何属性可以告诉操作上下文何时创建;您可以在WCF堆栈的任何层(自定义编码器,自定义消息检查器,自定义协议通道等)中自己添加此类属性,甚至可以在操作本身中添加 - 并且对于大多数情况,它们之间的区别是微不足道的。

例如,下面的代码显示了在三个地方对请求加时间戳的示例;在大多数测试中,我运行它们之间的差异最多只需几毫秒。

public class StackOverflow_39082986
{
    const string TimestampPropertyName = "MyTimestampProperty";
    class MyTimestampProperty
    {
        public DateTime EncoderTimestamp;
        public DateTime InspectorTimestamp;
    }
    [ServiceContract]
    public interface ITest
    {
        [OperationContract]
        void DoSomething();
    }
    public class Service : ITest
    {
        public void DoSomething()
        {
            var myProp = (MyTimestampProperty)OperationContext.Current.IncomingMessageProperties[TimestampPropertyName];
            var now = DateTime.UtcNow;
            Console.WriteLine("Request timestamps:");
            var timeFormat = "yyyy-MM-dd HH:MM:ss.fffffff";
            Console.WriteLine("  From encoder  : {0}", myProp.EncoderTimestamp.ToString(timeFormat));
            Console.WriteLine("  From inspector: {0}", myProp.InspectorTimestamp.ToString(timeFormat));
            Console.WriteLine("  From operation: {0}", now.ToString(timeFormat));
        }
    }
    class MyInspector : IEndpointBehavior, IDispatchMessageInspector
    {
        public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
        {
        }

        public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
        {
            MyTimestampProperty prop;
            if (request.Properties.ContainsKey(TimestampPropertyName))
            {
                prop = (MyTimestampProperty)request.Properties[TimestampPropertyName];
            }
            else
            {
                prop = new MyTimestampProperty();
                request.Properties.Add(TimestampPropertyName, prop);
            }

            prop.InspectorTimestamp = DateTime.UtcNow;
            return null;
        }

        public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
        {
        }

        public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
        {
            endpointDispatcher.DispatchRuntime.MessageInspectors.Add(this);
        }

        public void BeforeSendReply(ref Message reply, object correlationState)
        {
        }

        public void Validate(ServiceEndpoint endpoint)
        {
        }
    }
    public class MyEncoderBindingElement : MessageEncodingBindingElement
    {
        private MessageEncodingBindingElement inner;
        public MyEncoderBindingElement(MessageEncodingBindingElement inner)
        {
            this.inner = inner;
        }
        public override MessageVersion MessageVersion
        {
            get { return this.inner.MessageVersion; }
            set { this.inner.MessageVersion = value; }
        }

        public override BindingElement Clone()
        {
            return new MyEncoderBindingElement((MessageEncodingBindingElement)this.inner.Clone());
        }

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

        public override bool CanBuildChannelListener<TChannel>(BindingContext context)
        {
            return context.CanBuildInnerChannelListener<TChannel>();
        }

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

        class MyEncoderFactory : MessageEncoderFactory
        {
            MessageEncoderFactory inner;
            public MyEncoderFactory(MessageEncoderFactory inner)
            {
                this.inner = inner;
            }

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

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

        class MyEncoder : MessageEncoder
        {
            MessageEncoder inner;
            public MyEncoder(MessageEncoder inner)
            {
                this.inner = inner;
            }

            public override string ContentType
            {
                get { return this.inner.ContentType; }
            }

            public override string MediaType
            {
                get { return this.inner.MediaType; }
            }

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

            public override bool IsContentTypeSupported(string contentType)
            {
                return this.inner.IsContentTypeSupported(contentType);
            }

            public override Message ReadMessage(ArraySegment<byte> buffer, BufferManager bufferManager, string contentType)
            {
                var result = this.inner.ReadMessage(buffer, bufferManager, contentType);
                result.Properties.Add(TimestampPropertyName, new MyTimestampProperty { EncoderTimestamp = DateTime.UtcNow });
                return result;
            }

            public override Message ReadMessage(Stream stream, int maxSizeOfHeaders, string contentType)
            {
                var result = this.inner.ReadMessage(stream, maxSizeOfHeaders, contentType);
                result.Properties.Add(TimestampPropertyName, new MyTimestampProperty { EncoderTimestamp = DateTime.UtcNow });
                return result;
            }

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

            public override ArraySegment<byte> WriteMessage(Message message, int maxMessageSize, BufferManager bufferManager, int messageOffset)
            {
                return this.inner.WriteMessage(message, maxMessageSize, bufferManager, messageOffset);
            }
        }
    }
    static Binding GetBinding(bool server)
    {
        var result = new CustomBinding(new BasicHttpBinding());
        if (server)
        {
            for (var i = 0; i < result.Elements.Count; i++)
            {
                var mebe = result.Elements[i] as MessageEncodingBindingElement;
                if (mebe != null)
                {
                    result.Elements[i] = new MyEncoderBindingElement(mebe);
                    break;
                }
            }
        }

        return result;
    }
    public static void Test()
    {
        string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
        ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddress));
        ServiceEndpoint endpoint = host.AddServiceEndpoint(typeof(ITest), GetBinding(true), "");
        endpoint.EndpointBehaviors.Add(new MyInspector());
        host.Open();
        Console.WriteLine("Host opened");

        ChannelFactory<ITest> factory = new ChannelFactory<ITest>(GetBinding(false), new EndpointAddress(baseAddress));
        ITest proxy = factory.CreateChannel();
        proxy.DoSomething();

        ((IClientChannel)proxy).Close();
        factory.Close();

        Console.Write("Press ENTER to close the host");
        Console.ReadLine();
        host.Close();
    }
}

答案 1 :(得分:1)

我最近发现自己处于类似的位置,在阅读了@carlosfigueira的精彩答案后,我决定深入挖掘并找出如何使用IIS web.config文件使其示例工作。

事实证明,Carlos在微软有他自己的博客,并且有一些帖子非常详细地介绍了这个主题。 this page顶部的图表非常有用,并清楚地表明MessageEncoder是最接近我的服务时直接通过电话阅读客户端消息的信息。通过IIS托管。

这基本上是使用Carlos详细介绍的相同过程完成的,但要在web.config文件中完成,您必须创建一个派生自BindingElementExtensionElement的类,以便为IIS创建所需的钩子来创建BindingElement / Encoder / Factory,如下例所示。

(我不想复制Carlos已经发布的内容,但我在确定这一切的过程中确实调整了一些代码。所以,而不是发布不能与他合作的代码。例如,我只是在这里重新发布我的调整


步骤1)创建包装您的绑定通常使用的原始MessageEncoder的custom MessageEncoder(并关联MessageEncoderFactory)并使包装MessageEncoder将时间戳添加到原始/创建的Message包装MessageEncoder。

注意:这是三个类中的前两个,它们通过将调用传递给包装/内部对象来实际包装对象并实现其WCF接口(即。&#34; return inner.property/method")允许自定义MessageEncoder精确匹配MessageEncoder的行为,我们基本上试图扩展/改变。所以,虽然这是一个冗长的答案,但是一旦掌握了所有相关内容,它就不那么复杂了。

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中并将(再次)包装一个&#34;内部&#34;对象,它是您通常的绑定将使用的对象类型(TextMessageEncodingBindingElement的情况下为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 类。

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

但是,如果您希望使用属性来自定义web.config文件中的MessageEncoder行为,那么它需要具有用ConfigurationProperty属性修饰的特殊属性,以使IIS知道它们接受来自元素属性的值在web.config文件中...然后必须相应地封送到内部MessageEncoder。

(如果你知道你的消息编码器将始终使用相同的配置,那么它可能更容易硬编码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文件:

  • 添加/更新web.config的configuration\system.serviceModel\extensions\bindingElementExtensions部分,以包含我们刚刚在上面的代码中创建的 TimestampedTextMsgEncodingExtension 的完全限定引用
  • 使用&#34; customBinding&#34;的绑定创建/更新端点指向bindingConfiguration( HttpNoAuthTimestampEncoding
  • 在bindings \ customBinding下添加/更新bindingConfiguration部分( HttpNoAuthTimestampEncoding ),以使用我们为自定义MessageEncoder扩展名( timestampedTextMsgEncoding )指定名称的元素。 #34; bindingElementExtensions&#34;表示流量应该通过该编码器的部分。
<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服务方法,以获取我们添加到传入消息的时间戳属性。由于我们在将客户端消息发送到内部MessageEncoder之前记录了DateTime,因此这应该是我们获得DateTime的最早机会,因为POST消息的最后一位是从客户端传输的。

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


最后两个笔记。

  1. 如果您创建了一个新的端点,请确保在您进行测试时,您的客户端指向该端点的地址(在我意识到我被忽视之前,我在这上面追了大约一个小时那个细节)。

  2. 上面的web.config示例几乎没有显示我弄清楚如何使customBinding配置( HttpNoAuthTimestampEncoding )与basicHttpBinding配置的功能相匹配的困难( HttpNoAuth )。关于customBinding元素的文档有所帮助,但主要是大量的网络搜索和反复试验。特别值得注意的是使用<httpTransport><httpsTransport>在HTTP和HTTPS之间进行切换。相比之下,basicHttpBinding使用&#34; security \ @ mode = None&#34;和&#34; security \ @ mode =运输&#34;。