Skip to content
This repository was archived by the owner on Dec 14, 2018. It is now read-only.

Commit 3968df9

Browse files
committed
Fix issue #1282 - Add Request.CreateResponse extension methods
Adds the set of CreateResponse/CreateErrorResponse extension methods that return an HttpResponseMessage. For the overloads that perform content negotiation they will access the collection of MediaTypeFormatters through the shim options. Note that CreateResponse and friends use the OLD formatters. Also, HttpError and CreateErrorResponse assume ErrorDetail == false. Using the shim you will not get detailed error messages unless you construct the HttpError instance yourself.
1 parent 5a83383 commit 3968df9

File tree

11 files changed

+1581
-3
lines changed

11 files changed

+1581
-3
lines changed
Lines changed: 265 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,265 @@
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+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
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+
namespace System.Web.Http
5+
{
6+
/// <summary>
7+
/// Provides keys to look up error information stored in the <see cref="HttpError"/> dictionary.
8+
/// </summary>
9+
public static class HttpErrorKeys
10+
{
11+
/// <summary>
12+
/// Provides a key for the Message.
13+
/// </summary>
14+
public static readonly string MessageKey = "Message";
15+
16+
/// <summary>
17+
/// Provides a key for the MessageDetail.
18+
/// </summary>
19+
public static readonly string MessageDetailKey = "MessageDetail";
20+
21+
/// <summary>
22+
/// Provides a key for the ModelState.
23+
/// </summary>
24+
public static readonly string ModelStateKey = "ModelState";
25+
26+
/// <summary>
27+
/// Provides a key for the ExceptionMessage.
28+
/// </summary>
29+
public static readonly string ExceptionMessageKey = "ExceptionMessage";
30+
31+
/// <summary>
32+
/// Provides a key for the ExceptionType.
33+
/// </summary>
34+
public static readonly string ExceptionTypeKey = "ExceptionType";
35+
36+
/// <summary>
37+
/// Provides a key for the StackTrace.
38+
/// </summary>
39+
public static readonly string StackTraceKey = "StackTrace";
40+
41+
/// <summary>
42+
/// Provides a key for the InnerException.
43+
/// </summary>
44+
public static readonly string InnerExceptionKey = "InnerException";
45+
46+
/// <summary>
47+
/// Provides a key for the MessageLanguage.
48+
/// </summary>
49+
public static readonly string MessageLanguageKey = "MessageLanguage";
50+
51+
/// <summary>
52+
/// Provides a key for the ErrorCode.
53+
/// </summary>
54+
public static readonly string ErrorCodeKey = "ErrorCode";
55+
}
56+
}

0 commit comments

Comments
 (0)