我确实有一个完美运行的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
我确实需要帮助来查找请求的时间戳。
谢谢
编辑:对某些人提出了无关问题的问题,因此部分问题被删除了。
答案 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文件:
<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];
最后两个笔记。
如果您创建了一个新的端点,请确保在您进行测试时,您的客户端指向该端点的地址(在我意识到我被忽视之前,我在这上面追了大约一个小时那个细节)。
上面的web.config示例几乎没有显示我弄清楚如何使customBinding配置( HttpNoAuthTimestampEncoding )与basicHttpBinding配置的功能相匹配的困难( HttpNoAuth )。关于customBinding元素的文档有所帮助,但主要是大量的网络搜索和反复试验。特别值得注意的是使用<httpTransport>
和<httpsTransport>
在HTTP和HTTPS之间进行切换。相比之下,basicHttpBinding使用&#34; security \ @ mode = None&#34;和&#34; security \ @ mode =运输&#34;。