我正在编写一个 WCF 服务(.NET Framework),该服务应该充当向其发送消息的客户端的提供者(仅限单向通信)。服务必须使用SOAP 协议进行通信。对服务提供商的要求之一是创建自定义 HTTP 响应,其正文将包含 SOAP 错误,如 w3c 标准中所述。 我想我会尝试使用MessageInspector类来达到这个目的,但是我无法达到预期的效果。我尝试根据文档(
https://learn.microsoft.com/en-us/dotnet/framework/wcf/samples/message-inspectors?redirectedfrom=MSDN)实现必要的方法和配置,但到目前为止没有效果。 目前,该服务由 .NET Framework 控制台应用程序托管。以下是最重要元素的实现和项目结构:
结构:
MyService
DTO
-- CompositeType.cs
Utils
-- SchemaValidationBehavior.cs
-- SchemaValidationBehaviorExtensionElement.cs
-- SchemaValidationMessageInspector.cs
-- ValidationFault.cs
App.config
IMyService
MyService
[ServiceContract]
public interface IMyService
{
[OperationContract(IsOneWay = true)]
void SendData(CompositeType composite);
}
public class MyService : IMyService
{
public void SendData(CompositeType composite)
{
System.Diagnostics.Debugger.Break();
Console.WriteLine(composite.BoolValue.ToString() + " " + composite.StringValue);
}
}
class SchemaValidationMessageInspector : IClientMessageInspector, IDispatchMessageInspector
{
XmlSchemaSet schemaSet;
bool validateRequest;
bool validateReply;
bool isClientSide;
[ThreadStatic] // Wskazuje, że wartość pola statycznego jest unikalna dla każdego wątku
bool isRequest;
public SchemaValidationMessageInspector()
{
}
public SchemaValidationMessageInspector(XmlSchemaSet schemaSet, bool validateRequest, bool validateReply, bool isClientSide)
{
this.schemaSet = schemaSet;
this.validateRequest = validateRequest;
this.validateReply = validateReply;
this.isClientSide = isClientSide;
}
// IClientMessageInspector Interfaces -- Client message inspector
public void AfterReceiveReply(ref Message reply, object correlationState)
{
Console.WriteLine("I am inside AfterReceiveReply");
}
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
Console.WriteLine("I am inside BeforeSendRequest");
return null;
}
// IDispatchMessageInspector Interfaces -- Service message inspector
public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
{
Console.WriteLine("I am inside AfterReceiveRequest");
return null;
}
public void BeforeSendReply(ref Message reply, object correlationState)
{
Console.WriteLine("I am inside BeforeSendReply");
}
}
class SchemaValidationBehavior : IEndpointBehavior
{
XmlSchemaSet schemaSet;
bool validateRequest;
bool validateReply;
public SchemaValidationBehavior(){}
public SchemaValidationBehavior(XmlSchemaSet schemaSet, bool inspectRequest, bool inspectReply)
{
this.schemaSet = schemaSet;
this.validateReply = inspectReply;
this.validateRequest = inspectRequest;
}
#region IEndpointBehavior Members
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters){}
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
Console.WriteLine("I am inside ApplyClientBehavior");
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
Console.WriteLine("I am inside ApplyDispatchBehavior");
SchemaValidationMessageInspector inspector = new SchemaValidationMessageInspector();
endpointDispatcher.DispatchRuntime.MessageInspectors.Add(inspector);
}
public void Validate(ServiceEndpoint endpoint){}
#endregion
}
public class SchemaValidationBehaviorExtensionElement : BehaviorExtensionElement
{
public SchemaValidationBehaviorExtensionElement(){}
public override Type BehaviorType
{
get
{
return typeof(SchemaValidationBehavior);
}
}
protected override object CreateBehavior()
{
Console.WriteLine("I am inside ApplyClientBehavior");
return new SchemaValidationBehavior();
}
[ConfigurationProperty("validateRequest", DefaultValue = false, IsRequired = false)]
public bool ValidateRequest
{
get { return (bool)base["validateRequest"]; }
set { base["validateRequest"] = value; }
}
[ConfigurationProperty("validateReply", DefaultValue = false, IsRequired = false)]
public bool ValidateReply
{
get { return (bool)base["validateReply"]; }
set { base["validateReply"] = value; }
}
}
配置:<configuration>
<appSettings>
<add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" />
</appSettings>
<system.web>
<compilation debug="true" />
</system.web>
<system.serviceModel>
<!-- SERVICES -->
<services>
<service name="MyService.MyService" behaviorConfiguration="MyServiceBehavior">
<endpoint address="" binding="basicHttpBinding" contract="MyService.IMyService" behaviorConfiguration="MyEndpointBehavior">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="http://localhost:9000/MyService/" />
</baseAddresses>
</host>
</service>
</services>
<!-- SERVICES -->
<!-- BEHAVIORS -->
<behaviors>
<endpointBehaviors>
<behavior name="MyEndpointBehavior">
<schemaValidator validateRequest="true" validateReply="true"/>
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior name="MyServiceBehavior">
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
<!-- Add the behavior extension here -->
</behavior>
</serviceBehaviors>
</behaviors>
<!-- BEHAVIORS -->
<!-- EXTENSIONS -->
<extensions>
<behaviorExtensions>
<add name="schemaValidator" type="MyService.Utils.SchemaValidationBehaviorExtensionElement, MyService" />
</behaviorExtensions>
</extensions>
<!-- EXTENSIONS -->
</system.serviceModel>
</configuration>
我一定缺少一些配置,或者可能配置错误,但从客户端发送的消息直接发送到 MyService 类的 SendData() 方法。
附注我想这并不重要,但这是 Host App 的代码:
internal class Program
{
static void Main(string[] args)
{
ServiceHost host = null;
try
{
host = new ServiceHost(typeof(MyService.MyService));
host.Open();
Console.WriteLine($"Host for MyService started @ {DateTime.Now} {Environment.NewLine}Service hosted on http://localhost:9000");
Console.ReadLine();
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
Console.ReadLine();
}
finally
{
host?.Close();
}
}
}
我尝试在主机的 App.config 文件中添加所有必要的配置 - 它发挥了作用 - 所有消息检查器方法都在消息到达目标方法之前被调用!