C# SOAP 服务方法接收空参数而不是
C# SOAP service method receives null parameter instead of
作为我工作的一部分,我要编写一个基于 SOAP 1.2 的 Web 服务来接收和处理 XML 请求。然而,在使用远程客户端进行测试期间,我 运行 遇到了一个相当令人费解且不一致的问题。
这是服务的可调用方法之一的签名:
[WebMethod]
[ScriptMethod(UseHttpGet = false, ResponseFormat = ResponseFormat.Xml)]
public XmlDocument PostOrders(XmlDocument request)
这里的问题是:
- 当从我编写的极简测试应用程序调用该方法时,它工作正常。
- 当我的客户端的基于 php 的应用程序调用该方法时,该方法被传递了一个 null
request
并相应地在该应用程序尝试编写它的代码时抛出一个 NullReferenceException
文件的内容。客户端实际发送的内容无关紧要,将 Visual Studio 附加到 IIS 进程表明该方法是使用空值 request
调用的,而客户端实际发送的内容已丢失在 .NET 的某个地方等级。
我尝试了什么:
- 独立测试。 This 产生了同样的问题 - 但正确接收了复制粘贴到 SoapUI 中的相同请求。这就是为什么我上面说这个问题是不一致的:四个应用程序中的两个,每个应用程序在不同的计算机上重现该问题,另外两个则没有。
- 显然,如果服务器需要 SOAP 1.2 但接收到 1.1,则可能会发生这种情况,并且我能够通过在 SoapUI 中故意将请求发布为 1.1 来重现它。因此,我在
web.config
中禁用了 1.1 和基本 HTTP(验证两者都从 WSDL 中消失,只留下 1.2)并让客户端在他们这边明确定义版本 1.2。没有骰子。
- 修改服务后,方法的客户端 WSDL 定义突然从
XmlDocument
切换到 Linq XElement
。由于发现 WSDL 抽象了多平台兼容性的预期数据类型,我怀疑服务器端反序列化问题,我将方法的参数从 XmlDocument
更改为 XElement
。没有骰子。
web.config
的内容:
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.5.2" />
<httpRuntime />
<webServices>
<protocols>
<remove name="HttpGet" />
<remove name="HttpPost" />
<remove name="HttpSoap"/> <!-- disables SOAP 1.1 -->
</protocols>
<conformanceWarnings>
<remove name='BasicProfile1_1'/>
</conformanceWarnings>
</webServices>
<globalization uiCulture="en-US" />
<customErrors mode="Off" />
<pages controlRenderingCompatibilityVersion="3.5" clientIDMode="AutoID" />
<httpModules>
<add name="ApplicationInsightsWebTracking" type="Microsoft.ApplicationInsights.Web.ApplicationInsightsHttpModule, Microsoft.AI.Web" />
</httpModules>
</system.web>
<system.codedom>
<compilers>
<compiler language="c#;cs;csharp" extension=".cs" type="Microsoft.CodeDom.Providers.DotNetCompilerPlatform.CSharpCodeProvider, Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
<providerOption name="CompilerVersion" value="v4.0" />
</compiler>
<compiler language="vb;vbs;visualbasic;vbscript" extension=".vb" type="Microsoft.VisualBasic.VBCodeProvider, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" warningLevel="4" compilerOptions="/langversion:14 /nowarn:41008 /define:_MYTYPE=\"Web\" /optionInfer+" />
</compilers>
</system.codedom>
<system.webServer>
<modules>
<remove name="ApplicationInsightsWebTracking" />
<add name="ApplicationInsightsWebTracking" type="Microsoft.ApplicationInsights.Web.ApplicationInsightsHttpModule, Microsoft.AI.Web" preCondition="managedHandler" />
</modules>
<handlers>
<remove name="ExtensionlessUrlHandler-Integrated-4.0" />
<remove name="OPTIONSVerbHandler" />
<remove name="TRACEVerbHandler" />
<add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
</handlers>
<validation validateIntegratedModeConfiguration="false" />
</system.webServer>
<system.serviceModel>
<bindings />
<client />
</system.serviceModel>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
考虑到我能够在两个单独的应用程序中从两台单独的计算机成功发送请求而无需任何手动配置,但不是来自另外两个,这是服务器端问题还是客户端配置错误?
找到解决方案并发布它以防其他人遇到此问题。
这是一个 server-side 错误。如果您查看我上面发布的方法的签名:
public XmlDocument PostOrders(XmlDocument request)
问题是参数是 XmlDocument
。由于 SOAP 请求的有效负载已经封装在 XML 中,因此无法将其反序列化为 XmlDocument
,因为有效负载的根元素不是它来自的根元素,只能作为 XmlElement
或 Linq XElement
。然而,即使更改参数类型也不起作用 - 那是我查看 WSDL 的时候。
事实证明,.NET XML 类型都不起作用,因为即使我选择了可序列化的 class (XmlElement
/XElement
) 生成的 WSDL 也没有无法识别它们并且无法弄清楚该方法需要什么样的数据,因此 WSDL 没有声明参数的数据类型。由于没有在 WSDL 中声明数据类型,client-side 应用程序也不知道要发送什么,因此它切断了有效负载并仅发送 SOAP header。我不知道为什么 php 的 SOAP class 会那样工作,但他们显然是这样。
我在测试期间没有注意到它,因为我的测试应用程序也是 .NET,因此自动知道要发送什么,而 SoapUI 的请求是原始字符串,在发送之前没有任何序列化,所以它既不需要也不关心数据类型.
解决方案是将参数类型更改为 string
并禁用基本 HTTP。虽然启用了基本 HTTP,但当使用字符串参数调用方法时,反序列化总是抛出异常,因此我没有早点找到这个解决方案。
底线:如果您编写的 .NET Web 服务并非专门用于 .NET 客户端,请不要使用 .NET classes 作为输入参数,无论它们是否可序列化。使用字符串。你会省去很多头痛的事情。
作为我工作的一部分,我要编写一个基于 SOAP 1.2 的 Web 服务来接收和处理 XML 请求。然而,在使用远程客户端进行测试期间,我 运行 遇到了一个相当令人费解且不一致的问题。
这是服务的可调用方法之一的签名:
[WebMethod]
[ScriptMethod(UseHttpGet = false, ResponseFormat = ResponseFormat.Xml)]
public XmlDocument PostOrders(XmlDocument request)
这里的问题是:
- 当从我编写的极简测试应用程序调用该方法时,它工作正常。
- 当我的客户端的基于 php 的应用程序调用该方法时,该方法被传递了一个 null
request
并相应地在该应用程序尝试编写它的代码时抛出一个NullReferenceException
文件的内容。客户端实际发送的内容无关紧要,将 Visual Studio 附加到 IIS 进程表明该方法是使用空值request
调用的,而客户端实际发送的内容已丢失在 .NET 的某个地方等级。
我尝试了什么:
- 独立测试。 This 产生了同样的问题 - 但正确接收了复制粘贴到 SoapUI 中的相同请求。这就是为什么我上面说这个问题是不一致的:四个应用程序中的两个,每个应用程序在不同的计算机上重现该问题,另外两个则没有。
- 显然,如果服务器需要 SOAP 1.2 但接收到 1.1,则可能会发生这种情况,并且我能够通过在 SoapUI 中故意将请求发布为 1.1 来重现它。因此,我在
web.config
中禁用了 1.1 和基本 HTTP(验证两者都从 WSDL 中消失,只留下 1.2)并让客户端在他们这边明确定义版本 1.2。没有骰子。 - 修改服务后,方法的客户端 WSDL 定义突然从
XmlDocument
切换到 LinqXElement
。由于发现 WSDL 抽象了多平台兼容性的预期数据类型,我怀疑服务器端反序列化问题,我将方法的参数从XmlDocument
更改为XElement
。没有骰子。
web.config
的内容:
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.5.2" />
<httpRuntime />
<webServices>
<protocols>
<remove name="HttpGet" />
<remove name="HttpPost" />
<remove name="HttpSoap"/> <!-- disables SOAP 1.1 -->
</protocols>
<conformanceWarnings>
<remove name='BasicProfile1_1'/>
</conformanceWarnings>
</webServices>
<globalization uiCulture="en-US" />
<customErrors mode="Off" />
<pages controlRenderingCompatibilityVersion="3.5" clientIDMode="AutoID" />
<httpModules>
<add name="ApplicationInsightsWebTracking" type="Microsoft.ApplicationInsights.Web.ApplicationInsightsHttpModule, Microsoft.AI.Web" />
</httpModules>
</system.web>
<system.codedom>
<compilers>
<compiler language="c#;cs;csharp" extension=".cs" type="Microsoft.CodeDom.Providers.DotNetCompilerPlatform.CSharpCodeProvider, Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
<providerOption name="CompilerVersion" value="v4.0" />
</compiler>
<compiler language="vb;vbs;visualbasic;vbscript" extension=".vb" type="Microsoft.VisualBasic.VBCodeProvider, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" warningLevel="4" compilerOptions="/langversion:14 /nowarn:41008 /define:_MYTYPE=\"Web\" /optionInfer+" />
</compilers>
</system.codedom>
<system.webServer>
<modules>
<remove name="ApplicationInsightsWebTracking" />
<add name="ApplicationInsightsWebTracking" type="Microsoft.ApplicationInsights.Web.ApplicationInsightsHttpModule, Microsoft.AI.Web" preCondition="managedHandler" />
</modules>
<handlers>
<remove name="ExtensionlessUrlHandler-Integrated-4.0" />
<remove name="OPTIONSVerbHandler" />
<remove name="TRACEVerbHandler" />
<add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
</handlers>
<validation validateIntegratedModeConfiguration="false" />
</system.webServer>
<system.serviceModel>
<bindings />
<client />
</system.serviceModel>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
考虑到我能够在两个单独的应用程序中从两台单独的计算机成功发送请求而无需任何手动配置,但不是来自另外两个,这是服务器端问题还是客户端配置错误?
找到解决方案并发布它以防其他人遇到此问题。
这是一个 server-side 错误。如果您查看我上面发布的方法的签名:
public XmlDocument PostOrders(XmlDocument request)
问题是参数是 XmlDocument
。由于 SOAP 请求的有效负载已经封装在 XML 中,因此无法将其反序列化为 XmlDocument
,因为有效负载的根元素不是它来自的根元素,只能作为 XmlElement
或 Linq XElement
。然而,即使更改参数类型也不起作用 - 那是我查看 WSDL 的时候。
事实证明,.NET XML 类型都不起作用,因为即使我选择了可序列化的 class (XmlElement
/XElement
) 生成的 WSDL 也没有无法识别它们并且无法弄清楚该方法需要什么样的数据,因此 WSDL 没有声明参数的数据类型。由于没有在 WSDL 中声明数据类型,client-side 应用程序也不知道要发送什么,因此它切断了有效负载并仅发送 SOAP header。我不知道为什么 php 的 SOAP class 会那样工作,但他们显然是这样。
我在测试期间没有注意到它,因为我的测试应用程序也是 .NET,因此自动知道要发送什么,而 SoapUI 的请求是原始字符串,在发送之前没有任何序列化,所以它既不需要也不关心数据类型.
解决方案是将参数类型更改为 string
并禁用基本 HTTP。虽然启用了基本 HTTP,但当使用字符串参数调用方法时,反序列化总是抛出异常,因此我没有早点找到这个解决方案。
底线:如果您编写的 .NET Web 服务并非专门用于 .NET 客户端,请不要使用 .NET classes 作为输入参数,无论它们是否可序列化。使用字符串。你会省去很多头痛的事情。