1
+ // Copyright (c) .NET Foundation. All rights reserved.
2
+ // Licensed under the MIT License. See License.txt in the project root for license information.
3
+
4
+ using System ;
5
+ using System . Collections . Generic ;
6
+ using System . Diagnostics ;
7
+ using System . Net ;
8
+ using System . Net . Http ;
9
+ using System . Security . Cryptography ;
10
+ using System . Web . Http ;
11
+ using System . Web . Http . ExceptionHandling ;
12
+ using System . Web . Http . Results ;
13
+ using Microsoft . Azure . WebJobs . Host ;
14
+ using Microsoft . Azure . WebJobs . Host . Diagnostics ;
15
+ using Microsoft . Azure . WebJobs . Script . WebHost . Models ;
16
+ using Newtonsoft . Json ;
17
+ using ExceptionProcessor = System . Action < System . Web . Http . ExceptionHandling . ExceptionContext ,
18
+ Microsoft . Azure . WebJobs . Script . AuthorizationLevel , Microsoft . Azure . WebJobs . Script . WebHost . Models . ApiErrorModel > ;
19
+
20
+ namespace Microsoft . Azure . WebJobs . Script . WebHost . Controllers
21
+ {
22
+ public class ExceptionProcessingHandler : ExceptionHandler
23
+ {
24
+ private readonly IDictionary < Type , ExceptionProcessor > _handlers ;
25
+ private readonly HttpConfiguration _config ;
26
+ private readonly Lazy < TraceWriter > _traceWriterLoader ;
27
+
28
+ public ExceptionProcessingHandler ( HttpConfiguration config )
29
+ {
30
+ if ( config == null )
31
+ {
32
+ throw new ArgumentNullException ( "config" ) ;
33
+ }
34
+
35
+ _config = config ;
36
+ _traceWriterLoader = new Lazy < TraceWriter > ( ( ) => _config . DependencyResolver . GetService < TraceWriter > ( ) ) ;
37
+ _handlers = InitializeExceptionHandlers ( ) ;
38
+ }
39
+
40
+ private static IDictionary < Type , ExceptionProcessor > InitializeExceptionHandlers ( )
41
+ {
42
+ var handlers = new Dictionary < Type , ExceptionProcessor >
43
+ {
44
+ { typeof ( CryptographicException ) , CryptographicExceptionHandler }
45
+ } ;
46
+
47
+ return handlers ;
48
+ }
49
+
50
+ public override void Handle ( ExceptionHandlerContext context )
51
+ {
52
+ var error = new ApiErrorModel ( HttpStatusCode . InternalServerError ) ;
53
+
54
+ AuthorizationLevel currentLevel = context . Request . GetAuthorizationLevel ( ) ;
55
+ foreach ( var handler in GetExceptionHandlers ( context . Exception ) )
56
+ {
57
+ handler ( context . ExceptionContext , currentLevel , error ) ;
58
+ }
59
+
60
+ TraceErrorEvent ( context . ExceptionContext , error ) ;
61
+
62
+ context . Result = new ResponseMessageResult ( context . Request . CreateResponse ( error . StatusCode , error ) ) ;
63
+ }
64
+
65
+ private IEnumerable < ExceptionProcessor > GetExceptionHandlers ( Exception exc )
66
+ {
67
+ if ( exc == null )
68
+ {
69
+ yield break ;
70
+ }
71
+
72
+ // We return our default handler first.
73
+ // Subsequent handlers can override everything done by it.
74
+ yield return DefaultExceptionHandler ;
75
+
76
+ Type exceptionType = exc . GetType ( ) ;
77
+ ExceptionProcessor exceptionHandler = null ;
78
+ if ( exceptionType != null && _handlers . TryGetValue ( exceptionType , out exceptionHandler ) )
79
+ {
80
+ yield return exceptionHandler ;
81
+ }
82
+ }
83
+
84
+ private static void DefaultExceptionHandler ( ExceptionContext exceptionContext , AuthorizationLevel currentLevel , ApiErrorModel error )
85
+ {
86
+ if ( currentLevel == AuthorizationLevel . Admin || exceptionContext . RequestContext . IsLocal )
87
+ {
88
+ error . Message = GetExceptionMessage ( exceptionContext . Exception ) ;
89
+
90
+ if ( exceptionContext . RequestContext . IncludeErrorDetail )
91
+ {
92
+ error . ErrorDetails = ExceptionFormatter . GetFormattedException ( exceptionContext . Exception ) ;
93
+ }
94
+ }
95
+ else
96
+ {
97
+ error . Message = $ "An error has occurred. For more information, please check the logs for error ID { error . Id } ";
98
+ }
99
+ }
100
+
101
+ private void TraceErrorEvent ( ExceptionContext exceptionContext , ApiErrorModel error )
102
+ {
103
+ string controllerName = exceptionContext . ControllerContext ? . ControllerDescriptor . ControllerName ?? "<unknown>" ;
104
+ string actionName = exceptionContext . ActionContext ? . ActionDescriptor . ActionName ?? "<unknown>" ;
105
+
106
+ string message = JsonConvert . SerializeObject ( error ) ;
107
+ var traceEvent = new TraceEvent ( TraceLevel . Error , message , $ "ApiError.{ controllerName } .{ actionName } ", exceptionContext . Exception ) ;
108
+ _traceWriterLoader . Value . Trace ( traceEvent ) ;
109
+ }
110
+
111
+ private static void CryptographicExceptionHandler ( ExceptionContext exceptionContext , AuthorizationLevel currentLevel , ApiErrorModel error )
112
+ {
113
+ if ( currentLevel == AuthorizationLevel . Admin )
114
+ {
115
+ error . ErrorCode = ErrorCodes . KeyCryptographicError ;
116
+ error . Message = "Cryptographic error. Unable to encrypt or decrypt keys." ;
117
+ }
118
+ }
119
+
120
+ private static string GetExceptionMessage ( Exception exception )
121
+ {
122
+ if ( exception == null )
123
+ {
124
+ return string . Empty ;
125
+ }
126
+
127
+ var aggregateException = exception as AggregateException ;
128
+ if ( aggregateException != null )
129
+ {
130
+ exception = aggregateException . Flatten ( ) . InnerException ;
131
+ }
132
+
133
+ var messages = new List < string > ( ) ;
134
+ while ( exception != null )
135
+ {
136
+ messages . Add ( exception . Message ) ;
137
+ exception = exception . InnerException ;
138
+ }
139
+
140
+ return string . Join ( " -> " , messages ) ;
141
+ }
142
+ }
143
+ }
0 commit comments