File: System\Data\Services\DataServiceException.cs
Project: ndp\fx\src\DataWeb\Server\System.Data.Services.csproj (System.Data.Services)
//---------------------------------------------------------------------
// <copyright file="DataServiceException.cs" company="Microsoft">
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
// <summary>
//      Base class for exceptions thrown by the web data services.
// </summary>
//
// @owner  Microsoft
//---------------------------------------------------------------------
 
namespace System.Data.Services
{
    #region Namespaces.
 
    using System;
    using System.Diagnostics;
    using System.Globalization;
    using System.Runtime.Serialization;
    using System.Security.Permissions;
 
    #endregion Namespaces.
 
    /// <summary>
    /// The exception that is thrown when an error occurs while processing
    /// a web data service request.
    /// </summary>
    /// <remarks>
    /// The DataServiceException is thrown to indicate an error during
    /// request processing, specifying the appropriate response for
    /// the request.
    /// 
    /// RFC2616 about the status code values:
    ///     1xx: Informational  - Request received, continuing process
    ///     "100"  ; Section 10.1.1: Continue
    ///     "101"  ; Section 10.1.2: Switching Protocols
    ///     
    ///     2xx: Success        - The action was successfully received, understood, and accepted
    ///     "200"  ; Section 10.2.1: OK
    ///     "201"  ; Section 10.2.2: Created
    ///     "202"  ; Section 10.2.3: Accepted
    ///     "203"  ; Section 10.2.4: Non-Authoritative Information
    ///     "204"  ; Section 10.2.5: No Content
    ///     "205"  ; Section 10.2.6: Reset Content
    ///     "206"  ; Section 10.2.7: Partial Content
    ///     
    ///     3xx: Redirection    - Further action must be taken in order to complete the request
    ///     "300"  ; Section 10.3.1: Multiple Choices
    ///     "301"  ; Section 10.3.2: Moved Permanently
    ///     "302"  ; Section 10.3.3: Found
    ///     "303"  ; Section 10.3.4: See Other
    ///     "304"  ; Section 10.3.5: Not Modified
    ///     "305"  ; Section 10.3.6: Use Proxy
    ///     "307"  ; Section 10.3.8: Temporary Redirect
    ///     
    ///     4xx: Client Error   - The request contains bad syntax or cannot be fulfilled
    ///     "400"  ; Section 10.4.1: Bad Request
    ///     "401"  ; Section 10.4.2: Unauthorized
    ///     "402"  ; Section 10.4.3: Payment Required
    ///     "403"  ; Section 10.4.4: Forbidden
    ///     "404"  ; Section 10.4.5: Not Found
    ///     "405"  ; Section 10.4.6: Method Not Allowed
    ///     "406"  ; Section 10.4.7: Not Acceptable
    ///     "407"  ; Section 10.4.8: Proxy Authentication Required
    ///     "408"  ; Section 10.4.9: Request Time-out
    ///     "409"  ; Section 10.4.10: Conflict
    ///     "410"  ; Section 10.4.11: Gone
    ///     "411"  ; Section 10.4.12: Length Required
    ///     "412"  ; Section 10.4.13: Precondition Failed
    ///     "413"  ; Section 10.4.14: Request Entity Too Large
    ///     "414"  ; Section 10.4.15: Request-URI Too Large
    ///     "415"  ; Section 10.4.16: Unsupported Media Type
    ///     "416"  ; Section 10.4.17: Requested range not satisfiable
    ///     "417"  ; Section 10.4.18: Expectation Failed
    ///     
    ///     5xx: Server Error   - The server failed to fulfill an apparently valid request
    ///     "500"  ; Section 10.5.1: Internal Server Error
    ///     "501"  ; Section 10.5.2: Not Implemented
    ///     "502"  ; Section 10.5.3: Bad Gateway
    ///     "503"  ; Section 10.5.4: Service Unavailable
    ///     "504"  ; Section 10.5.5: Gateway Time-out
    ///     "505"  ; Section 10.5.6: HTTP Version not supported
    /// </remarks>
    [Serializable]
    [DebuggerDisplay("{statusCode}: {Message}")]
    public sealed class DataServiceException : InvalidOperationException
    {
        #region Private fields.
 
        /// <summary>Language for the exception message.</summary>
        private readonly string messageLanguage;
 
        /// <summary>Error code to be used in payloads.</summary>
        private readonly string errorCode;
 
        /// <summary>HTTP response status code for this exception.</summary>
        private readonly int statusCode;
 
        /// <summary>'Allow' response for header.</summary>
        private string responseAllowHeader;
 
        #endregion Private fields.
 
        #region Constructors.
 
        /// <summary>
        /// Initializes a new instance of the DataServiceException class.
        /// </summary>
        /// <remarks>
        /// The Message property is initialized to a system-supplied message 
        /// that describes the error. This message takes into account the 
        /// current system culture. The StatusCode property is set to 500
        /// (Internal Server Error).
        /// </remarks>
        public DataServiceException()
            : this(500, Strings.DataServiceException_GeneralError)
        {
        }
 
