// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Web.Http.ModelBinding;
using System.Web.Http.Properties;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
namespace System.Web.Http
{
///
/// Defines a serializable container for storing error information. This information is stored
/// as key/value pairs. The dictionary keys to look up standard error information are available
/// on the type.
///
[SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix", Justification = "This type is only a dictionary to get the right serialization format")]
[SuppressMessage("Microsoft.Usage", "CA2237:MarkISerializableTypesWithSerializable", Justification = "DCS does not support IXmlSerializable types that are also marked as [Serializable]")]
[XmlRoot("Error")]
public sealed class HttpError : Dictionary, IXmlSerializable
{
///
/// Initializes a new instance of the class.
///
public HttpError()
: base(StringComparer.OrdinalIgnoreCase)
{
}
///
/// Initializes a new instance of the class containing error message .
///
/// The error message to associate with this instance.
public HttpError(string message)
: this()
{
if (message == null)
{
throw Error.ArgumentNull("message");
}
Message = message;
}
///
/// Initializes a new instance of the class for .
///
/// The exception to use for error information.
/// true to include the exception information in the error; false otherwise
public HttpError(Exception exception, bool includeErrorDetail)
: this()
{
if (exception == null)
{
throw Error.ArgumentNull("exception");
}
Message = SRResources.ErrorOccurred;
if (includeErrorDetail)
{
Add(HttpErrorKeys.ExceptionMessageKey, exception.Message);
Add(HttpErrorKeys.ExceptionTypeKey, exception.GetType().FullName);
Add(HttpErrorKeys.StackTraceKey, exception.StackTrace);
if (exception.InnerException != null)
{
Add(HttpErrorKeys.InnerExceptionKey, new HttpError(exception.InnerException, includeErrorDetail));
}
}
}
///
/// Initializes a new instance of the class for .
///
/// The invalid model state to use for error information.
/// true to include exception messages in the error; false otherwise
public HttpError(ModelStateDictionary modelState, bool includeErrorDetail)
: this()
{
if (modelState == null)
{
throw Error.ArgumentNull("modelState");
}
if (modelState.IsValid)
{
throw Error.Argument("modelState", SRResources.ValidModelState);
}
Message = SRResources.BadRequest;
HttpError modelStateError = new HttpError();
foreach (KeyValuePair keyModelStatePair in modelState)
{
string key = keyModelStatePair.Key;
ModelErrorCollection errors = keyModelStatePair.Value.Errors;
if (errors != null && errors.Count > 0)
{
IEnumerable errorMessages = errors.Select(error =>
{
if (includeErrorDetail && error.Exception != null)
{
return error.Exception.Message;
}
else
{
return String.IsNullOrEmpty(error.ErrorMessage) ? SRResources.ErrorOccurred : error.ErrorMessage;
}
}).ToArray();
modelStateError.Add(key, errorMessages);
}
}
Add(HttpErrorKeys.ModelStateKey, modelStateError);
}
///
/// Initializes a new instance of the class containing error message
/// and error message detail .
///
/// The error message to associate with this instance.
/// The error message detail to associate with this instance.
internal HttpError(string message, string messageDetail)
: this(message)
{
if (messageDetail == null)
{
throw Error.ArgumentNull("message");
}
Add(HttpErrorKeys.MessageDetailKey, messageDetail);
}
///
/// The high-level, user-visible message explaining the cause of the error. Information carried in this field
/// should be considered public in that it will go over the wire regardless of the .
/// As a result care should be taken not to disclose sensitive information about the server or the application.
///
public string Message
{
get { return GetPropertyValue(HttpErrorKeys.MessageKey); }
set { this[HttpErrorKeys.MessageKey] = value; }
}
///
/// The containing information about the errors that occurred during model binding.
///
///
/// The inclusion of information carried in the is
/// controlled by the . All other information in the
/// should be considered public in that it will go over the wire. As a result care should be taken not to
/// disclose sensitive information about the server or the application.
///
public HttpError ModelState
{
get { return GetPropertyValue(HttpErrorKeys.ModelStateKey); }
}
///
/// A detailed description of the error intended for the developer to understand exactly what failed.
///
///
/// The inclusion of this field is controlled by the . The
/// field is expected to contain information about the server or the application that should not
/// be disclosed broadly.
///
public string MessageDetail
{
get { return GetPropertyValue(HttpErrorKeys.MessageDetailKey); }
set { this[HttpErrorKeys.MessageDetailKey] = value; }
}
///
/// The message of the if available.
///
///
/// The inclusion of this field is controlled by the . The
/// field is expected to contain information about the server or the application that should not
/// be disclosed broadly.
///
public string ExceptionMessage
{
get { return GetPropertyValue(HttpErrorKeys.ExceptionMessageKey); }
set { this[HttpErrorKeys.ExceptionMessageKey] = value; }
}
///
/// The type of the if available.
///
///
/// The inclusion of this field is controlled by the . The
/// field is expected to contain information about the server or the application that should not
/// be disclosed broadly.
///
public string ExceptionType
{
get { return GetPropertyValue(HttpErrorKeys.ExceptionTypeKey); }
set { this[HttpErrorKeys.ExceptionTypeKey] = value; }
}
///
/// The stack trace information associated with this instance if available.
///
///
/// The inclusion of this field is controlled by the . The
/// field is expected to contain information about the server or the application that should not
/// be disclosed broadly.
///
public string StackTrace
{
get { return GetPropertyValue(HttpErrorKeys.StackTraceKey); }
set { this[HttpErrorKeys.StackTraceKey] = value; }
}
///
/// The inner associated with this instance if available.
///
///
/// The inclusion of this field is controlled by the . The
/// field is expected to contain information about the server or the application that should not
/// be disclosed broadly.
///
public HttpError InnerException
{
get { return GetPropertyValue(HttpErrorKeys.InnerExceptionKey); }
}
///
/// Gets a particular property value from this error instance.
///
/// The type of the property.
/// The name of the error property.
/// The value of the error property.
public TValue GetPropertyValue(string key)
{
TValue value;
if (this.TryGetValue(key, out value))
{
return value;
}
return default(TValue);
}
XmlSchema IXmlSerializable.GetSchema()
{
return null;
}
void IXmlSerializable.ReadXml(XmlReader reader)
{
if (reader.IsEmptyElement)
{
reader.Read();
return;
}
reader.ReadStartElement();
while (reader.NodeType != System.Xml.XmlNodeType.EndElement)
{
string key = XmlConvert.DecodeName(reader.LocalName);
string value = reader.ReadInnerXml();
this.Add(key, value);
reader.MoveToContent();
}
reader.ReadEndElement();
}
void IXmlSerializable.WriteXml(XmlWriter writer)
{
foreach (KeyValuePair keyValuePair in this)
{
string key = keyValuePair.Key;
object value = keyValuePair.Value;
writer.WriteStartElement(XmlConvert.EncodeLocalName(key));
if (value != null)
{
HttpError innerError = value as HttpError;
if (innerError == null)
{
writer.WriteValue(value);
}
else
{
((IXmlSerializable)innerError).WriteXml(writer);
}
}
writer.WriteEndElement();
}
}
}
}