一、问题描述
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:
Module ManagedPipelineHandler Requested URL http://localhost:55305/WebService.asmx?op=callBussiness Notification ExecuteRequestHandler Physical Path …\ESBWebService.asmx Handler WebServiceHandlerFactory-Integrated-4.0 Logon Method 匿名 Error Code 0x00000000 Logon 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; } } } }