SSRS向ServiceStack发送SOAP请求时布尔值反序列化失败
咱们先抓核心:ServiceStack没法正确识别SSRS发过来的布尔值,大概率是SOAP请求里的元素名称、格式或者命名空间和DTO的配置不匹配导致的。下面给你一步步排查和解决的方案:
1. 对齐SOAP元素名称和DTO的DataMember配置
你现在的DTO里,ApiMember的Name是"Is Enabled",但这个只是给Swagger文档用的,SOAP序列化完全看DataMember的配置。如果SSRS发送的SOAP请求里,布尔值的元素名是Is Enabled(带空格),那你得给DataMember显式指定这个名称:
[DataContract] [Route("/Stuff")] public class GetStuff : IReturn<GetStuffResponse> { // 重点:给DataMember指定和SSRS一致的元素名称 [DataMember(Name = "Is Enabled", IsRequired = false)] [ApiMember(Name = "Is Enabled", DataType = "bool", IsRequired = false)] public bool? IsEnabled { get; set; } }
如果SSRS发送的是IsEnabled(无空格),那保持DataMember默认即可,但最好显式写出来,避免歧义。
2. 检查SOAP请求里的布尔值格式
ServiceStack基于.NET的DataContractSerializer处理SOAP,它默认接受的布尔值格式是小写的true/false,一般也兼容首字母大写的True/False。但如果SSRS发送的是1/0或者带引号的字符串形式,就会出问题。
你可以抓包看一下SSRS发的SOAP请求里,这个布尔元素的实际值:
正确格式示例:
<IsEnabled>true</IsEnabled>
带命名空间的格式:<ns:IsEnabled>false</ns:IsEnabled>
如果格式不对,得调整SSRS那边的参数输出格式,让它输出标准的布尔值字符串。
3. 确认SOAP请求的命名空间匹配
如果你的DTO没有指定DataContract的Namespace,默认命名空间是http://schemas.datacontract.org/2004/07/[你的项目命名空间]。你需要确保SSRS发送的SOAP请求里,元素所在的命名空间和这个一致。
如果想自定义命名空间,直接给DataContract加上:
[DataContract(Namespace = "http://your-service-namespace.com")] [Route("/Stuff")] public class GetStuff : IReturn<GetStuffResponse> { [DataMember(Name = "IsEnabled")] public bool? IsEnabled { get; set; } }
然后让SSRS的请求使用这个命名空间。
4. 开启ServiceStack的调试日志排查
要是还是找不到问题,就开启ServiceStack的请求日志,看看完整的SOAP请求内容,方便定位哪里不匹配。在你的AppHost的Configure方法里加上:
public override void Configure(Container container) { SetConfig(new HostConfig { DebugMode = true, LogAllRequests = true // 记录所有请求的详细内容 }); }
之后查看ServiceStack的日志,就能看到SSRS发过来的原始SOAP请求,对比你的DTO配置,很快就能找到问题点。
5. 极端情况:自定义转换逻辑
如果SSRS那边实在没法调整输出格式,比如只能发1/0,那可以在DTO里加一个过渡的字符串属性,再转换为bool:
[DataContract] [Route("/Stuff")] public class GetStuff : IReturn<GetStuffResponse> { [DataMember(Name = "IsEnabled")] public string IsEnabledRaw { get; set; } // 忽略这个属性的SOAP序列化,只做逻辑转换 [IgnoreDataMember] public bool? IsEnabled { get { if (string.IsNullOrEmpty(IsEnabledRaw)) return null; return IsEnabledRaw.Equals("1") || IsEnabledRaw.Equals("true", StringComparison.OrdinalIgnoreCase); } } }
不过这是无奈之举,优先还是让两边的格式对齐更靠谱。
内容的提问来源于stack exchange,提问作者Bitfiddler

