1

由于试图从我们所有的 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 服务方法创建的所有响应的基类。

当它从服务方法返回而没有发生错误或验证错误时,消息在正文中包含一个包装元素,该元素具有一个名为“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>

但是,如果我尝试处理 IErrorHandler 中的故障,使用 Message.CreateMessage() 方法,从 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>

从错误处理程序返回的 Message 似乎以不同的方式序列化。由于某种原因,缺少“响应”包装元素。

这是错误处理程序的实现(摘录):

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 类等等,但我不能让它看起来像框架隐式生成的响应消息。

非常感谢任何帮助,非常感谢!

4

1 回答 1

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/");
        }
    }
}

希望这可以帮助!

于 2019-08-01T19:54:15.373 回答