        /// <summary>
        /// Initializes a new instance of the DataServiceException class.
        /// </summary>
        /// <param name="message">Plain text error message for this exception.</param>
        /// <remarks>
        /// The StatusCode property is set to 500 (Internal Server Error).
        /// </remarks>
        public DataServiceException(string message)
            : this(500, message)
        {
        }
 
        /// <summary>
        /// Initializes a new instance of the DataServiceException class.
        /// </summary>
        /// <param name="message">Plain text error message for this exception.</param>
        /// <param name="innerException">Exception that caused this exception to be thrown.</param>
        /// <remarks>
        /// The StatusCode property is set to 500 (Internal Server Error).
        /// </remarks>
        public DataServiceException(string message, Exception innerException)
            : this(500, null, message, null, innerException)
        {
        }
 
        /// <summary>
        /// Initializes a new instance of the DataServiceException class.
        /// </summary>
        /// <param name="statusCode">HTTP response status code for this exception.</param>
        /// <param name="message">Plain text error message for this exception.</param>
        public DataServiceException(int statusCode, string message)
            : this(statusCode, null, message, null, null)
        {
        }
 
        /// <summary>
        /// Initializes a new instance of the DataServiceException class.
        /// </summary>
        /// <param name="statusCode">HTTP response status code for this exception.</param>
        /// <param name="errorCode">Error code to be used in payloads.</param>
        /// <param name="message">Plain text error message for this exception.</param>
        /// <param name="messageXmlLang">Language of the <paramref name="message"/>.</param>
        /// <param name="innerException">Exception that caused this exception to be thrown.</param>
        public DataServiceException(int statusCode, string errorCode, string message, string messageXmlLang, Exception innerException)
            : base(message, innerException)
        {
            this.errorCode = errorCode ?? String.Empty;
            this.messageLanguage = messageXmlLang ?? CultureInfo.CurrentCulture.Name;
            this.statusCode = statusCode;
        }
 
#pragma warning disable 0628
 
        // Warning CS0628:
        // A sealed class cannot introduce a protected member because no other class will be able to inherit from the 
        // sealed class and use the protected member.
        //
        // This method is used by the runtime when deserializing an exception. It follows the standard pattern,
        // which will also be necessary when this class is subclassed by DataServiceException<T>.
 
        /// <summary>
        /// Initializes a new instance of the DataServiceException class from the 
        /// specified SerializationInfo and StreamingContext instances.
        /// </summary>
        /// <param name="serializationInfo">
        /// A SerializationInfo containing the information required to serialize 
        /// the new DataServiceException.
        /// </param>
        /// <param name="streamingContext">
        /// A StreamingContext containing the source of the serialized stream 
        /// associated with the new DataServiceException.
        /// </param>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1047", Justification = "Follows serialization info pattern.")]
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1032", Justification = "Follows serialization info pattern.")]
        protected DataServiceException(SerializationInfo serializationInfo, StreamingContext streamingContext)
            : base(serializationInfo, streamingContext)
        {
            if (serializationInfo != null)
            {
                this.errorCode = serializationInfo.GetString("errorCode");
                this.messageLanguage = serializationInfo.GetString("messageXmlLang");
                this.responseAllowHeader = serializationInfo.GetString("responseAllowHeader");
                this.statusCode = serializationInfo.GetInt32("statusCode");
            }
        }
 
#pragma warning restore 0628
 
        #endregion Constructors.
 
        #region Public properties.
 
        /// <summary>Error code to be used in payloads.</summary>
        public string ErrorCode
        {
            get { return this.errorCode; }
        }
 
        /// <summary>Language for the exception Message.</summary>
        public string MessageLanguage
        {
            get { return this.messageLanguage; }
        }
 
        /// <summary>Response status code for this exception.</summary>
        public int StatusCode
        {
            get { return this.statusCode; }
        }
 
        #endregion Public properties.
 
        #region Internal properties.
 
        /// <summary>'Allow' response for header.</summary>
        internal string ResponseAllowHeader
        {
            get { return this.responseAllowHeader; }
        }
 
        #endregion Internal properties.
 
        #region Methods.
 
        /// <summary>
        /// Sets the SerializationInfo with information about the exception.
        /// </summary>
        /// <param name="info">The SerializationInfo that holds the serialized object data about the exception being thrown.</param>
        /// <param name="context">The StreamingContext that contains contextual information about the source or destination.</param>
        /* 
         * put in an explicit LinkDemand for FullTrust because the base GetObjectData is public and SecurityCritical which translates to a LinkDemand for FullTrust
         * We add it explicitly here so that we don't get an FXCOP CA2123
         */
        [System.Security.SecurityCritical]
        [PermissionSet(SecurityAction.LinkDemand, Unrestricted = true)]
        public override void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            if (info != null)
            {
                info.AddValue("errorCode", this.errorCode);
                info.AddValue("messageXmlLang", this.messageLanguage);
                info.AddValue("responseAllowHeader", this.responseAllowHeader);
                info.AddValue("statusCode", this.statusCode);
            }
 
            base.GetObjectData(info, context);
        }
 
