You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ASP.NET Core 3.1如何生成带wsse头的XML请求

ASP.NET Core 3.1生成带WSSE标签的SOAP XML问题

我们用ASP.NET Core 3.1生成XML调用WSDL服务,手动编写的XML在Postman中可正常调用,但程序生成的XML缺失wsse命名空间前缀,结构不符合要求,导致调用失败。

正确XML示例

<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
   <soap:Header>
      <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" soap:mustUnderstand="1">
         <wsse:UsernameToken xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="XXXX">
            <wsse:Username>XXX</wsse:Username>
            <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">XXXX</wsse:Password>
         </wsse:UsernameToken>
      </wsse:Security>
   </soap:Header>
   <soap:Body xmlns:ns1="http://www.opentravel.org/OTA/2003/05">
      <ns1:OTA_AirAvailRQ EchoToken="11868765275150-1300257933" PrimaryLangID="en-us" SequenceNmbr="1" TimeStamp="2012-08-27T03:00:23" Version="20061.00" Target="TEST">
         <ns1:POS>
            <ns1:Source TerminalID="XXXX">
               <ns1:RequestorID ID="XXXX" Type="XXXX" />
               <ns1:BookingChannel Type="12" />
            </ns1:Source>
         </ns1:POS>
         <ns1:OriginDestinationInformation>
            <ns1:DepartureDateTime WindowAfter="P1D" WindowBefore="P1D">2023-01-20T10:00:00</ns1:DepartureDateTime>
            <ns1:OriginLocation LocationCode="BOM" />
            <ns1:DestinationLocation LocationCode="SHJ" />
         </ns1:OriginDestinationInformation>
         <ns1:OriginDestinationInformation>
            <ns1:DepartureDateTime WindowAfter="P1D" WindowBefore="P1D">2023-01-25T10:00:00</ns1:DepartureDateTime>
            <ns1:OriginLocation LocationCode="SHJ" />
            <ns1:DestinationLocation LocationCode="BOM" />
         </ns1:OriginDestinationInformation>
         <ns1:TravelerInfoSummary>
            <ns1:AirTravelerAvail>
               <ns1:PassengerTypeQuantity Code="ADT" Quantity="3" />
               <ns1:PassengerTypeQuantity Code="CHD" Quantity="1" />
            </ns1:AirTravelerAvail>
         </ns1:TravelerInfoSummary>
      </ns1:OTA_AirAvailRQ>
   </soap:Body>
</soap:Envelope>

程序生成的错误XML示例

<?xml version="1.0" encoding="utf-8"?>
<Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" soap="http://schemas.xmlsoap.org/soap/envelope/" xsd="http://www.w3.org/2001/XMLSchema" xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.opentravel.org/OTA/2003/05">
    <Header>
        <Security mustUnderstand="1" wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
            <UsernameToken Id="xxx" wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xs">
                <Username>xx</Username>
                <Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">xxx</Password>
            </UsernameToken>
        </Security>
    </Header>
    <Body ns2="http://www.opentravel.org/OTA/2003/05">
        <OTA_AirAvailRQ EchoToken="11868765275150-1300257933" PrimaryLangID="en-us" SequenceNmbr="1" Target="xx" TimeStamp="2023-01-14T12:01:53.3258485+03:00" Version="20061">
            <POS>
                <Source TerminalID="xx">
                    <RequestorID ID="xx" Type="4" />
                    <BookingChannel Type="12" />
                </Source>
            </POS>
            <OriginDestinationInformation>
                <DepartureDateTime WindowAfter="P1D" WindowBefore="P1D">2023-01-20T10:00:00</DepartureDateTime>
                <OriginLocation LocationCode="BOM" />
                <DestinationLocation LocationCode="SHJ" />
            </OriginDestinationInformation>
            <TravelerInfoSummary>
                <AirTravelerAvail>
                    <PassengerTypeQuantity Code="ADT" Quantity="1" />
                </AirTravelerAvail>
            </TravelerInfoSummary>
        </OTA_AirAvailRQ>
    </Body>
</Envelope>

尝试过的方案

方案1:WCF客户端

使用BasicHttpBinding配置,但报错“Compiling JScript/CSharp scripts is not supported”,代码如下:

