由于尝试从我们所有的Web服务中删除面向方面的代码,我遇到了一个奇怪的问题。
有一个IParameterInspector,它执行验证并抛出一个在返回响应的IErrorHandler中捕获的FaultException。
ResponseBase看起来像这样:
[DataContract(Namespace = Namespaces.Data)]
public class ResponseBase
{
public ResponseBase()
{
this.Build = BuildHelper.GetBuild();
}
[DataMember(Name = CommonParameterNames.RequestId, Order = 0)]
public string RequestId { get; set; }
[DataMember(Name = CommonParameterNames.Status, Order = 1)]
public string Status { get; set; }
[DataMember(Name = CommonParameterNames.Errors, Order = 2, EmitDefaultValue = false)]
public Error[] Errors { get; set; }
[DataMember(Name = CommonParameterNames.Build, Order = 3, EmitDefaultValue = false)]
public Build Build { get; set; }
}
这是Web服务方法创建的所有响应的基类。
当从wervice方法返回而没有发生错误或验证错误时,Message在主体中包含一个包装器元素,名称为“response”,紧跟在方法名称本身的开始标记之后:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"><s:Body>
<BulkUploadImageBase64Response xmlns="http://www.mydomain.com/webapi/">
<response xmlns:a="http://www.mydomain.com/webapi/data/" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<a:request_id>
但是,如果我尝试使用Message.CreateMessage()方法处理IErrorHandler中的错误,从FaultException传递ResponseBase对象,结果会有所不同:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"><s:Body>
<BulkUploadImageResponse xmlns="http://www.mydomain.com/webapi/data/" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<request_id>e505d0d3-fc5d-474f-8ec7-5c7521c8a869</request_id>
<status>DataValidationError</status>
错误处理程序返回的消息似乎以不同的方式序列化。由于某种原因,缺少“响应”包装元素。
这是错误处理程序实现(摘录):
public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
{
if (error is FaultException<ResponseBase>)
{
var fe = error as FaultException<ResponseBase>;
fault = Message.CreateMessage(version, null, fe.Detail);
}
我已经尝试过不同的东西:将BodyReader与DataContractSerializer一起使用,将MessageContract属性添加到ResponseBase类,依此类推,但我不能让它看起来像框架隐式生成的响应消息。
非常感谢任何帮助,非常感谢!
答案 0 :(得分:0)
我知道这是一个老问题,但是我遇到了同样的问题,并设法从ProvideFault
获得了答复,以匹配通常的答复。
这是要点:
public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
{
if (error is FaultException<ResponseBase>)
{
var fe = error as FaultException<ResponseBase>;
var action = OperationContext.Current.IncomingMessageHeaders.Action;
var operation = OperationContext.Current.EndpointDispatcher.DispatchRuntime.Operations.FirstOrDefault(o => o.Action == action);
// Instantiate a serializer that will wrap the response like the service would, based on the operation name
var serializer = new FaultSerializer(operation.Name);
// Create the message
fault = Message.CreateMessage(version, operation.ReplyAction, fe.Detail, serializer);
}
}
然后,序列化器将如下所示:
using System.Runtime.Serialization;
using System.Xml;
namespace MyNamespace
{
internal class FaultSerializer: XmlObjectSerializer
{
private readonly string _actionName;
public FaultSerializer(string actionName)
{
_actionName = actionName;
}
public override bool IsStartObject(XmlDictionaryReader reader)
{
// Not used
throw new System.NotImplementedException();
}
public override object ReadObject(XmlDictionaryReader reader, bool verifyObjectName)
{
// Not used
throw new System.NotImplementedException();
}
public override void WriteEndObject(XmlDictionaryWriter writer)
{
writer.WriteEndElement();
}
public override void WriteObjectContent(XmlDictionaryWriter writer, object graph)
{
// Instead of naming the main object element with the object type,
// append Result to the action name
var xmlDictionary = new XmlDictionary();
var settings = new DataContractSerializerSettings {
RootName = xmlDictionary.Add($"{_actionName}Result")
};
var serializer = new DataContractSerializer(graph.GetType(), settings);
serializer.WriteObject(writer, graph);
}
public override void WriteStartObject(XmlDictionaryWriter writer, object graph)
{
// Wrap the entire object in a Response element
writer.WriteStartElement($"{_actionName}Response", "http://tempuri.org/");
}
}
}
希望这会有所帮助!