【问题标题】:WCF, information retrieval from a request, timestamp cant be foundWCF,从请求中检索信息,找不到时间戳
【发布时间】:2016-12-29 05:00:36
【问题描述】:

我确实有一个运行良好的 WCF 宁静服务。

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

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

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

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

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

谢谢

编辑:对于某些人来说,这个问题提出了不相关的问题,因此部分问题被删除了。

【问题讨论】:

  • "but I can't make that code to work" 是什么意思? 究竟会发生什么?请阅读How to Ask
  • 感谢您的链接。我的意思是我不确定如何在我的项目中实现和调用 HttpRequest。欢呼
  • 再一次,当你编译并运行它时会发生什么?
  • 它说,对象不包含Request的定义
  • 那么您从哪里获得该代码以及为什么要将其应用于 WCF?如果你在 IIS 中以兼容模式运行,你可以使用System.Web.HttpContext.Current.Request

标签: c# rest wcf http


【解决方案1】:

这取决于您所说的请求时间戳的含义。如果您想知道请求离开客户端的时间,那么您没有该信息(即使在 ASP.NET 上,具有 HttpContext.Timestamp 属性,也不可用) - 服务器只知道它何时收到请求(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();
    }
}

【讨论】:

    【解决方案2】:

    我最近发现自己处于类似的位置,在阅读了 @carlosfigueira 的精彩回答后,我决定深入挖掘并弄清楚如何让他的示例从 IIS web.config 文件中运行。

    事实证明,Carlos 在 Microsoft 有自己的博客,并且有几篇文章非常详细地介绍了这个主题。 this page 顶部的图表非常有帮助,并清楚地表明 MessageEncoder 是当服务通过 IIS 托管时,我最接近直接离线读取客户端消息的方式。

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

    (我讨厌复制 Carlos 已经发布的内容,但我确实在弄清楚这一切的过程中稍微调整了他的代码。所以,与其偶然发布不适用于他的示例的代码,我将在这里重新发布我的调整)


    第 1 步)创建您的 custom MessageEncoder(以及相关的 MessageEncoderFactory),它包装您的绑定通常使用的原始 MessageEncoder,并使包装 MessageEncoder 将时间戳添加到由原始/包装的 MessageEncoder 创建的 Message

    注意:这是三个类中的前两个,它们本质上将包装对象并通过将调用传递给包装/内部对象来实现其 WCF 接口(即。“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 中,并将(再次)包装一个“内部”对象,该对象是您通常绑定将使用的对象类型(在这种情况下为 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 的完全限定引用
    • 使用指向 bindingConfiguration (HttpNoAuthTimestampEncoding) 的“customBinding”绑定创建/更新端点
    • 在 bindings\customBinding 下添加/更新 bindingConfiguration 部分 (HttpNoAuthTimestampEncoding),以使用我们在“ bindingElementExtensions”部分以指示流量应流经该编码器。
    <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 元素的文档提供了一点帮助,但主要是大量的网络搜索和反复试验。特别值得注意的是使用&lt;httpTransport&gt;&lt;httpsTransport&gt; 在HTTP 和HTTPS 之间切换。相比之下,basicHttpBinding 使用“security\@mode=None”和“security\@mode=Transport”。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-03-21
      • 2017-11-26
      • 2015-07-27
      • 2020-04-08
      • 1970-01-01
      • 1970-01-01
      • 2014-02-03
      • 1970-01-01
      相关资源
      最近更新 更多