System.ServiceModel.BasicHttpBinding binding =
new System.ServiceModel.BasicHttpBinding();
binding.MaxBufferSize = int.MaxValue;
binding.ReaderQuotas = System.Xml.XmlDictionaryReaderQuotas.Max;
binding.MaxReceivedMessageSize = int.MaxValue;
binding.AllowCookies = true;
binding.Security.Mode = System.ServiceModel.BasicHttpSecurityMode.Transport;
binding.TransferMode = System.ServiceModel.TransferMode.Buffered;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;
ServiceHelper _ServiceHelper = new ServiceHelper();
var address = new EndpointAddress("https://airarabia.isaaviations.com/webservices/services/AAResWebServices");
var client = new AAResWebServicesClient((Binding)binding, address);

client.ClientCredentials.UserName.UserName = _ServiceHelper.airarabia9GUserName;
client.ClientCredentials.UserName.Password = _ServiceHelper.airarabia9Gpassword;
OTA_AirAvailRQ test = new OTA_AirAvailRQ();
OTA_AirAvailRS TEST2;
try
{
    var httpHeaders = ReturnHttpHeader(_ServiceHelper.airarabia9GUserName, _ServiceHelper.airarabia9Gpassword);
    client.Endpoint.EndpointBehaviors.Add(new HttpHeadersEndpointBehavior(httpHeaders));
    TEST2 = client.getAvailability(test);
}
catch (Exception se)
{
    Console.WriteLine("Error : " + se.Message);
    client.Abort();
    return null;
}

方案2:手动实体序列化

手动创建实体后用XmlSerializer序列化,生成的XML结构不符合要求,代码如下:

//envelope Containt all the modules and will convert them into XML
var stringwriter = new System.IO.StringWriter();
var serializer = new XmlSerializer(typeof(Envelope),
                 "http://www.opentravel.org/OTA/2003/05");
serializer.Serialize(stringwriter, envelope);
string strXML = stringwriter.ToString();
strXML = strXML.Replace("utf-16", "utf-8");

var url = _Helper.airarabia9GURL;
var client2 = new RestClient(url);
var request = new RestRequest(url, Method.POST);

#region Headers
request.AddHeader("Content-Type", "application/xml");
request.AddHeader("Cookie", "JSESSIONID=0F63BEC1C68DDEC6ADB581CC621E1B8E.demo2144");
#endregion

request.AddParameter("application/xml", Mybody, ParameterType.RequestBody);
IRestResponse response = client2.Execute(request);
Console.WriteLine(response.Content);

可行解决方法

修复WCF客户端方案

问题出在绑定类型和WSSE安全配置上,BasicHttpBinding默认不支持WS-Security,需调整配置或手动添加WSSE头:

1. 改用WSHttpBinding并配置WS-Security

// 使用支持WS-Security的WSHttpBinding
var binding = new WSHttpBinding(SecurityMode.Transport);
binding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;
binding.Security.Message.EstablishSecurityContext = false;
binding.MaxBufferSize = int.MaxValue;
binding.ReaderQuotas = XmlDictionaryReaderQuotas.Max;
binding.MaxReceivedMessageSize = int.MaxValue;
binding.AllowCookies = true;

var address = new EndpointAddress("https://airarabia.isaaviations.com/webservices/services/AAResWebServices");
var client = new AAResWebServicesClient(binding, address);

// 配置用户名密码
client.ClientCredentials.UserName.UserName = _ServiceHelper.airarabia9GUserName;
client.ClientCredentials.UserName.Password = _ServiceHelper.airarabia9Gpassword;

// 禁用脚本编译解决报错
var metadataBehavior = new ServiceMetadataBehavior { HttpGetEnabled = false };
client.Endpoint.EndpointBehaviors.Add(metadataBehavior);

try
{
    OTA_AirAvailRQ test = new OTA_AirAvailRQ();
    // 填充test的必要字段
    var TEST2 = client.getAvailability(test);
    client.Close();
    return TEST2;
}
catch (Exception se)
{
    Console.WriteLine("Error : " + se.Message);
    client.Abort();
    return null;
}

