如何在WCF中反序列化自定义SOAP头?
本文关键字:自定义 SOAP 反序列化 WCF | 更新日期: 2023-09-27 18:18:47
我试图在WCF上为所有SOAP请求添加一个自定义头。我找到了一篇关于如何做到这一点的很棒的文章。我的MessageHeader
类是这样的:
public class OperatorNameMessageHeader : MessageHeader
{
private string opName;
public const string HeaderName = "OperatorNameMessageHeader";
public const string HeaderNamespace = "http://schemas.microsoft.com/scout";
public override string Name { get { return HeaderName; } }
public override string Namespace { get { return HeaderNamespace; } }
public string OperatorName
{
get { return opName; }
set { opName = value; }
}
public OperatorNameMessageHeader()
{
}
public OperatorNameMessageHeader(string operatorName)
{
opName = operatorName;
}
protected override void OnWriteHeaderContents(XmlDictionaryWriter writer, MessageVersion messageVersion)
{
writer.WriteElementString("OperatorName", opName);
}
}
文章没有说明的一件事是如何读取服务器上的值。根据这篇文章,你可以使用OperationContext.Current.IncomingMessageHeaders
来阅读这些标题。当我在调试器下查看这些MessageHeaders
时,我看到3个头,包括我的自定义头。因此,它肯定会出现在SOAP数据中。然而,当我调用GetHeader
:
OperatorNameMessageHeader test = msgHeaders.GetHeader<OperatorNameMessageHeader>(OperatorNameMessageHeader.HeaderName, OperatorNameMessageHeader.HeaderNamespace);
则test.OperatorName
为空。基本上,我只是得到一个空的OperatorNameMessageHeader
对象,它没有从SOAP中的数据中反序列化。
下一步是运行WCF跟踪工具。当我这样做时,我可以验证自定义报头确实是通过网络发送:
<MessageHeaders>
<ActivityId CorrelationId="66a7c5b6-3548-4f3c-9120-4484af76b64b" xmlns="http://schemas.microsoft.com/2004/09/ServiceModel/Diagnostics">f9bef03b-4e7b-4e84-b327-5e79814d9933</ActivityId>
<OperatorNameMessageHeader xmlns="http://schemas.microsoft.com/scout">
<OperatorName>Correct Operator Name</OperatorName>
</OperatorNameMessageHeader>
<To d4p1:mustUnderstand="1" xmlns:d4p1="http://schemas.xmlsoap.org/soap/envelope/" xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none">http://localhost:90/IRolesAndResourcesManager</To>
<Action d4p1:mustUnderstand="1" xmlns:d4p1="http://schemas.xmlsoap.org/soap/envelope/" xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none">http://tempuri.org/IRolesAndResourcesManager/Authenticate</Action>
</MessageHeaders>
所以,服务器有数据,我只是不能获取到它。这个问题的解决办法是什么?
我遇到了完全相同的问题,并且能够使其工作如下:
[DataContract(Namespace = OperatorNameMessageHeader.HeaderNamespace)]
public class OperatorNameMessageHeader
{
public const string HeaderName = "OperatorNameMessageHeader";
public const string HeaderNamespace = "http://schemas.microsoft.com/scout";
[DataMember]
public string OperatorName { get; set; }
}
有了这个,我可以像下面这样读取标题:
public static OperatorNameMessageHeader DeserializeSoap(string xml)
{
using (var reader = XmlReader.Create(new StringReader(xml)))
{
var m = System.ServiceModel.Channels.Message.CreateMessage(reader, int.MaxValue, MessageVersion.Soap11);
var operatorNameHeader = m.Headers.GetHeader<OperatorNameMessageHeader>(OperatorNameMessageHeader.HeaderName, OperatorNameMessageHeader.HeaderNamespace);
return operatorNameHeader;
}
}
注意,我使用WCF来反序列化XML字符串,因此需要使用[DataContract]
和[DataMember]
属性—没有它们,它将无法工作。不确定您是否需要实际从MessageHeader
为您的情况派生,但对我来说,这是不必要的,以便读取自定义头。
我也遇到过类似的问题。我必须从头部读取用户名和密码。我找到了一个暂时的解决方案,我使用XmlDictionaryReader。但是使用这段代码,我只查找名称,我仍然可以改进它,但目前有效。我在VB中有它,c#中也会有类似的
Dim username As String = ""
Dim password As String = ""
Dim usernameTokenId As String = ""
Dim passwordType As String = ""
For i As Integer = 0 To OperationContext.Current.IncomingMessageHeaders.Count - 1
Dim mhi As Channels.MessageHeaderInfo = OperationContext.Current.IncomingMessageHeaders.Item(i)
Dim headers As Channels.MessageHeaders = OperationContext.Current.RequestContext.RequestMessage.Headers
If mhi.Name.Equals("Security") Then
Dim xr As XmlDictionaryReader = OperationContext.Current.IncomingMessageHeaders.GetReaderAtHeader(i)
xr.MoveToContent()
While xr.MoveToNextAttribute()
Console.Write(" {0}='{1}'", xr.Name, xr.Value)
End While
Do
Select Case xr.NodeType
Case XmlNodeType.Element
If xr.LocalName.Equals("Username") Then
username = xr.ReadElementContentAsString()
End If
If xr.LocalName.Equals("Password") Then
password = xr.ReadElementContentAsString()
End If
While xr.MoveToNextAttribute()
If xr.LocalName.Equals("Id") Then
usernameTokenId = xr.Value
End If
If xr.LocalName.Equals("Type") Then
passwordType = xr.Value
End If
End While
Case XmlNodeType.Attribute
'Case XmlNodeType.Text
' Console.Write(xr.Value)
'Case XmlNodeType.EndElement
' Console.Write("</{0}>", xr.Name)
End Select
Loop While xr.Read()
End If
Dim name As String = mhi.Name
Next