将数组序列化为XML作为HttpError的一部分时出现InvalidOperationException

时间:2014-03-17 10:30:31

标签: c#-4.0 asp.net-web-api

从包含集合的InvalidOperationException创建错误响应时,有没有办法绕过HttpError

似乎只有在将响应序列化为XML时才会出现,序列化为JSON的工作绝对正常。

问题似乎与HttpError存储要在Dictionary<string, object>中序列化的数据有关。如果我添加一个自定义类(FieldError)的数组,那么它就不会正确地将其序列化为XML。但是,当我直接使用属性为FieldError类的数组的类直接序列化时,数组会正确地序列化为XML。

有没有什么方法可以让集合作为HTTP错误的一部分进行序列化?

抛出异常时得到的完整响应是:

<Error>
    <Message>An error has occurred.</Message>
    <ExceptionMessage>The 'ObjectContent`1' type failed to serialize the response body for content type 'text/xml; charset=utf-8'.</ExceptionMessage>
    <ExceptionType>System.InvalidOperationException</ExceptionType>
    <StackTrace />
    <InnerException>
        <Message>An error has occurred.</Message>
        <ExceptionMessage>Xml type 'xdt:untypedAtomic' does not support a conversion from Clr type 'FieldError' to Clr type 'String'.</ExceptionMessage>
        <ExceptionType>System.InvalidCastException</ExceptionType>
        <StackTrace>at System.Xml.Schema.XmlUntypedConverter.ChangeListType(Object value, Type destinationType, IXmlNamespaceResolver nsResolver)
            at System.Xml.Schema.XmlUntypedConverter.ChangeTypeWildcardDestination(Object value, Type destinationType, IXmlNamespaceResolver nsResolver)
            at System.Xml.Schema.XmlUntypedConverter.ToString(Object value, IXmlNamespaceResolver nsResolver)
            at System.Xml.Schema.XmlListConverter.ListAsString(IEnumerable list, IXmlNamespaceResolver nsResolver)
            at System.Xml.Schema.XmlListConverter.ChangeListType(Object value, Type destinationType, IXmlNamespaceResolver nsResolver)
            at System.Xml.Schema.XmlUntypedConverter.ChangeListType(Object value, Type destinationType, IXmlNamespaceResolver nsResolver)
            at System.Xml.Schema.XmlUntypedConverter.ChangeTypeWildcardDestination(Object value, Type destinationType, IXmlNamespaceResolver nsResolver)
            at System.Xml.Schema.XmlUntypedConverter.ToString(Object value, IXmlNamespaceResolver nsResolver)
            at System.Xml.XmlWriter.WriteValue(Object value)
            at System.Web.Http.HttpError.System.Xml.Serialization.IXmlSerializable.WriteXml(XmlWriter writer)
            at System.Runtime.Serialization.XmlObjectSerializerWriteContext.WriteIXmlSerializable(XmlWriterDelegator xmlWriter, Object obj, XmlSerializableWriter xmlSerializableWriter)
            at System.Runtime.Serialization.XmlDataContract.WriteXmlValue(XmlWriterDelegator xmlWriter, Object obj, XmlObjectSerializerWriteContext context)
            at System.Runtime.Serialization.DataContractSerializer.InternalWriteObjectContent(XmlWriterDelegator writer, Object graph, DataContractResolver dataContractResolver)
            at System.Runtime.Serialization.DataContractSerializer.InternalWriteObject(XmlWriterDelegator writer, Object graph, DataContractResolver dataContractResolver)
            at System.Runtime.Serialization.XmlObjectSerializer.WriteObjectHandleExceptions(XmlWriterDelegator writer, Object graph, DataContractResolver dataContractResolver)
            at System.Runtime.Serialization.DataContractSerializer.WriteObject(XmlWriter writer, Object graph)
            at System.Net.Http.Formatting.XmlMediaTypeFormatter.WriteToStream(Type type, Object value, Stream writeStream, HttpContent content)
            at System.Net.Http.Formatting.XmlMediaTypeFormatter.WriteToStreamAsync(Type type, Object value, Stream writeStream, HttpContent content, TransportContext transportContext, CancellationToken cancellationToken)
            --- End of stack trace from previous location where exception was thrown ---
            at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
            at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
            at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
            at System.Web.Http.WebHost.HttpControllerHandler.&lt;WriteBufferedResponseContentAsync&gt;d__1b.MoveNext()
        </StackTrace>
    </InnerException>
</Error>

我将web api设置为通过JSON和XML序列化响应:

config.Formatters.Clear();

config.Formatters.Add(new JsonMediaTypeFormatter { SerializerSettings = { NullValueHandling = NullValueHandling.Ignore } });
config.Formatters.Add(new XmlMediaTypeFormatter { UseXmlSerializer = false });

我有一个ExceptionFilterAttribute可将我的API引发的任何自定义异常转换为HttpError并返回错误响应:

public override void OnException(HttpActionExecutedContext context)
{
    var apiException = context.Exception as ApiException;
    if (apiException != null)
    {
        context.Response = context.Request.CreateErrorResponse((HttpStatusCode)apiException.ReasonCode, apiException.ToHttpError());
        return;
    }

    base.OnException(context);
}

我的自定义API异常的基类转换为HttpErrors,如下所示:

internal virtual HttpError ToHttpError()
{
    var httpError = new HttpError(this.Message) { { "ExceptionType", this.TypeName } };

    var innerApiError = this.InnerException as ApiException;
    if (innerApiError != null)
    {
        httpError.Add("InnerError", innerApiError.ToHttpError());
    }
    else if (this.InnerException != null)
    {
        httpError.Add("InnerError", new HttpError(this.InnerException.Message) { { "ExceptionType", this.InnerException.GetType().Name } });
    }

    return httpError;
}

然后我有一个派生异常类,其数组重载ToHttpError

public FieldError[] Errors { get; private set; }

internal override HttpError ToHttpError()
{
    var error = base.ToHttpError();

    error.Add("Errors", this.Errors);

    return error;
}

FieldErrors属性是一个填充数组,FieldError类声明为:

[DataContract(Namespace = "")]
public class FieldError
{
    [DataMember]
    public string Field
    {
        get;
        set;
    }

    [DataMember]
    public string Error
    {
        get;
        set;
    }

    [IgnoreDataMember]
    public ApiErrorTypes ErrorType
    {
        get;
        set;
    }
}

当我告诉API返回JSON时,我得到的响应是:

{
    "Message":"The supplied parameters are invalid.",
    "ExceptionType":"InvalidRequestDataException",
    "Errors":[
      {
        "Field":"CollectionDate",
        "Error":"Collection date is not in a valid date format"
      }
    ]
}

1 个答案:

答案 0 :(得分:0)

我找到解决此问题的唯一方法是创建一个使用DataContract的新类,并调用context.Request.CreateResponse()来创建响应,而不是使用HttpErrorcontext.Request.CreateErrorResponse()