        /// <summary>Creates a new "Bad Request" exception for recursion limit exceeded.</summary>
        /// <param name="recursionLimit">Recursion limit that was reaced.</param>
        /// <returns>A new exception to indicate that the request is rejected.</returns>
        internal static DataServiceException CreateDeepRecursion(int recursionLimit)
        {
            return DataServiceException.CreateBadRequestError(Strings.BadRequest_DeepRecursion(recursionLimit));
        }
 
        /// <summary>Creates a new "Bad Request" exception for recursion limit exceeded.</summary>
        /// <returns>A new exception to indicate that the request is rejected.</returns>
        internal static DataServiceException CreateDeepRecursion_General()
        {
            return DataServiceException.CreateBadRequestError(Strings.BadRequest_DeepRecursion_General);
        }
 
        /// <summary>Creates a new "Forbidden" exception.</summary>
        /// <returns>A new exception to indicate that the request is forbidden.</returns>
        internal static DataServiceException CreateForbidden()
        {
            // 403: Forbidden
            return new DataServiceException(403, Strings.RequestUriProcessor_Forbidden);
        }
 
        /// <summary>Creates a new "Resource Not Found" exception.</summary>
        /// <param name="identifier">segment identifier information for which resource was not found.</param>
        /// <returns>A new exception to indicate the requested resource cannot be found.</returns>
        internal static DataServiceException CreateResourceNotFound(string identifier)
        {
            // 404: Not Found
            return new DataServiceException(404, Strings.RequestUriProcessor_ResourceNotFound(identifier));
        }
 
        /// <summary>Creates a new "Resource Not Found" exception.</summary>
        /// <param name="errorMessage">Plain text error message for this exception.</param>
        /// <returns>A new exception to indicate the requested resource cannot be found.</returns>
        internal static DataServiceException ResourceNotFoundError(string errorMessage)
        {
            // 404: Not Found
            return new DataServiceException(404, errorMessage);
        }
 
        /// <summary>Creates a new exception to indicate a syntax error.</summary>
        /// <returns>A new exception to indicate a syntax error.</returns>
        internal static DataServiceException CreateSyntaxError()
        {
            return CreateSyntaxError(Strings.RequestUriProcessor_SyntaxError);
        }
 
        /// <summary>Creates a new exception to indicate a syntax error.</summary>
        /// <param name="message">Plain text error message for this exception.</param>
        /// <returns>A new exception to indicate a syntax error.</returns>
        internal static DataServiceException CreateSyntaxError(string message)
        {
            return DataServiceException.CreateBadRequestError(message);
        }
 
        /// <summary>
        /// Creates a new exception to indicate Precondition error.
        /// </summary>
        /// <param name="message">Plain text error message for this exception.</param>
        /// <returns>A new exception to indicate a Precondition failed error.</returns>
        internal static DataServiceException CreatePreConditionFailedError(string message)
        {
            // 412 - Precondition failed
            return new DataServiceException(412, message);
        }
 
        /// <summary>
        /// Creates a new exception to indicate Precondition error.
        /// </summary>
        /// <param name="message">Plain text error message for this exception.</param>
        /// <param name="innerException">Inner Exception.</param>
        /// <returns>A new exception to indicate a Precondition failed error.</returns>
        internal static DataServiceException CreatePreConditionFailedError(string message, Exception innerException)
        {
            // 412 - Precondition failed
            return new DataServiceException(412, null, message, null, innerException);
        }
 
        /// <summary>
        /// Creates a new exception to indicate BadRequest error.
        /// </summary>
        /// <param name="message">Plain text error message for this exception.</param>
        /// <returns>A new exception to indicate a bad request error.</returns>
        internal static DataServiceException CreateBadRequestError(string message)
        {
            // 400 - Bad Request
            return new DataServiceException(400, message);
        }
 
        /// <summary>
        /// Creates a new exception to indicate BadRequest error.
        /// </summary>
        /// <param name="message">Plain text error message for this exception.</param>
        /// <param name="innerException">Inner Exception.</param>
        /// <returns>A new exception to indicate a bad request error.</returns>
        internal static DataServiceException CreateBadRequestError(string message, Exception innerException)
        {
            // 400 - Bad Request
            return new DataServiceException(400, null, message, null, innerException);
        }
 
        /// <summary>Creates a new "Method Not Allowed" exception.</summary>
        /// <param name="message">Error message.</param>
        /// <param name="allow">String value for 'Allow' header in response.</param>
        /// <returns>A new exception to indicate the requested method is not allowed on the response.</returns>
        internal static DataServiceException CreateMethodNotAllowed(string message, string allow)
        {
            // 405 - Method Not Allowed
            DataServiceException result = new DataServiceException(405, message);
            result.responseAllowHeader = allow;
            return result;
        }
 
        /// <summary>
        /// Creates a new exception to indicate MethodNotImplemented error.
        /// </summary>
        /// <param name="message">Plain text error message for this exception.</param>
        /// <returns>A new exception to indicate a MethodNotImplemented error.</returns>
        internal static DataServiceException CreateMethodNotImplemented(string message)
        {
            // 501 - Method Not Implemented
            return new DataServiceException(501, message);
        }
 
        #endregion Methods.
    }
}