我有一个完美运行的WCF RESTful服务。
我想要检索尽可能多的信息尽可能每当有人发送GET
或POST
请求,我的服务
我使用下列步骤恢复我的大部分信息:
OperationContext context = OperationContext.Current;
MessageProperties messageProperties = context.IncomingMessageProperties;
RemoteEndpointMessageProperty endpointProperty =
messageProperties[RemoteEndpointMessageProperty.Name] as RemoteEndpointMessageProperty;
我确实需要找到请求的时间戳帮助。
谢谢
编辑:对某些人的问题作出无关的问题,去除因此这个问题的一部分。
我发现自己最近类似的位置和读取从伟大的答案后@carlosfigueira ,我决定深入挖掘一点,并找出如何让他的榜样的工作从IIS web.config文件。
事实证明,卡洛斯已经在微软自己的博客,并具有覆盖很详细此主题相关的一些职位。 在顶部的图此页 ,是非常有益的,并明确表示, MessageEncoder,具体是我会去当服务是通过IIS托管,直接读取客户端的消息了电线最接近的一次。
这是使用相同的过程卡洛斯详细基本完成,但要获得它在web.config文件做,你必须创建派生的类BindingElementExtensionElement给IIS所需的挂钩来创建BindingElement /编码器/工厂如图所示的例子下面。
(我讨厌重复什么卡洛斯已经公布,但确实在盘算出这一切的过程中调整自己的代码位。因此,而不是偶然邮政代码不以他为榜样的工作,我就重新发布我的这里的调整)
步骤1)创建自定义MessageEncoder,具体 (和相关的MessageEncoderFactory )封装了原MessageEncoder,具体您结合通常会使用,使包装MessageEncoder,具体的时间戳添加到消息的原/包裹MessageEncoder,具体创建。
注意:这是前两三个班,将主要包装对象,并通过调用传递到包装/内部对象(即“ 返回inner.property/method”),允许自定义MessageEncoder,具体精确匹配实现他们的WCF接口我们基本上是试图延长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文件:
- 添加/更新配置\ system.serviceModel \ \扩展bindingElementExtensions你的web.config的部分包括一个完全合格的参考,我们只是在代码中创建上面TimestampedTextMsgEncodingExtension
- 创建/使用的“绑定更新端点customBinding ”指向一个bindingConfiguration(HttpNoAuthTimestampEncoding)
- 添加/更新下绑定的bindingConfiguration部分(HttpNoAuthTimestampEncoding)\ customBinding使用与我们的“bindingElementExtensions”部分中分配给我们的定制MessageEncoder,具体扩展(timestampedTextMsgEncoding)名称的元素来表示交通流应该通过编码器。
<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服务方法(S)拉我们加入进来的消息的时间戳属性。 由于我们记录的日期时间我们给客户的邮件内MessageEncoder,具体之前,这应该是基本上是我们必须得到一个DateTime,因为POST消息的最后一位是从客户端转移尽早。
DateTime SOAPMsgReceived = (DateTime)System.ServiceModel.OperationContext.Current.IncomingMessageProperties[TimestampedMsgEncoder.TimestampProp];
最后两个音符。
如果你创建了一个新的端点,请确保您的客户指出了端点的地址,当你去考(我追我对这个尾巴大约一个小时前,我意识到我忽视了细节)。
上述的web.config样品做一点要说明我已经搞清楚如何使customBinding配置(HttpNoAuthTimestampEncoding)匹配basicHttpBinding的配置(HttpNoAuth)的功能性的难度。 在对文档customBinding元素帮助一点点,但大多是一个很大的网络搜索和反复试验。 尤其值得注意的是用<httpTransport>
和<httpsTransport>
到HTTP和HTTPS之间翻转。 相比之下,basicHttpBinding的使用 “安全\ @模式= none” 和 “安全\ @模式=运输”。
这取决于你的意思是请求的时间戳什么。 如果你想知道什么时候请求离开客户端,那么你没有这些信息(甚至在ASP.NET,与HttpContext.Timestamp
属性,不可用) -服务器只有在接收到请求知道( 这是什么HttpContext.Timestamp
给你,基于文档 )。
对于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();
}
}