PageRenderTime 44ms CodeModel.GetById 15ms RepoModel.GetById 1ms app.codeStats 0ms

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

#
C# | 457 lines | 276 code | 39 blank | 142 comment | 56 complexity | 900ca661fb86c4f2d7ac0f74ad0884e3 MD5 | raw file
Possible License(s): CC-BY-SA-3.0, Apache-2.0
  1. // <copyright>
  2. // Copyright (c) Microsoft Corporation. All rights reserved.
  3. // </copyright>
  4. namespace Microsoft.ApplicationServer.Http.Dispatcher
  5. {
  6. using System;
  7. using System.Collections.Generic;
  8. using System.Diagnostics.CodeAnalysis;
  9. using System.Net;
  10. using System.Net.Http;
  11. using System.Net.Http.Formatting;
  12. using System.Reflection;
  13. using Microsoft.ApplicationServer.Http.Description;
  14. using Microsoft.Server.Common;
  15. /// <summary>
  16. /// A <see cref="HttpOperationHandler"/> that takes in a instance of some type
  17. /// and returns an <see cref="HttpResponseMessage"/> instance of that same type.
  18. /// </summary>
  19. public class ResponseContentHandler : HttpOperationHandler
  20. {
  21. private static readonly HttpResponseMessageConverter simpleHttpResponseMessageConverter = new SimpleHttpResponseMessageConverter();
  22. private static readonly HttpResponseMessageConverter httpContentMessageConverter = new HttpContentMessageConverter();
  23. private static readonly HttpResponseMessageConverter voidHttpResponseMessageConverter = new VoidHttpResponseMessageConverter();
  24. private static readonly Type httpResponseMessageConverterGenericType = typeof(HttpResponseMessageConverter<>);
  25. private static readonly Type responseContentHandlerType = typeof(ResponseContentHandler);
  26. private HttpParameter outputParameter;
  27. private HttpParameter inputParameter;
  28. private HttpResponseMessageConverter responseMessageConverter;
  29. private bool isVoidResponseType;
  30. /// <summary>
  31. /// Initializes a new instance of a <see cref="ResponseContentHandler"/> with the
  32. /// given <paramref name="responseContentParameter"/> and <paramref name="formatters"/>.
  33. /// </summary>
  34. /// <param name="responseContentParameter">The <see cref="HttpParameter"/> for the content of the response.</param>
  35. /// <param name="formatters">The collection of <see cref="MediaTypeFormatter"/> instances to use for deserializing the response content.</param>
  36. public ResponseContentHandler(HttpParameter responseContentParameter, IEnumerable<MediaTypeFormatter> formatters)
  37. {
  38. if (formatters == null)
  39. {
  40. throw Fx.Exception.ArgumentNull("formatters");
  41. }
  42. Type paramType = responseContentParameter == null ?
  43. TypeHelper.VoidType :
  44. responseContentParameter.ParameterType;
  45. if (paramType == TypeHelper.VoidType)
  46. {
  47. this.isVoidResponseType = true;
  48. this.responseMessageConverter = voidHttpResponseMessageConverter;
  49. this.outputParameter = HttpParameter.ResponseMessage;
  50. }
  51. else
  52. {
  53. paramType = HttpTypeHelper.GetHttpResponseOrContentInnerTypeOrNull(paramType) ?? paramType;
  54. if (HttpTypeHelper.IsHttpRequest(paramType))
  55. {
  56. throw Fx.Exception.AsError(
  57. new InvalidOperationException(
  58. Http.SR.InvalidParameterForContentHandler(
  59. HttpParameter.HttpParameterType.Name,
  60. responseContentParameter.Name,
  61. responseContentParameter.ParameterType.Name,
  62. responseContentHandlerType.Name)));
  63. }
  64. Type outputParameterType;
  65. if (HttpTypeHelper.IsHttp(paramType))
  66. {
  67. // HttpContent and non-generic ObjectContent will be embedded into an HttpResponseMessage
  68. outputParameterType = HttpTypeHelper.IsHttpContent(paramType)
  69. ? HttpTypeHelper.HttpResponseMessageType
  70. : paramType;
  71. }
  72. else
  73. {
  74. outputParameterType = HttpTypeHelper.MakeHttpResponseMessageOf(paramType);
  75. }
  76. this.outputParameter = new HttpParameter(responseContentParameter.Name, outputParameterType);
  77. this.inputParameter = responseContentParameter;
  78. if (HttpTypeHelper.IsHttpResponse(paramType))
  79. {
  80. this.responseMessageConverter = simpleHttpResponseMessageConverter;
  81. }
  82. else if (HttpTypeHelper.IsHttpContent(paramType))
  83. {
  84. this.responseMessageConverter = httpContentMessageConverter;
  85. }
  86. else
  87. {
  88. Type closedConverterType = httpResponseMessageConverterGenericType.MakeGenericType(new Type[] { paramType });
  89. ConstructorInfo constructor = closedConverterType.GetConstructor(Type.EmptyTypes);
  90. this.responseMessageConverter = constructor.Invoke(null) as HttpResponseMessageConverter;
  91. }
  92. }
  93. this.Formatters = new MediaTypeFormatterCollection(formatters);
  94. }
  95. /// <summary>
  96. /// Gets the default <see cref="MediaTypeFormatter"/> instances to use for the <see cref="HttpResponseMessage"/>
  97. /// instances created by the <see cref="ResponseContentHandler"/>.
  98. /// </summary>
  99. public MediaTypeFormatterCollection Formatters { get; private set; }
  100. /// <summary>
  101. /// Retrieves the collection of <see cref="HttpParameter"/> instances describing the
  102. /// input values for this <see cref="ResponseContentHandler"/>.
  103. /// </summary>
  104. /// <remarks>
  105. /// The <see cref="UriTemplateHandler"/> always returns a single input of
  106. /// <see cref="HttpParameter.ResponseMessage"/>.
  107. /// </remarks>
  108. /// <returns>A collection that consists of just the <see cref="HttpParameter.ResponseMessage"/>.</returns>
  109. protected override sealed IEnumerable<HttpParameter> OnGetInputParameters()
  110. {
  111. return this.inputParameter != null ?
  112. new HttpParameter[] { HttpParameter.RequestMessage, this.inputParameter } :
  113. new HttpParameter[] { HttpParameter.RequestMessage };
  114. }
  115. /// <summary>
  116. /// Retrieves the collection of <see cref="HttpParameter"/>s describing the
  117. /// output values of this <see cref="ResponseContentHandler"/>.
  118. /// </summary>
  119. /// <remarks>
  120. /// The <see cref="UriTemplateHandler"/> always returns the <see cref="HttpParameter"/>
  121. /// instance that was passed into the constructor of the <see cref="ResponseContentHandler"/>.
  122. /// </remarks>
  123. /// <returns>
  124. /// A collection that consists of just the <see cref="HttpParameter"/>
  125. /// instance that was passed into the constructor of the <see cref="ResponseContentHandler"/>.
  126. /// </returns>
  127. protected override sealed IEnumerable<HttpParameter> OnGetOutputParameters()
  128. {
  129. return new HttpParameter[] { this.outputParameter };
  130. }
  131. /// <summary>
  132. /// Called to execute this <see cref="ResponseContentHandler"/>.
  133. /// </summary>
  134. /// <param name="input">
  135. /// The input values to handle corresponding to the <see cref="HttpParameter"/>
  136. /// returned by <see cref="OnGetInputParameters"/>.
  137. /// </param>
  138. /// <returns>
  139. /// The output values corresponding to the <see cref="HttpParameter"/> returned
  140. /// by <see cref="OnGetOutputParameters"/>.
  141. /// </returns>
  142. [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "disposed later.")]
  143. protected override sealed object[] OnHandle(object[] input)
  144. {
  145. Fx.Assert(input != null, "The 'input' parameter should not be null.");
  146. #if DEBUG
  147. // input[0] is always the HttpRequestMessage
  148. // input[1] exists only for non-void types and contains the raw output from the service operation.
  149. if (this.isVoidResponseType)
  150. {
  151. Fx.Assert(input.Length == 1, "A void response should have exactly one element in the 'input' array");
  152. }
  153. else
  154. {
  155. Fx.Assert(input.Length == 2, "A non-void response should have exactly 2 elements in the 'input' array");
  156. }
  157. #endif
  158. HttpRequestMessage requestMessage = input[0] as HttpRequestMessage;
  159. if (requestMessage == null)
  160. {
  161. throw Fx.Exception.ArgumentNull(HttpParameter.ResponseMessage.Name);
  162. }
  163. // Void type creates an empty HttpResponseMessage with OK status.
  164. // Non-void types create an HttpResponseMessage<T> converted from the output of the operation.
  165. HttpResponseMessage convertedResponseMessage = this.isVoidResponseType
  166. ? new HttpResponseMessage() { RequestMessage = requestMessage }
  167. : this.responseMessageConverter.Convert(requestMessage, input[1], this.Formatters);
  168. return new object[] { convertedResponseMessage };
  169. }
  170. /// <summary>
  171. /// Abstract base class used by the <see cref="ResponseContentHandler"/> to create
  172. /// <see cref="HttpResponseMessage"/> instances for a particular type
  173. /// without the performance hit of using reflection for every new instance.
  174. /// </summary>
  175. private abstract class HttpResponseMessageConverter
  176. {
  177. /// <summary>
  178. /// Base abstract method that is overridden by the <see cref="HttpResponseMessageConverter"/>
  179. /// to convert an <see cref="HttpResponseMessage"/> into an <see cref="HttpResponseMessage"/> of
  180. /// a particular type.
  181. /// </summary>
  182. /// <param name="requestMessage">The <see cref="HttpRequestMessage"/> to attach to the converted <see cref="HttpResponseMessage"/>.</param>
  183. /// <param name="responseContent">The content of the response message.</param>
  184. /// <param name="formatters">The <see cref="MediaTypeFormatter"/> collection to use with the <see cref="ObjectContent"/>
  185. /// used by the converted <see cref="HttpResponseMessageConverter"/>.</param>
  186. /// <returns>
  187. /// The converted <see cref="HttpResponseMessageConverter"/>.
  188. /// </returns>
  189. public abstract HttpResponseMessage Convert(HttpRequestMessage requestMessage, object responseContent, IEnumerable<MediaTypeFormatter> formatters);
  190. }
  191. /// <summary>
  192. /// An <see cref="HttpResponseMessageConverter"/> that is only used when the response content is a non-generic <see cref="HttpResponseMessage"/>.
  193. /// </summary>
  194. private class SimpleHttpResponseMessageConverter : HttpResponseMessageConverter
  195. {
  196. /// <summary>
  197. /// Overridden method that simply sets the <see cref="HttpResponseMessage.RequestMessage"/> on the <see cref="HttpResponseMessage"/> instance that
  198. /// is already provided as the <paramref name="responseContent"/>.
  199. /// </summary>
  200. /// <param name="requestMessage">The <see cref="HttpRequestMessage"/> to attach to the <see cref="HttpResponseMessage"/>.</param>
  201. /// <param name="responseContent">The response message.</param>
  202. /// <param name="formatters">The <see cref="MediaTypeFormatter"/> collection to use with the <see cref="ObjectContent"/>
  203. /// used by the converted <see cref="HttpResponseMessageConverter"/>.</param>
  204. /// <returns>
  205. /// The <see cref="HttpResponseMessage"/>.
  206. /// </returns>
  207. [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "caller becomes owner.")]
  208. public override HttpResponseMessage Convert(HttpRequestMessage requestMessage, object responseContent, IEnumerable<MediaTypeFormatter> formatters)
  209. {
  210. HttpResponseMessage response = responseContent as HttpResponseMessage;
  211. if (response == null)
  212. {
  213. response = new HttpResponseMessage();
  214. }
  215. else
  216. {
  217. ObjectContent objectContent = response.Content as ObjectContent;
  218. if (objectContent != null)
  219. {
  220. foreach (MediaTypeFormatter formatter in formatters)
  221. {
  222. objectContent.Formatters.Add(formatter);
  223. }
  224. }
  225. }
  226. response.RequestMessage = requestMessage;
  227. return response;
  228. }
  229. }
  230. /// <summary>
  231. /// An <see cref="VoidHttpResponseMessageConverter"/> that is only used when the response content is a void.
  232. /// </summary>
  233. private class VoidHttpResponseMessageConverter : HttpResponseMessageConverter
  234. {
  235. /// <summary>
  236. /// Overridden method that simply sets the HttpReponseMessage.RequestMessage on a new <see cref="HttpResponseMessage"/> instance.
  237. /// </summary>
  238. /// <param name="requestMessage">The <see cref="HttpRequestMessage"/> to attach to the <see cref="HttpResponseMessage"/>.</param>
  239. /// <param name="responseContent">The value is always null.</param>
  240. /// <param name="formatters">The value is not used.</param>
  241. /// <returns>
  242. /// The <see cref="HttpResponseMessage"/>.
  243. /// </returns>
  244. [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "caller becomes owner.")]
  245. public override HttpResponseMessage Convert(HttpRequestMessage requestMessage, object responseContent, IEnumerable<MediaTypeFormatter> formatters)
  246. {
  247. HttpResponseMessage response = new HttpResponseMessage();
  248. response.RequestMessage = requestMessage;
  249. return response;
  250. }
  251. }
  252. /// <summary>
  253. /// An <see cref="HttpResponseMessageConverter"/> that is only used when the response content is a an <see cref="HttpContent"/>.
  254. /// </summary>
  255. private class HttpContentMessageConverter : HttpResponseMessageConverter
  256. {
  257. /// <summary>
  258. /// Overriden method that simply creates a new the <see cref="HttpResponseMessage"/> instance and sets the <see cref="HttpContent"/> that
  259. /// is already provided as the <paramref name="responseContent"/>.
  260. /// </summary>
  261. /// <param name="requestMessage">The <see cref="HttpRequestMessage"/> to attach to the <see cref="HttpResponseMessage"/>.</param>
  262. /// <param name="responseContent">The response message content.</param>
  263. /// <param name="formatters">The <see cref="MediaTypeFormatter"/> collection to use with the <see cref="ObjectContent"/>
  264. /// used by the converted <see cref="HttpResponseMessageConverter"/>.</param>
  265. /// <returns>
  266. /// The <see cref="HttpResponseMessage"/>.
  267. /// </returns>
  268. [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "caller becomes owner.")]
  269. public override HttpResponseMessage Convert(HttpRequestMessage requestMessage, object responseContent, IEnumerable<MediaTypeFormatter> formatters)
  270. {
  271. HttpResponseMessage response = new HttpResponseMessage();
  272. response.Content = responseContent as HttpContent;
  273. response.RequestMessage = requestMessage;
  274. ObjectContent objectContent = response.Content as ObjectContent;
  275. if (objectContent != null)
  276. {
  277. foreach (MediaTypeFormatter formatter in formatters)
  278. {
  279. objectContent.Formatters.Add(formatter);
  280. }
  281. }
  282. return response;
  283. }
  284. }
  285. /// <summary>
  286. /// Generic version of the <see cref="HttpResponseMessageConverter"/> used by the
  287. /// <see cref="ResponseContentHandler"/> to create <see cref="HttpResponseMessage"/> instances
  288. /// for a particular <typeparamref name="T"/> without the performance hit of using reflection
  289. /// for every new instance.
  290. /// </summary>
  291. /// <typeparam name="T">The type with which to create new <see cref="HttpResponseMessage"/> instances.</typeparam>
  292. private class HttpResponseMessageConverter<T> : HttpResponseMessageConverter
  293. {
  294. /// <summary>
  295. /// Converts an <see cref="HttpResponseMessage"/> into an <see cref="HttpResponseMessage"/> of
  296. /// a particular type.
  297. /// </summary>
  298. /// <param name="requestMessage">The <see cref="HttpRequestMessage"/> to attach to the converted <see cref="HttpResponseMessage"/>.</param>
  299. /// <param name="responseContent">The content of the response message.</param>
  300. /// <param name="formatters">The <see cref="MediaTypeFormatter"/> collection to use with the <see cref="ObjectContent"/>
  301. /// used by the converted <see cref="HttpResponseMessageConverter"/>.</param>
  302. /// <returns>
  303. /// The converted <see cref="HttpResponseMessage"/>.
  304. /// </returns>
  305. [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "caller becomes owner.")]
  306. [SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily", Justification = "Casting is limited to two times due to the if statements.")]
  307. public override HttpResponseMessage Convert(HttpRequestMessage requestMessage, object responseContent, IEnumerable<MediaTypeFormatter> formatters)
  308. {
  309. Fx.Assert(requestMessage != null, "The 'requestMessage' parameter should not be null.");
  310. HttpResponseMessage<T> convertedResponseMessage = null;
  311. if (responseContent == null || responseContent is T)
  312. {
  313. convertedResponseMessage = new HttpResponseMessage<T>((T)responseContent, formatters);
  314. convertedResponseMessage.RequestMessage = requestMessage;
  315. return convertedResponseMessage;
  316. }
  317. if (responseContent is ObjectContent<T>)
  318. {
  319. convertedResponseMessage = new HttpResponseMessage<T>(HttpStatusCode.OK);
  320. convertedResponseMessage.Content = (ObjectContent<T>)responseContent;
  321. }
  322. else
  323. {
  324. convertedResponseMessage = (HttpResponseMessage<T>)responseContent;
  325. }
  326. ObjectContent<T> objectContent = convertedResponseMessage.Content;
  327. if (objectContent != null)
  328. {
  329. MediaTypeFormatterCollection hostFormatters = new MediaTypeFormatterCollection(formatters);
  330. MediaTypeFormatterCollection operationFormatters = objectContent.Formatters;
  331. // have to use a List since objectContent.Formatter getter will new up a collection with default formatters
  332. List<MediaTypeFormatter> list = new List<MediaTypeFormatter>();
  333. bool addHostXmlFormatter = false;
  334. bool addHostJsonValueFormatter = false;
  335. bool addHostJsonFormatter = false;
  336. bool addHostFormUrlEncodedFormatter = false;
  337. // we should start with the formatters configured from objectContent.Formatters
  338. foreach (MediaTypeFormatter formatter in operationFormatters)
  339. {
  340. if (operationFormatters.XmlFormatter == formatter &&
  341. !operationFormatters.XmlFormatter.IsModified)
  342. {
  343. // we should take the one from the host
  344. addHostXmlFormatter = true;
  345. }
  346. else if (operationFormatters.JsonValueFormatter == formatter &&
  347. !operationFormatters.JsonValueFormatter.IsModified)
  348. {
  349. // we should take the one from the host
  350. addHostJsonValueFormatter = true;
  351. }
  352. else if (operationFormatters.JsonFormatter == formatter &&
  353. !operationFormatters.JsonFormatter.IsModified)
  354. {
  355. // we should take the one from the host
  356. addHostJsonFormatter = true;
  357. }
  358. else if (operationFormatters.FormUrlEncodedFormatter == formatter &&
  359. !operationFormatters.FormUrlEncodedFormatter.IsModified)
  360. {
  361. // we should take the one from the host
  362. addHostFormUrlEncodedFormatter = true;
  363. }
  364. else
  365. {
  366. list.Add(formatter);
  367. }
  368. }
  369. // now we add some additional custom formatter set through host level
  370. foreach (MediaTypeFormatter formatter in hostFormatters)
  371. {
  372. if (hostFormatters.XmlFormatter == formatter)
  373. {
  374. // we only add the host level Xml formatter when it has not been added
  375. if (addHostXmlFormatter)
  376. {
  377. list.Add(formatter);
  378. }
  379. }
  380. else if (hostFormatters.JsonValueFormatter == formatter)
  381. {
  382. // we only add the host level JsonValue formatter when it has not been added
  383. if (addHostJsonValueFormatter)
  384. {
  385. list.Add(formatter);
  386. }
  387. }
  388. else if (hostFormatters.JsonFormatter == formatter)
  389. {
  390. // we only add the host level Json formatter when it has not been added
  391. if (addHostJsonFormatter)
  392. {
  393. list.Add(formatter);
  394. }
  395. }
  396. else if (hostFormatters.FormUrlEncodedFormatter == formatter)
  397. {
  398. // we only add the host level form URL encoded formatter when it has not been added
  399. if (addHostFormUrlEncodedFormatter)
  400. {
  401. list.Add(formatter);
  402. }
  403. }
  404. else
  405. {
  406. // adding other user defined formatter
  407. list.Add(formatter);
  408. }
  409. }
  410. // then we should update the objectContent's formatters with the merged list
  411. objectContent.SetFormatters(list);
  412. }
  413. convertedResponseMessage.RequestMessage = requestMessage;
  414. return convertedResponseMessage;
  415. }
  416. }
  417. }
  418. }