【C#】标准WebService Soap1.1 兼容 ContentType: application/xml

一、问题描述

1.1 ESB平台要求

  • ContentType:application/xml
  • Soap协议版本:1.1

    1.2 提供的 WebService 接口

    • 语言:C#
    • 目标框架:.NetFramework 4.6.1

      1.3 Postman 测试结果

      HTTP Error 415.0 - Unsupported Media Type

      服务器无法为请求提供服务,因为不支持该媒体类型。

      最可能的原因:

      所请求文件的格式已由服务器配置为不可进行下载。

      可尝试的操作:

      确认所请求文件的格式有效。

      Detailed Error Information:

      ModuleManagedPipelineHandlerRequested URLhttp://localhost:55305/WebService.asmx?op=callBussiness
      NotificationExecuteRequestHandlerPhysical Path…\ESBWebService.asmx
      HandlerWebServiceHandlerFactory-Integrated-4.0Logon Method匿名
      Error Code0x00000000Logon User匿名

      More Information:

      如果服务器因不支持该文件类型而无法为请求提供服务,就会出现此错误。

      View more information »

      二、问题说明

      C# 使用创建的标准WebService 只支持以下ContentType类型

      • SOAP 1.1 : text/xml; charset=utf-8
      • SOAP 1.2 : application/soap+xml; charset=utf-8
      • HTTP POST: application/x-www-form-urlencoded

        综上所述,要想解决此问题,由以下两种途径:

        • 与ESB平台人员沟通,要求使用WebService所支持的媒体类型text/xml;
        • 自己扩展SOAP,拦截application/xml类型的请求

          三、解决方案

          3.1 与ESB平台人员沟通,要求使用WebService所支持的媒体类型text/xml;

          3.2 自己扩展SOAP,拦截application/xml类型的请求

          要在C# WebService 启动服务时支持application/xml文件类型,您可以通过在 WebService 服务代码中添加一个 SOAP 扩展来实现。

          3.2.1 扩展 SoapExtensionAttribute

          [AttributeUsage(AttributeTargets.Method)]
          public class ESBSoapExtensionAttribute : SoapExtensionAttribute
          { private int priority;
              public ESBSoapExtensionAttribute()
              { }
              public override Type ExtensionType
              { get { return typeof(ESBSoapExtension); }
              }
              public override int Priority
              { get { return priority; }
                  set { priority = value; }
              }
          }
          

          3.2.2 扩展 SoapExtension

          在ProcessMessage中判断ContentType是不是"application/xml",如果是则替换为可以被解析的"text/xml"

          public class ESBSoapExtension : SoapExtension
          { public ESBSoapExtension extension;
              public override object GetInitializer(LogicalMethodInfo methodInfo, SoapExtensionAttribute attribute)
              { return extension ?? (extension = new ESBSoapExtension());
              }
              public override object GetInitializer(Type serviceType)
              { return extension ?? (extension = new ESBSoapExtension());
              }
              public override void Initialize(object initializer)
              { //base.Initialize(initializer);
              }
              public override void ProcessMessage(SoapMessage message)
              { // 检查请求头Content-Type是否为"application/xml"
                  try
                  { switch (message.Stage)
                      { case SoapMessageStage.BeforeDeserialize:
                              if (message is SoapServerMessage serverMessage && serverMessage.ContentType.Contains("application/xml"))
                              { // 设置响应头Content-Type为"application/xml"
                                  serverMessage.ContentType = "text/xml";
                              }
                              break;
                          case SoapMessageStage.BeforeSerialize:
                              break;
                          case SoapMessageStage.AfterSerialize:
                              break;
                          case SoapMessageStage.AfterDeserialize:
                              // 在反序列化之后进行处理(响应阶段)
                              break;
                          default:
                              break;
                      }
                      
                  }
                  catch(Exception exp)
                  { }
              }
          }
          

          3.2.3 在方法callBussiness上注入扩展

           [WebMethod(Description = "调用业务")]
           [ESBSoapExtension(Priority =1)]
           public string callBussiness(string message)
           {return message;
           }
          

          ** 完整代码**

          using System;
          using System.IO;
          using System.Text;
          using System.Web;
          using System.Web.Script.Serialization;
          using System.Web.Services;
          using System.Web.Services.Protocols;
          using System.Xml;
          using System.Xml.Serialization;
          using Rss_WebServer.code;
          [AttributeUsage(AttributeTargets.Method)]
          public class ESBSoapExtensionAttribute : SoapExtensionAttribute
          { private int priority;
              public ESBSoapExtensionAttribute()
              { }
              public override Type ExtensionType
              { get { return typeof(ESBSoapExtension); }
              }
              public override int Priority
              { get { return priority; }
                  set { priority = value; }
              }
              
          }
          public class ESBSoapExtension : SoapExtension
          { public ESBSoapExtension extension;
              public override object GetInitializer(LogicalMethodInfo methodInfo, SoapExtensionAttribute attribute)
              { return extension ?? (extension = new ESBSoapExtension());
              }
              public override object GetInitializer(Type serviceType)
              { return extension ?? (extension = new ESBSoapExtension());
              }
              public override void Initialize(object initializer)
              { //base.Initialize(initializer);
              }
              public override void ProcessMessage(SoapMessage message)
              { // 检查请求头Content-Type是否为"application/xml"
                  try
                  { switch (message.Stage)
                      { case SoapMessageStage.BeforeDeserialize:
                              if (message is SoapServerMessage serverMessage && serverMessage.ContentType.Contains("application/xml"))
                              { // 设置响应头Content-Type为"application/xml"
                                  serverMessage.ContentType = "text/xml";
                              }
                              break;
                          case SoapMessageStage.BeforeSerialize:
                              break;
                          case SoapMessageStage.AfterSerialize:
                              break;
                          case SoapMessageStage.AfterDeserialize:
                              // 在反序列化之后进行处理(响应阶段)
                              break;
                          default:
                              break;
                      }
                      
                  }
                  catch(Exception exp)
                  { }
              }
          }
          namespace ESB
          { ///  /// WebService 的摘要说明
              ///  [WebService(Namespace = "http://esb.webservice")]
              [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
              [System.ComponentModel.ToolboxItem(false)]
              // 若要允许使用 ASP.NET AJAX 从脚本中调用此 Web 服务,请取消注释以下行。 
              [System.Web.Script.Services.ScriptService]
              public class WebService : System.Web.Services.WebService
              { public override object GetService(Type service)
                  { return base.GetService(service);
                  }
                  public WebService()
                  { //this.Application.sa
                      this.Context.Request.ContentType = "application/xml";
                  }
                  [WebMethod(Description = "调用业务")]
                  [ESBSoapExtension(Priority =1)]
                  public string callBussiness(string message)
                  { try
                      { if (string.IsNullOrEmpty(message))
                          { message = WebServiceAnalysis(base.Context.Request, nameof(message));
                          }
                          return message;
                      }
                      catch (Exception exp)
                      { return exp.Message;
                      }
                  }
                  ///  /// 重新解析 WebService
                  ///  ///  ///  ///  private string WebServiceAnalysis(System.Web.HttpRequest request, string name)
                  { try
                      { if (request.ContentLength == 0)
                          { throw new Exception($"Body(xml数据) 无数据");
                          }
                          // 获取请求内容
                          Stream inputStream = request.InputStream;
                          // 重新获取内容
                          inputStream.Position = 0;
                          // 读取请求主体内容
                          using (StreamReader reader = new StreamReader(inputStream, Encoding.UTF8))
                          { string requestBody = reader.ReadToEnd();
                              XmlDocument xmlDoc = new XmlDocument();
                              xmlDoc.LoadXml(requestBody);
                              XmlNode strNode = xmlDoc.SelectSingleNode($"//{name}");
                              if (strNode != null)
                              { return strNode.InnerText;
                              }
                              else
                              { throw new Exception($"未在Body(xml数据)找到{name}节点");
                              }
                          }
                      }
                      catch (Exception exp)
                      { throw exp;
                      }
                  }
              }
          }