2. 手动添加WSSE消息头(兼容BasicHttpBinding)

通过自定义MessageInspector构造WSSE头:

public class WsseMessageInspector : IClientMessageInspector
{
    private readonly string _username;
    private readonly string _password;

    public WsseMessageInspector(string username, string password)
    {
        _username = username;
        _password = password;
    }

    public object BeforeSendRequest(ref Message request, IClientChannel channel)
    {
        var writer = new StringWriter();
        var xmlWriter = XmlWriter.Create(writer);

        // 构造WSSE Security头
        xmlWriter.WriteStartElement("wsse", "Security", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd");
        xmlWriter.WriteAttributeString("soap", "mustUnderstand", "http://schemas.xmlsoap.org/soap/envelope/", "1");

        xmlWriter.WriteStartElement("wsse", "UsernameToken", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd");
        xmlWriter.WriteAttributeString("wsu", "Id", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd", "XXXX");

        xmlWriter.WriteElementString("wsse", "Username", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd", _username);
        xmlWriter.WriteStartElement("wsse", "Password", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd");
        xmlWriter.WriteAttributeString("Type", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText");
        xmlWriter.WriteString(_password);
        xmlWriter.WriteEndElement();

        xmlWriter.WriteEndElement();
        xmlWriter.WriteEndElement();

        xmlWriter.Flush();
        var securityXml = writer.ToString();

        var doc = new XmlDocument();
        doc.LoadXml(securityXml);
        var header = MessageHeader.CreateHeader("Security", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd", doc.DocumentElement);
        request.Headers.Add(header);

        return null;
    }

    public void AfterReceiveReply(ref Message reply, object correlationState) { }
}

// 注册Inspector的Behavior
public class EndpointBehavior : IEndpointBehavior
{
    private readonly IClientMessageInspector _inspector;

    public EndpointBehavior(IClientMessageInspector inspector) => _inspector = inspector;

    public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) { }
    public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime) => clientRuntime.ClientMessageInspectors.Add(_inspector);
    public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) { }
    public void Validate(ServiceEndpoint endpoint) { }
}

// 使用方式
client.Endpoint.EndpointBehaviors.Add(new EndpointBehavior(new WsseMessageInspector(_ServiceHelper.airarabia9GUserName, _ServiceHelper.airarabia9Gpassword)));

修复手动序列化方案

需给实体类添加正确的XML命名空间属性,强制生成指定前缀:

1. 定义带命名空间的实体类

[XmlRoot(ElementName = "Envelope", Namespace = "http://schemas.xmlsoap.org/soap/envelope/")]
public class Envelope
{
    [XmlElement(ElementName = "Header", Namespace = "http://schemas.xmlsoap.org/soap/envelope/")]
    public Header Header { get; set; }

    [XmlElement(ElementName = "Body", Namespace = "http://schemas.xmlsoap.org/soap/envelope/")]
    public Body Body { get; set; }

    // 声明命名空间前缀
    [XmlNamespaceDeclarations]
    public XmlSerializerNamespaces Xmlns { get; set; } = new XmlSerializerNamespaces(new[]
    {
        new XmlQualifiedName("soap", "http://schemas.xmlsoap.org/soap/envelope/"),
        new XmlQualifiedName("wsse", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"),
        new XmlQualifiedName("wsu", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"),
        new XmlQualifiedName("ns1", "http://www.opentravel.org/OTA/2003/05")
    });
}

public class Header
{
    [XmlElement(ElementName = "Security", Namespace = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd")]
    public Security Security { get; set; }
}

public class Security
{
    [XmlAttribute(AttributeName = "mustUnderstand", Namespace = "http://schemas.xmlsoap.org/soap/envelope/")]
    public string MustUnderstand { get; set; } = "1";

    [XmlElement(ElementName = "UsernameToken", Namespace = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd")]
    public UsernameToken UsernameToken { get; set; }
}

public class UsernameToken
{
    [XmlAttribute(AttributeName = "Id", Namespace = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd")]
    public string Id { get; set; }

    [XmlElement(ElementName = "Username", Namespace = "http://docs.oasis-open.org/wss/2004/01/o
相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 11:01:34