1
+ // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
2
+ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3
+
4
+ using System . Collections . Generic ;
5
+ using System . Linq ;
6
+ using System . Xml ;
7
+ using System . Xml . Schema ;
8
+ using System . Xml . Serialization ;
9
+ using Microsoft . AspNet . Mvc ;
10
+ using Microsoft . AspNet . Mvc . ModelBinding ;
11
+ using ShimResources = Microsoft . AspNet . Mvc . WebApiCompatShim . Resources ;
12
+
13
+ namespace System . Web . Http
14
+ {
15
+ /// <summary>
16
+ /// Defines a serializable container for storing error information. This information is stored
17
+ /// as key/value pairs. The dictionary keys to look up standard error information are available
18
+ /// on the <see cref="HttpErrorKeys"/> type.
19
+ /// </summary>
20
+ [ XmlRoot ( "Error" ) ]
21
+ public sealed class HttpError : Dictionary < string , object > , IXmlSerializable
22
+ {
23
+ /// <summary>
24
+ /// Initializes a new instance of the <see cref="HttpError"/> class.
25
+ /// </summary>
26
+ public HttpError ( )
27
+ : base ( StringComparer . OrdinalIgnoreCase )
28
+ {
29
+ }
30
+
31
+ /// <summary>
32
+ /// Initializes a new instance of the <see cref="HttpError"/> class containing error message <paramref name="message"/>.
33
+ /// </summary>
34
+ /// <param name="message">The error message to associate with this instance.</param>
35
+ public HttpError ( [ NotNull ] string message )
36
+ : this ( )
37
+ {
38
+ Message = message ;
39
+ }
40
+
41
+ /// <summary>
42
+ /// Initializes a new instance of the <see cref="HttpError"/> class for <paramref name="exception"/>.
43
+ /// </summary>
44
+ /// <param name="exception">The exception to use for error information.</param>
45
+ /// <param name="includeErrorDetail"><c>true</c> to include the exception information in the error; <c>false</c> otherwise</param>
46
+ public HttpError ( [ NotNull ] Exception exception , bool includeErrorDetail )
47
+ : this ( )
48
+ {
49
+ Message = ShimResources . HttpError_GenericError ;
50
+
51
+ if ( includeErrorDetail )
52
+ {
53
+ Add ( HttpErrorKeys . ExceptionMessageKey , exception . Message ) ;
54
+ Add ( HttpErrorKeys . ExceptionTypeKey , exception . GetType ( ) . FullName ) ;
55
+ Add ( HttpErrorKeys . StackTraceKey , exception . StackTrace ) ;
56
+ if ( exception . InnerException != null )
57
+ {
58
+ Add ( HttpErrorKeys . InnerExceptionKey , new HttpError ( exception . InnerException , includeErrorDetail ) ) ;
59
+ }
60
+ }
61
+ }
62
+
63
+ /// <summary>
64
+ /// Initializes a new instance of the <see cref="HttpError"/> class for <paramref name="modelState"/>.
65
+ /// </summary>
66
+ /// <param name="modelState">The invalid model state to use for error information.</param>
67
+ /// <param name="includeErrorDetail"><c>true</c> to include exception messages in the error; <c>false</c> otherwise</param>
68
+ public HttpError ( [ NotNull ] ModelStateDictionary modelState , bool includeErrorDetail )
69
+ : this ( )
70
+ {
71
+ if ( modelState . IsValid )
72
+ {
73
+ throw new ArgumentException ( ShimResources . HttpError_ValidModelState , nameof ( modelState ) ) ;
74
+ }
75
+
76
+ Message = ShimResources . HttpError_BadRequest ;
77
+
78
+ var modelStateError = new HttpError ( ) ;
79
+ foreach ( KeyValuePair < string , ModelState > keyModelStatePair in modelState )
80
+ {
81
+ var key = keyModelStatePair . Key ;
82
+ var errors = keyModelStatePair . Value . Errors ;
83
+ if ( errors != null && errors . Count > 0 )
84
+ {
85
+ var errorMessages = errors . Select ( error =>
86
+ {
87
+ if ( includeErrorDetail && error . Exception != null )
88
+ {
89
+ return error . Exception . Message ;
90
+ }
91
+ else
92
+ {
93
+ return
94
+ string . IsNullOrEmpty ( error . ErrorMessage ) ?
95
+ ShimResources . HttpError_GenericError :
96
+ error . ErrorMessage ;
97
+ }
98
+ } ) . ToArray ( ) ;
99
+ modelStateError . Add ( key , errorMessages ) ;
100
+ }
101
+ }
102
+
103
+ Add ( HttpErrorKeys . ModelStateKey , modelStateError ) ;
104
+ }
105
+
106
+ /// <summary>
107
+ /// The high-level, user-visible message explaining the cause of the error. Information carried in this field
108
+ /// should be considered public in that it will go over the wire regardless of the value of error detail policy.
109
+ /// As a result care should be taken not to disclose sensitive information about the server or the application.
110
+ /// </summary>
111
+ public string Message
112
+ {
113
+ get { return GetPropertyValue < String > ( HttpErrorKeys . MessageKey ) ; }
114
+ set { this [ HttpErrorKeys . MessageKey ] = value ; }
115
+ }
116
+
117
+ /// <summary>
118
+ /// The <see cref="ModelState"/> containing information about the errors that occurred during model binding.
119
+ /// </summary>
120
+ /// <remarks>
121
+ /// The inclusion of <see cref="System.Exception"/> information carried in the <see cref="ModelState"/> is
122
+ /// controlled by the error detail policy. All other information in the <see cref="ModelState"/>
123
+ /// should be considered public in that it will go over the wire. As a result care should be taken not to
124
+ /// disclose sensitive information about the server or the application.
125
+ /// </remarks>
126
+ public HttpError ModelState
127
+ {
128
+ get { return GetPropertyValue < HttpError > ( HttpErrorKeys . ModelStateKey ) ; }
129
+ }
130
+
131
+ /// <summary>
132
+ /// A detailed description of the error intended for the developer to understand exactly what failed.
133
+ /// </summary>
134
+ /// <remarks>
135
+ /// The inclusion of this field is controlled by the error detail policy. The
136
+ /// field is expected to contain information about the server or the application that should not
137
+ /// be disclosed broadly.
138
+ /// </remarks>
139
+ public string MessageDetail
140
+ {
141
+ get { return GetPropertyValue < String > ( HttpErrorKeys . MessageDetailKey ) ; }
142
+ set { this [ HttpErrorKeys . MessageDetailKey ] = value ; }
143
+ }
144
+
145
+ /// <summary>
146
+ /// The message of the <see cref="System.Exception"/> if available.
147
+ /// </summary>
148
+ /// <remarks>
149
+ /// The inclusion of this field is controlled by the error detail policy. The
150
+ /// field is expected to contain information about the server or the application that should not
151
+ /// be disclosed broadly.
152
+ /// </remarks>
153
+ public string ExceptionMessage
154
+ {
155
+ get { return GetPropertyValue < String > ( HttpErrorKeys . ExceptionMessageKey ) ; }
156
+ set { this [ HttpErrorKeys . ExceptionMessageKey ] = value ; }
157
+ }
158
+
159
+ /// <summary>
160
+ /// The type of the <see cref="System.Exception"/> if available.
161
+ /// </summary>
162
+ /// <remarks>
163
+ /// The inclusion of this field is controlled by the error detail policy. The
164
+ /// field is expected to contain information about the server or the application that should not
165
+ /// be disclosed broadly.
166
+ /// </remarks>
167
+ public string ExceptionType
168
+ {
169
+ get { return GetPropertyValue < String > ( HttpErrorKeys . ExceptionTypeKey ) ; }
170
+ set { this [ HttpErrorKeys . ExceptionTypeKey ] = value ; }
171
+ }
172
+
173
+ /// <summary>
174
+ /// The stack trace information associated with this instance if available.
175
+ /// </summary>
176
+ /// <remarks>
177
+ /// The inclusion of this field is controlled by the error detail policy. The
178
+ /// field is expected to contain information about the server or the application that should not
179
+ /// be disclosed broadly.
180
+ /// </remarks>
181
+ public string StackTrace
182
+ {
183
+ get { return GetPropertyValue < String > ( HttpErrorKeys . StackTraceKey ) ; }
184
+ set { this [ HttpErrorKeys . StackTraceKey ] = value ; }
185
+ }
186
+
187
+ /// <summary>
188
+ /// The inner <see cref="System.Exception"/> associated with this instance if available.
189
+ /// </summary>
190
+ /// <remarks>
191
+ /// The inclusion of this field is controlled by the error detail policy. The
192
+ /// field is expected to contain information about the server or the application that should not
193
+ /// be disclosed broadly.
194
+ /// </remarks>
195
+ public HttpError InnerException
196
+ {
197
+ get { return GetPropertyValue < HttpError > ( HttpErrorKeys . InnerExceptionKey ) ; }
198
+ }
199
+
200
+ /// <summary>
201
+ /// Gets a particular property value from this error instance.
202
+ /// </summary>
203
+ /// <typeparam name="TValue">The type of the property.</typeparam>
204
+ /// <param name="key">The name of the error property.</param>
205
+ /// <returns>The value of the error property.</returns>
206
+ public TValue GetPropertyValue < TValue > ( string key )
207
+ {
208
+ object value ;
209
+ if ( TryGetValue ( key , out value ) && value is TValue )
210
+ {
211
+ return ( TValue ) value ;
212
+ }
213
+
214
+ return default ( TValue ) ;
215
+ }
216
+
217
+ XmlSchema IXmlSerializable . GetSchema ( )
218
+ {
219
+ return null ;
220
+ }
221
+
222
+ void IXmlSerializable . ReadXml ( XmlReader reader )
223
+ {
224
+ if ( reader . IsEmptyElement )
225
+ {
226
+ reader . Read ( ) ;
227
+ return ;
228
+ }
229
+
230
+ reader . ReadStartElement ( ) ;
231
+ while ( reader . NodeType != System . Xml . XmlNodeType . EndElement )
232
+ {
233
+ var key = XmlConvert . DecodeName ( reader . LocalName ) ;
234
+ var value = reader . ReadInnerXml ( ) ;
235
+
236
+ Add ( key , value ) ;
237
+ reader . MoveToContent ( ) ;
238
+ }
239
+ reader . ReadEndElement ( ) ;
240
+ }
241
+
242
+ void IXmlSerializable . WriteXml ( XmlWriter writer )
243
+ {
244
+ foreach ( var keyValuePair in this )
245
+ {
246
+ var key = keyValuePair . Key ;
247
+ var value = keyValuePair . Value ;
248
+ writer . WriteStartElement ( XmlConvert . EncodeLocalName ( key ) ) ;
249
+ if ( value != null )
250
+ {
251
+ var innerError = value as HttpError ;
252
+ if ( innerError == null )
253
+ {
254
+ writer . WriteValue ( value ) ;
255
+ }
256
+ else
257
+ {
258
+ ( ( IXmlSerializable ) innerError ) . WriteXml ( writer ) ;
259
+ }
260
+ }
261
+ writer . WriteEndElement ( ) ;
262
+ }
263
+ }
264
+ }
265
+ }
0 commit comments