PageRenderTime 47ms CodeModel.GetById 35ms app.highlight 7ms RepoModel.GetById 1ms app.codeStats 0ms

/WCFWebApi/src/Microsoft.ApplicationServer.Http/Microsoft/ApplicationServer/Http/Dispatcher/HttpResponseErrorHandler.cs

#
C# | 172 lines | 134 code | 25 blank | 13 comment | 21 complexity | 656cb3614dd6a69de7842d369e009df3 MD5 | raw file
  1// <copyright>
  2//   Copyright (c) Microsoft Corporation.  All rights reserved.
  3// </copyright>
  4
  5namespace Microsoft.ApplicationServer.Http.Dispatcher
  6{
  7    using System;
  8    using System.Collections.Generic;
  9    using System.Diagnostics.CodeAnalysis;
 10    using System.Net;
 11    using System.Net.Http;
 12    using System.Net.Http.Formatting;
 13    using System.Reflection;
 14    using System.ServiceModel;
 15    using System.ServiceModel.Web;
 16    using Microsoft.Server.Common;
 17
 18    internal class HttpResponseErrorHandler : HttpErrorHandler
 19    {
 20        private MediaTypeFormatterCollection formatters;
 21        private Uri helpUri;
 22        private bool includeExceptionDetail;
 23
 24        internal HttpResponseErrorHandler(IEnumerable<MediaTypeFormatter> formatters, Uri helpUri, bool includeExceptionDetail)
 25        {
 26            Fx.Assert(formatters != null, "The 'formatters' parameter should not be null.");
 27
 28            this.formatters = new MediaTypeFormatterCollection(formatters);
 29            this.helpUri = helpUri;
 30            this.includeExceptionDetail = includeExceptionDetail;
 31        }
 32
 33        /// <summary>
 34        /// Enables the creation of a custom <see cref="HttpResponseMessage"/> that is returned
 35        /// when an exception is encountered servicing an Http request.
 36        /// </summary>
 37        /// <param name="error">The exception thrown in the course of executing the Http request.</param>
 38        /// <param name="message">The <see cref="HttpResponseMessage"/> to return.  It cannot be <c>null</c>.</param>
 39        /// <returns>A value indicating whether the message is ready to be returned.</returns>
 40        [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "disposed later.")]
 41        protected override bool OnTryProvideResponse(Exception error, ref HttpResponseMessage message)
 42        {
 43            HttpResponseMessage response = null;
 44
 45            HttpResponseException errorAsResponseException = error as HttpResponseException;
 46            if (errorAsResponseException != null)
 47            {
 48                response = errorAsResponseException.Response;
 49            }
 50            else
 51            {
 52                WebFaultExceptionWrapper webFault = new WebFaultExceptionWrapper(error);
 53                if (webFault.IsWebFaultException)
 54                {
 55                    object detailObject = webFault.DetailObject;
 56                    if (detailObject != null)
 57                    {
 58                        response = detailObject as HttpResponseMessage;
 59                        if (response == null)
 60                        {
 61                            response = new HttpResponseMessage();
 62                            response.Content = new ObjectContent(webFault.DetailType, detailObject);
 63                        }
 64                    }
 65                    else
 66                    {
 67                        response = new HttpResponseMessage();
 68                    }
 69
 70                    response.StatusCode = webFault.StatusCode;
 71                }
 72                else
 73                {
 74                    response = this.CreateHtmlResponse(error);
 75                }
 76            }
 77
 78            this.PrepareHttpResponse(response);
 79            message = response;
 80            return true;
 81        }
 82
 83        private HttpResponseMessage CreateHtmlResponse(Exception error)
 84        {
 85            HttpRequestMessage requestMessage = OperationContext.Current.GetHttpRequestMessage();
 86            HttpResponseMessage responseMessage = StandardHttpResponseMessageBuilder.CreateInternalServerErrorResponse(
 87                                                                                                                    requestMessage,
 88                                                                                                                    error,
 89                                                                                                                    this.includeExceptionDetail,
 90                                                                                                                    this.helpUri);
 91
 92            return responseMessage;
 93        }
 94
 95        private void PrepareHttpResponse(HttpResponseMessage response)
 96        {
 97            ObjectContent objectContent = response.Content as ObjectContent;
 98            if (objectContent != null)
 99            {
100                foreach (MediaTypeFormatter formatter in this.formatters)
101                {
102                    objectContent.Formatters.Add(formatter);
103                }
104            }
105
106            if (response.RequestMessage == null)
107            {
108                response.RequestMessage = OperationContext.Current.GetHttpRequestMessage();
109            }
110        }
111
112        private class WebFaultExceptionWrapper
113        {
114            private static readonly Type genericWebFaultExceptionType = typeof(WebFaultException<>);
115
116            internal WebFaultExceptionWrapper(Exception error)
117            {
118                Fx.Assert(error != null, "error cannot be null");
119
120                WebFaultException asWebFaultException = error as WebFaultException;
121                Type errorType = error.GetType();
122                bool isGenericWebFaultException = errorType.IsGenericType && errorType.GetGenericTypeDefinition() == genericWebFaultExceptionType;
123
124                if (isGenericWebFaultException || asWebFaultException != null)
125                {
126                    this.IsWebFaultException = true;
127
128                    if (isGenericWebFaultException)
129                    {
130                        this.InitializeFromGenericWebFaultException(error);
131                    }
132                    else
133                    {
134                        this.InitializeFromWebFaultException(asWebFaultException);
135                    }
136                }
137            }
138
139            internal bool IsWebFaultException { get; private set; }
140
141            internal object DetailObject { get; private set; }
142
143            internal Type DetailType { get; private set; }
144
145            internal HttpStatusCode StatusCode { get; private set; }
146
147            private void InitializeFromWebFaultException(WebFaultException webFaultException)
148            {
149                this.StatusCode = webFaultException.StatusCode;
150                this.DetailObject = null;
151                this.DetailType = null;
152            }
153
154            private void InitializeFromGenericWebFaultException(Exception error)
155            {
156                Type exceptionType = error.GetType();
157                this.DetailType = exceptionType.GetGenericArguments()[0];
158
159                // The following 2 Reflection accessors only involve public API.
160                // StatusCode is defined in WebFaultException<T>.
161                PropertyInfo statusProperty = exceptionType.GetProperty("StatusCode", BindingFlags.Instance | BindingFlags.Public);
162                Fx.Assert(statusProperty != null, "Could not get StatusCode property");
163                this.StatusCode = (HttpStatusCode)statusProperty.GetValue(error, null);
164
165                // Detail is defined in FaultException<T>
166                PropertyInfo detailObjectProperty = exceptionType.GetProperty("Detail", BindingFlags.Instance | BindingFlags.Public);
167                Fx.Assert(detailObjectProperty != null, "Could not get DetailObject property");
168                this.DetailObject = detailObjectProperty.GetValue(error, null);
169            }
170        }
171    }
172}