WCF:使用自定义 header 发送 SOAP 请求
WCF: Send SOAP request with custom header
我正在开发 WCF 项目。我对 WCF 的经验不多。
我的目标是从客户端接收 SOAP 请求并传递到另一台主机(我的目标是 Amadeus Web 服务)。
我按照给出的本教程进行操作 link : https://weblogs.asp.net/paolopia/handling-custom-soap-headers-via-wcf-behaviors
这是我的代码
CustomBehavior.cs
using System;
using System.ServiceModel.Configuration;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
namespace WCFTest
{
[AttributeUsage(AttributeTargets.Class)]
public class CustomBehavior : Attribute, IEndpointBehavior
{
#region IEndpointBehavior Members
public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime)
{
CustomMessageInspector inspector = new CustomMessageInspector();
clientRuntime.MessageInspectors.Add(inspector);
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher)
{
ChannelDispatcher channelDispatcher = endpointDispatcher.ChannelDispatcher;
if (channelDispatcher != null)
{
foreach (EndpointDispatcher ed in channelDispatcher.Endpoints)
{
CustomMessageInspector inspector = new CustomMessageInspector();
ed.DispatchRuntime.MessageInspectors.Add(inspector);
}
}
}
public void Validate(ServiceEndpoint endpoint)
{
}
#endregion
}
public class CustomBehaviorExtensionElement : BehaviorExtensionElement
{
protected override object CreateBehavior()
{
return new CustomBehavior();
}
public override Type BehaviorType
{
get
{
return typeof(CustomBehavior);
}
}
}
}
CustomHeader.cs
using System;
using System.ServiceModel.Channels;
using System.Xml;
namespace WCFTest
{
public class CustomHeader : MessageHeader
{
private String _key;
public String Key
{
get
{
return (this._key);
}
}
public CustomHeader(String key)
{
this._key = key;
}
public override string Name
{
get { return (CustomHeaderNames.CustomHeaderName); }
}
public override string Namespace
{
get { return (CustomHeaderNames.CustomHeaderNamespace); }
}
protected override void OnWriteHeaderContents(System.Xml.XmlDictionaryWriter writer, MessageVersion messageVersion)
{
// Write the content of the header directly using the XmlDictionaryWriter
writer.WriteElementString(CustomHeaderNames.KeyName, this.Key);
}
public static CustomHeader ReadHeader(XmlDictionaryReader reader)
{
// Read the header content (key) using the XmlDictionaryReader
if (reader.ReadToDescendant(CustomHeaderNames.KeyName, CustomHeaderNames.CustomHeaderNamespace))
{
String key = reader.ReadElementString();
return (new CustomHeader(key));
}
else
{
return null;
}
}
}
public static class CustomHeaderNames
{
public const String CustomHeaderName = "CustomHeader";
public const String KeyName = "Key";
public const String CustomHeaderNamespace = "http://schemas.devleap.com/CustomHeader";
}
}
CustomMessageInspector.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Dispatcher;
using System.Web;
using System.Xml;
namespace WCFTest
{
public class CustomMessageInspector : IDispatchMessageInspector, IClientMessageInspector
{
#region Message Inspector of the Service
public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
{
// Look for my custom header in the request
Int32 headerPosition = request.Headers.FindHeader(CustomHeaderNames.CustomHeaderName, CustomHeaderNames.CustomHeaderNamespace);
// Get an XmlDictionaryReader to read the header content
XmlDictionaryReader reader = request.Headers.GetReaderAtHeader(headerPosition);
// Read it through its static method ReadHeader
CustomHeader header = CustomHeader.ReadHeader(reader);
// Add the content of the header to the IncomingMessageProperties dictionary
OperationContext.Current.IncomingMessageProperties.Add("key", header.Key);
return null;
}
public void BeforeSendReply(ref Message reply, object correlationState)
{
}
#endregion
#region Message Inspector of the Consumer
public void AfterReceiveReply(ref Message reply, object correlationState)
{
}
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
// Prepare the request message copy to be modified
MessageBuffer buffer = request.CreateBufferedCopy(Int32.MaxValue);
request = buffer.CreateMessage();
// Simulate to have a random Key generation process
request.Headers.Add(new CustomHeader(Guid.NewGuid().ToString()));
return null;
}
#endregion
}
}
IService1.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;
namespace WCFTest
{
[ServiceContract]
public interface IService1
{
}
}
Service1.svc
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;
namespace WCFTest
{
public class Service1 : IService1
{
}
}
web.config
<?xml version="1.0"?>
<configuration>
<appSettings>
<add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" />
</appSettings>
<system.web>
<compilation debug="true" targetFramework="4.8" />
<httpRuntime targetFramework="4.8"/>
</system.web>
<system.serviceModel>
<extensions>
<behaviorExtensions>
<add name="customBehavior" type="WCF_Test.CustomBehaviorExtensionElement, DevLeap.WCF.Behaviors.Extensions, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</behaviorExtensions>
</extensions>
<services>
<service name="WCF_Test.Service1" behaviorConfiguration="">
<endpoint
address="net.tcp://localhost:35001/ServiceOne/"
binding="netTcpBinding"
contract="WCF_Test.IService1"
behaviorConfiguration="endpointBehavior" />
</service>
</services>
<behaviors>
<endpointBehaviors>
<behavior name="endpointBehavior">
<customBehavior />
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior>
<!-- To avoid disclosing metadata information, set the values below to false before deployment -->
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
<!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
</serviceBehaviors>
</behaviors>
<protocolMapping>
<add binding="basicHttpsBinding" scheme="https" />
</protocolMapping>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
<!--
To browse web app root directory during debugging, set the value below to true.
Set to false before deployment to avoid disclosing web app folder information.
-->
<directoryBrowse enabled="true"/>
</system.webServer>
</configuration>
现在我的问题是
如何更改默认的 WCF SOAP Header 和 Body 消息?我的 SOAP 请求应该如下所示:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" xmlns:sec="http://xml.amadeus.com/2010/06/Security_v1" xmlns:link="http://wsdl.amadeus.com/2010/06/ws/Link_v1" xmlns:ses="http://xml.amadeus.com/2010/06/Session_v3" xmlns:pnr="http://xml.amadeus.com/FLIREQ_07_1_1A">
<s:Header xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<add:MessageID xmlns:add="http://www.w3.org/2005/08/addressing">305ec189-1f8c-45c4-ba78-a832d6181b48</add:MessageID>
<add:Action xmlns:add="http://www.w3.org/2005/08/addressing">http://webservices.amadeus.com/FLIREQ_07_1_1A</add:Action>
<add:To xmlns:add="http://www.w3.org/2005/08/addressing">https://nodeA1.test.webservices.amadeus.com/1ASIWGENOM</add:To>
<link:TransactionFlowLink xmlns:link="http://wsdl.amadeus.com/2010/06/ws/Link_v1">
<link:Consumer>
<link:UniqueID>iMM42S6y6KWQ6LUpZnqA8Q==</link:UniqueID>
</link:Consumer>
</link:TransactionFlowLink>
<sec:Security xmlns:sec="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<sec:UsernameToken xmlns:oas1="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" oas1:Id="UsernameToken-1">
<sec:Username>myusername</sec:Username>
<sec:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">UKxRL2MOdKz1k1ik1l76tQ==</sec:Nonce>
<sec:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">nmgMLy29QGO+gH7zQSOrOmVji8o=</sec:Password>
<oas1:Created>2020-07-06T06:08:17.938Z</oas1:Created>
</sec:UsernameToken>
</sec:Security>
<AMA_SecurityHostedUser xmlns="http://xml.amadeus.com/2010/06/Security_v1">
<UserID POS_Type="1" PseudoCityCode="ULNOM0101" RequestorType="U" />
</AMA_SecurityHostedUser>
</s:Header>
<s:Body>
<Air_FlightInfo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<generalFlightInfo xmlns="http://xml.amadeus.com/FLIREQ_07_1_1A">
<flightDate>
<departureDate>160720</departureDate>
</flightDate>
<companyDetails>
<marketingCompany>OM</marketingCompany>
</companyDetails>
<flightIdentification>
<flightNumber>0301</flightNumber>
</flightIdentification>
</generalFlightInfo>
</Air_FlightInfo>
</s:Body>
</s:Envelope>
目前我的Service1.svc是空的。如何调用自定义消息检查器?
Visual Studio 在 web.config 文件上给我这个错误。 Visual Studio 未检测到我创建的自定义扩展。如何解决这个问题?
Severity Code Description Project Path File Line Source Suppression State
Warning The element 'behavior' has invalid child element 'customBehavior'. List of possible elements expected: 'clientVia, callbackDebug, callbackTimeouts, clear, clientCredentials, transactedBatching, dataContractSerializer, dispatcherSynchronization, remove, synchronousReceive, webHttp, enableWebScript, endpointDiscovery, soapProcessing'. WCF_Test C:\Users\sambuu-yondon.e\source\repos\AMA_WCF\WCF_Test C:\Users\sambuu-yondon.e\source\repos\AMA_WCF\WCF_Test\Web.config 29 IntelliSense
- 我从 Amadeus 的开发者网站下载了 WSDL 文件并作为服务参考添加到我的项目中。现在怎么用呢? WSDL 代理 class 是我的 SOAP Body 部分。应该包含 Soap:Body 标签。
如果有什么不清楚的地方,我会给你更多的细节。谢谢。
如果要在 server-side 上添加自定义 header,只需实现 IDispatchMessageInspector 接口即可。
这是我的演示:
public class CustomMessageInspector : IDispatchMessageInspector
{
public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
{
MessageHeader header = MessageHeader.CreateHeader("Testrequest", "http://Test", "Test");
OperationContext.Current.IncomingMessageHeaders.Add(header);
Console.WriteLine("request"+request);
return null;
}
public void BeforeSendReply(ref Message reply, object correlationState)
{
MessageHeader header = MessageHeader.CreateHeader("Testreply", "http://Test", "Test");
OperationContext.Current.OutgoingMessageHeaders.Add(header);
Console.WriteLine("reply"+reply);
}
}
CustomMessageInspector 实现了IDispatchMessageInspector 接口,在获取消息后添加自定义header,发送消息前也添加自定义header
[AttributeUsage(AttributeTargets.Interface)]
public class CustomBehavior : Attribute, IContractBehavior
{
public void AddBindingParameters(ContractDescription contractDescription, ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
return;
}
public void ApplyClientBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
return;
}
public void ApplyDispatchBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, DispatchRuntime dispatchRuntime)
{
dispatchRuntime.MessageInspectors.Add(new CustomMessageInspector());
}
public void Validate(ContractDescription contractDescription, ServiceEndpoint endpoint)
{
return;
}
}
我们将这个拦截器添加到服务的行为中。
最后我们将 Custombehavior 应用到我们的服务中。
更新
我的项目:
namespace Test
{
public class CustomMessageInspector : IDispatchMessageInspector
{
public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
{
MessageHeader header = MessageHeader.CreateHeader("Testrequest", "http://Test", "Test");
OperationContext.Current.IncomingMessageHeaders.Add(header);
Console.WriteLine("request"+request);
return null;
}
public void BeforeSendReply(ref Message reply, object correlationState)
{
MessageHeader header = MessageHeader.CreateHeader("Testreply", "http://Test", "Test");
OperationContext.Current.OutgoingMessageHeaders.Add(header);
Console.WriteLine("reply"+reply);
}
}
[AttributeUsage(AttributeTargets.Interface)]
public class CustomBehavior : Attribute, IContractBehavior
{
public void AddBindingParameters(ContractDescription contractDescription, ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
return;
}
public void ApplyClientBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
return;
}
public void ApplyDispatchBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, DispatchRuntime dispatchRuntime)
{
dispatchRuntime.MessageInspectors.Add(new CustomMessageInspector());
}
public void Validate(ContractDescription contractDescription, ServiceEndpoint endpoint)
{
return;
}
}
class Program
{
[ServiceContract]
[CustomBehavior]
public interface IService1
{
[OperationContract]
string hello();
}
public class Service1 : IService1
{
public string hello()
{
return "hi";
}
}
static void Main(string[] args)
{
// Step 1: Create a URI to serve as the base address.
Uri baseAddress = new Uri("http://localhost:8000/GettingStarted/");
// Step 2: Create a ServiceHost instance.
ServiceHost selfHost = new ServiceHost(typeof(Service1), baseAddress);
try
{
// Step 3: Add a service endpoint.
selfHost.AddServiceEndpoint(typeof(IService1), new BasicHttpBinding(), "CalculatorService");
// Step 4: Enable metadata exchange.
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
selfHost.Description.Behaviors.Add(smb);
// Step 5: Start the service.
selfHost.Open();
Console.WriteLine("The service is ready.");
// Close the ServiceHost to stop the service.
Console.WriteLine("Press <Enter> to terminate the service.");
Console.WriteLine();
Console.ReadLine();
selfHost.Close();
}
catch (CommunicationException ce)
{
Console.WriteLine("An exception occurred: {0}", ce.Message);
selfHost.Abort();
}
}
}
}
我正在开发 WCF 项目。我对 WCF 的经验不多。
我的目标是从客户端接收 SOAP 请求并传递到另一台主机(我的目标是 Amadeus Web 服务)。
我按照给出的本教程进行操作 link : https://weblogs.asp.net/paolopia/handling-custom-soap-headers-via-wcf-behaviors
这是我的代码
CustomBehavior.cs
using System;
using System.ServiceModel.Configuration;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
namespace WCFTest
{
[AttributeUsage(AttributeTargets.Class)]
public class CustomBehavior : Attribute, IEndpointBehavior
{
#region IEndpointBehavior Members
public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime)
{
CustomMessageInspector inspector = new CustomMessageInspector();
clientRuntime.MessageInspectors.Add(inspector);
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher)
{
ChannelDispatcher channelDispatcher = endpointDispatcher.ChannelDispatcher;
if (channelDispatcher != null)
{
foreach (EndpointDispatcher ed in channelDispatcher.Endpoints)
{
CustomMessageInspector inspector = new CustomMessageInspector();
ed.DispatchRuntime.MessageInspectors.Add(inspector);
}
}
}
public void Validate(ServiceEndpoint endpoint)
{
}
#endregion
}
public class CustomBehaviorExtensionElement : BehaviorExtensionElement
{
protected override object CreateBehavior()
{
return new CustomBehavior();
}
public override Type BehaviorType
{
get
{
return typeof(CustomBehavior);
}
}
}
}
CustomHeader.cs
using System;
using System.ServiceModel.Channels;
using System.Xml;
namespace WCFTest
{
public class CustomHeader : MessageHeader
{
private String _key;
public String Key
{
get
{
return (this._key);
}
}
public CustomHeader(String key)
{
this._key = key;
}
public override string Name
{
get { return (CustomHeaderNames.CustomHeaderName); }
}
public override string Namespace
{
get { return (CustomHeaderNames.CustomHeaderNamespace); }
}
protected override void OnWriteHeaderContents(System.Xml.XmlDictionaryWriter writer, MessageVersion messageVersion)
{
// Write the content of the header directly using the XmlDictionaryWriter
writer.WriteElementString(CustomHeaderNames.KeyName, this.Key);
}
public static CustomHeader ReadHeader(XmlDictionaryReader reader)
{
// Read the header content (key) using the XmlDictionaryReader
if (reader.ReadToDescendant(CustomHeaderNames.KeyName, CustomHeaderNames.CustomHeaderNamespace))
{
String key = reader.ReadElementString();
return (new CustomHeader(key));
}
else
{
return null;
}
}
}
public static class CustomHeaderNames
{
public const String CustomHeaderName = "CustomHeader";
public const String KeyName = "Key";
public const String CustomHeaderNamespace = "http://schemas.devleap.com/CustomHeader";
}
}
CustomMessageInspector.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Dispatcher;
using System.Web;
using System.Xml;
namespace WCFTest
{
public class CustomMessageInspector : IDispatchMessageInspector, IClientMessageInspector
{
#region Message Inspector of the Service
public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
{
// Look for my custom header in the request
Int32 headerPosition = request.Headers.FindHeader(CustomHeaderNames.CustomHeaderName, CustomHeaderNames.CustomHeaderNamespace);
// Get an XmlDictionaryReader to read the header content
XmlDictionaryReader reader = request.Headers.GetReaderAtHeader(headerPosition);
// Read it through its static method ReadHeader
CustomHeader header = CustomHeader.ReadHeader(reader);
// Add the content of the header to the IncomingMessageProperties dictionary
OperationContext.Current.IncomingMessageProperties.Add("key", header.Key);
return null;
}
public void BeforeSendReply(ref Message reply, object correlationState)
{
}
#endregion
#region Message Inspector of the Consumer
public void AfterReceiveReply(ref Message reply, object correlationState)
{
}
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
// Prepare the request message copy to be modified
MessageBuffer buffer = request.CreateBufferedCopy(Int32.MaxValue);
request = buffer.CreateMessage();
// Simulate to have a random Key generation process
request.Headers.Add(new CustomHeader(Guid.NewGuid().ToString()));
return null;
}
#endregion
}
}
IService1.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;
namespace WCFTest
{
[ServiceContract]
public interface IService1
{
}
}
Service1.svc
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;
namespace WCFTest
{
public class Service1 : IService1
{
}
}
web.config
<?xml version="1.0"?>
<configuration>
<appSettings>
<add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" />
</appSettings>
<system.web>
<compilation debug="true" targetFramework="4.8" />
<httpRuntime targetFramework="4.8"/>
</system.web>
<system.serviceModel>
<extensions>
<behaviorExtensions>
<add name="customBehavior" type="WCF_Test.CustomBehaviorExtensionElement, DevLeap.WCF.Behaviors.Extensions, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</behaviorExtensions>
</extensions>
<services>
<service name="WCF_Test.Service1" behaviorConfiguration="">
<endpoint
address="net.tcp://localhost:35001/ServiceOne/"
binding="netTcpBinding"
contract="WCF_Test.IService1"
behaviorConfiguration="endpointBehavior" />
</service>
</services>
<behaviors>
<endpointBehaviors>
<behavior name="endpointBehavior">
<customBehavior />
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior>
<!-- To avoid disclosing metadata information, set the values below to false before deployment -->
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
<!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
</serviceBehaviors>
</behaviors>
<protocolMapping>
<add binding="basicHttpsBinding" scheme="https" />
</protocolMapping>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
<!--
To browse web app root directory during debugging, set the value below to true.
Set to false before deployment to avoid disclosing web app folder information.
-->
<directoryBrowse enabled="true"/>
</system.webServer>
</configuration>
现在我的问题是
如何更改默认的 WCF SOAP Header 和 Body 消息?我的 SOAP 请求应该如下所示:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" xmlns:sec="http://xml.amadeus.com/2010/06/Security_v1" xmlns:link="http://wsdl.amadeus.com/2010/06/ws/Link_v1" xmlns:ses="http://xml.amadeus.com/2010/06/Session_v3" xmlns:pnr="http://xml.amadeus.com/FLIREQ_07_1_1A"> <s:Header xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"> <add:MessageID xmlns:add="http://www.w3.org/2005/08/addressing">305ec189-1f8c-45c4-ba78-a832d6181b48</add:MessageID> <add:Action xmlns:add="http://www.w3.org/2005/08/addressing">http://webservices.amadeus.com/FLIREQ_07_1_1A</add:Action> <add:To xmlns:add="http://www.w3.org/2005/08/addressing">https://nodeA1.test.webservices.amadeus.com/1ASIWGENOM</add:To> <link:TransactionFlowLink xmlns:link="http://wsdl.amadeus.com/2010/06/ws/Link_v1"> <link:Consumer> <link:UniqueID>iMM42S6y6KWQ6LUpZnqA8Q==</link:UniqueID> </link:Consumer> </link:TransactionFlowLink> <sec:Security xmlns:sec="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"> <sec:UsernameToken xmlns:oas1="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" oas1:Id="UsernameToken-1"> <sec:Username>myusername</sec:Username> <sec:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">UKxRL2MOdKz1k1ik1l76tQ==</sec:Nonce> <sec:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">nmgMLy29QGO+gH7zQSOrOmVji8o=</sec:Password> <oas1:Created>2020-07-06T06:08:17.938Z</oas1:Created> </sec:UsernameToken> </sec:Security> <AMA_SecurityHostedUser xmlns="http://xml.amadeus.com/2010/06/Security_v1"> <UserID POS_Type="1" PseudoCityCode="ULNOM0101" RequestorType="U" /> </AMA_SecurityHostedUser> </s:Header> <s:Body> <Air_FlightInfo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <generalFlightInfo xmlns="http://xml.amadeus.com/FLIREQ_07_1_1A"> <flightDate> <departureDate>160720</departureDate> </flightDate> <companyDetails> <marketingCompany>OM</marketingCompany> </companyDetails> <flightIdentification> <flightNumber>0301</flightNumber> </flightIdentification> </generalFlightInfo> </Air_FlightInfo> </s:Body> </s:Envelope>
目前我的Service1.svc是空的。如何调用自定义消息检查器?
Visual Studio 在 web.config 文件上给我这个错误。 Visual Studio 未检测到我创建的自定义扩展。如何解决这个问题?
Severity Code Description Project Path File Line Source Suppression State Warning The element 'behavior' has invalid child element 'customBehavior'. List of possible elements expected: 'clientVia, callbackDebug, callbackTimeouts, clear, clientCredentials, transactedBatching, dataContractSerializer, dispatcherSynchronization, remove, synchronousReceive, webHttp, enableWebScript, endpointDiscovery, soapProcessing'. WCF_Test C:\Users\sambuu-yondon.e\source\repos\AMA_WCF\WCF_Test C:\Users\sambuu-yondon.e\source\repos\AMA_WCF\WCF_Test\Web.config 29 IntelliSense
- 我从 Amadeus 的开发者网站下载了 WSDL 文件并作为服务参考添加到我的项目中。现在怎么用呢? WSDL 代理 class 是我的 SOAP Body 部分。应该包含 Soap:Body 标签。
如果有什么不清楚的地方,我会给你更多的细节。谢谢。
如果要在 server-side 上添加自定义 header,只需实现 IDispatchMessageInspector 接口即可。
这是我的演示:
public class CustomMessageInspector : IDispatchMessageInspector
{
public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
{
MessageHeader header = MessageHeader.CreateHeader("Testrequest", "http://Test", "Test");
OperationContext.Current.IncomingMessageHeaders.Add(header);
Console.WriteLine("request"+request);
return null;
}
public void BeforeSendReply(ref Message reply, object correlationState)
{
MessageHeader header = MessageHeader.CreateHeader("Testreply", "http://Test", "Test");
OperationContext.Current.OutgoingMessageHeaders.Add(header);
Console.WriteLine("reply"+reply);
}
}
CustomMessageInspector 实现了IDispatchMessageInspector 接口,在获取消息后添加自定义header,发送消息前也添加自定义header
[AttributeUsage(AttributeTargets.Interface)]
public class CustomBehavior : Attribute, IContractBehavior
{
public void AddBindingParameters(ContractDescription contractDescription, ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
return;
}
public void ApplyClientBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
return;
}
public void ApplyDispatchBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, DispatchRuntime dispatchRuntime)
{
dispatchRuntime.MessageInspectors.Add(new CustomMessageInspector());
}
public void Validate(ContractDescription contractDescription, ServiceEndpoint endpoint)
{
return;
}
}
我们将这个拦截器添加到服务的行为中。
最后我们将 Custombehavior 应用到我们的服务中。
更新
我的项目:
namespace Test
{
public class CustomMessageInspector : IDispatchMessageInspector
{
public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
{
MessageHeader header = MessageHeader.CreateHeader("Testrequest", "http://Test", "Test");
OperationContext.Current.IncomingMessageHeaders.Add(header);
Console.WriteLine("request"+request);
return null;
}
public void BeforeSendReply(ref Message reply, object correlationState)
{
MessageHeader header = MessageHeader.CreateHeader("Testreply", "http://Test", "Test");
OperationContext.Current.OutgoingMessageHeaders.Add(header);
Console.WriteLine("reply"+reply);
}
}
[AttributeUsage(AttributeTargets.Interface)]
public class CustomBehavior : Attribute, IContractBehavior
{
public void AddBindingParameters(ContractDescription contractDescription, ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
return;
}
public void ApplyClientBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
return;
}
public void ApplyDispatchBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, DispatchRuntime dispatchRuntime)
{
dispatchRuntime.MessageInspectors.Add(new CustomMessageInspector());
}
public void Validate(ContractDescription contractDescription, ServiceEndpoint endpoint)
{
return;
}
}
class Program
{
[ServiceContract]
[CustomBehavior]
public interface IService1
{
[OperationContract]
string hello();
}
public class Service1 : IService1
{
public string hello()
{
return "hi";
}
}
static void Main(string[] args)
{
// Step 1: Create a URI to serve as the base address.
Uri baseAddress = new Uri("http://localhost:8000/GettingStarted/");
// Step 2: Create a ServiceHost instance.
ServiceHost selfHost = new ServiceHost(typeof(Service1), baseAddress);
try
{
// Step 3: Add a service endpoint.
selfHost.AddServiceEndpoint(typeof(IService1), new BasicHttpBinding(), "CalculatorService");
// Step 4: Enable metadata exchange.
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
selfHost.Description.Behaviors.Add(smb);
// Step 5: Start the service.
selfHost.Open();
Console.WriteLine("The service is ready.");
// Close the ServiceHost to stop the service.
Console.WriteLine("Press <Enter> to terminate the service.");
Console.WriteLine();
Console.ReadLine();
selfHost.Close();
}
catch (CommunicationException ce)
{
Console.WriteLine("An exception occurred: {0}", ce.Message);
selfHost.Abort();
}
}
}
}