|
9 | 9 |
|
10 | 10 | namespace Microsoft.AspNet.Mvc
|
11 | 11 | {
|
| 12 | + /// <summary> |
| 13 | + /// An action result which formats the given object as JSON. |
| 14 | + /// </summary> |
12 | 15 | public class JsonResult : ActionResult
|
13 | 16 | {
|
14 |
| - private static readonly IList<MediaTypeHeaderValue> _defaultSupportedContentTypes = |
15 |
| - new List<MediaTypeHeaderValue>() |
16 |
| - { |
17 |
| - MediaTypeHeaderValue.Parse("application/json"), |
18 |
| - MediaTypeHeaderValue.Parse("text/json"), |
19 |
| - }; |
20 |
| - private IOutputFormatter _defaultFormatter; |
21 |
| - |
22 |
| - private ObjectResult _objectResult; |
23 |
| - |
24 |
| - public JsonResult(object data) : |
25 |
| - this(data, null) |
| 17 | + /// <summary> |
| 18 | + /// The list of content-types used for formatting when <see cref="ContentTypes"/> is null or empty. |
| 19 | + /// </summary> |
| 20 | + public static readonly IReadOnlyList<MediaTypeHeaderValue> DefaultContentTypes = new MediaTypeHeaderValue[] |
26 | 21 | {
|
27 |
| - } |
| 22 | + MediaTypeHeaderValue.Parse("application/json"), |
| 23 | + MediaTypeHeaderValue.Parse("text/json"), |
| 24 | + }; |
28 | 25 |
|
29 | 26 | /// <summary>
|
30 |
| - /// Creates an instance of <see cref="JsonResult"/> class. |
| 27 | + /// Creates a new <see cref="JsonResult"/> with the given <paramref name="data"/>. |
31 | 28 | /// </summary>
|
32 |
| - /// <param name="data"></param> |
33 |
| - /// <param name="defaultFormatter">If no matching formatter is found, |
34 |
| - /// the response is written to using defaultFormatter.</param> |
35 |
| - /// <remarks> |
36 |
| - /// The default formatter must be able to handle either application/json |
37 |
| - /// or text/json. |
38 |
| - /// </remarks> |
39 |
| - public JsonResult(object data, IOutputFormatter defaultFormatter) |
| 29 | + /// <param name="value">The value to format as JSON.</param> |
| 30 | + public JsonResult(object value) |
| 31 | + : this(value, formatter: null) |
40 | 32 | {
|
41 |
| - _defaultFormatter = defaultFormatter; |
42 |
| - _objectResult = new ObjectResult(data); |
43 | 33 | }
|
44 | 34 |
|
45 |
| - public object Value |
| 35 | + /// <summary> |
| 36 | + /// Creates a new <see cref="JsonResult"/> with the given <paramref name="data"/>. |
| 37 | + /// </summary> |
| 38 | + /// <param name="value">The value to format as JSON.</param> |
| 39 | + /// <param name="formatter">The formatter to use, or <c>null</c> to choose a formatter dynamically.</param> |
| 40 | + public JsonResult(object value, IOutputFormatter formatter) |
46 | 41 | {
|
47 |
| - get |
48 |
| - { |
49 |
| - return _objectResult.Value; |
50 |
| - } |
51 |
| - set |
52 |
| - { |
53 |
| - _objectResult.Value = value; |
54 |
| - } |
55 |
| - } |
| 42 | + Value = value; |
| 43 | + Formatter = formatter; |
56 | 44 |
|
57 |
| - public IList<MediaTypeHeaderValue> ContentTypes |
58 |
| - { |
59 |
| - get |
60 |
| - { |
61 |
| - return _objectResult.ContentTypes; |
62 |
| - } |
63 |
| - set |
64 |
| - { |
65 |
| - _objectResult.ContentTypes = value; |
66 |
| - } |
| 45 | + ContentTypes = new List<MediaTypeHeaderValue>(); |
67 | 46 | }
|
68 | 47 |
|
| 48 | + /// <summary> |
| 49 | + /// Gets or sets the list of supported Content-Types. |
| 50 | + /// </summary> |
| 51 | + public IList<MediaTypeHeaderValue> ContentTypes { get; set; } |
| 52 | + |
| 53 | + /// <summary> |
| 54 | + /// Gets or sets the formatter. |
| 55 | + /// </summary> |
| 56 | + public IOutputFormatter Formatter { get; set; } |
| 57 | + |
| 58 | + /// <summary> |
| 59 | + /// Gets or sets the value to be formatted. |
| 60 | + /// </summary> |
| 61 | + public object Value { get; set; } |
| 62 | + |
| 63 | + /// <inheritdoc /> |
69 | 64 | public override async Task ExecuteResultAsync([NotNull] ActionContext context)
|
70 | 65 | {
|
| 66 | + var objectResult = new ObjectResult(Value); |
| 67 | + |
71 | 68 | // Set the content type explicitly to application/json and text/json.
|
72 | 69 | // if the user has not already set it.
|
73 | 70 | if (ContentTypes == null || ContentTypes.Count == 0)
|
74 | 71 | {
|
75 |
| - ContentTypes = _defaultSupportedContentTypes; |
| 72 | + foreach (var contentType in DefaultContentTypes) |
| 73 | + { |
| 74 | + objectResult.ContentTypes.Add(contentType); |
| 75 | + } |
| 76 | + } |
| 77 | + else |
| 78 | + { |
| 79 | + objectResult.ContentTypes = ContentTypes; |
76 | 80 | }
|
77 | 81 |
|
78 | 82 | var formatterContext = new OutputFormatterContext()
|
79 | 83 | {
|
80 |
| - DeclaredType = _objectResult.DeclaredType, |
81 | 84 | ActionContext = context,
|
| 85 | + DeclaredType = objectResult.DeclaredType, |
82 | 86 | Object = Value,
|
83 | 87 | };
|
84 | 88 |
|
85 |
| - // Need to call this instead of directly calling _objectResult.ExecuteResultAsync |
86 |
| - // as that sets the status to 406 if a formatter is not found. |
87 |
| - // this can be cleaned up after https://github.com/aspnet/Mvc/issues/941 gets resolved. |
88 |
| - var formatter = SelectFormatter(formatterContext); |
| 89 | + var formatter = SelectFormatter(objectResult, formatterContext); |
89 | 90 | await formatter.WriteAsync(formatterContext);
|
90 | 91 | }
|
91 | 92 |
|
92 |
| - private IOutputFormatter SelectFormatter(OutputFormatterContext formatterContext) |
| 93 | + private IOutputFormatter SelectFormatter(ObjectResult objectResult, OutputFormatterContext formatterContext) |
93 | 94 | {
|
94 |
| - var defaultFormatters = formatterContext.ActionContext |
95 |
| - .HttpContext |
96 |
| - .RequestServices |
97 |
| - .GetRequiredService<IOutputFormattersProvider>() |
98 |
| - .OutputFormatters; |
99 |
| - |
100 |
| - var formatter = _objectResult.SelectFormatter(formatterContext, defaultFormatters); |
101 |
| - if (formatter == null) |
| 95 | + if (Formatter == null) |
102 | 96 | {
|
103 |
| - formatter = _defaultFormatter ?? formatterContext.ActionContext |
104 |
| - .HttpContext |
105 |
| - .RequestServices |
106 |
| - .GetRequiredService<JsonOutputFormatter>(); |
107 |
| - } |
| 97 | + // If no formatter was provided, then run Conneg with the formatters configured in options. |
| 98 | + var formatters = formatterContext |
| 99 | + .ActionContext |
| 100 | + .HttpContext |
| 101 | + .RequestServices |
| 102 | + .GetRequiredService<IOutputFormattersProvider>() |
| 103 | + .OutputFormatters |
| 104 | + .OfType<IJsonOutputFormatter>() |
| 105 | + .ToArray(); |
| 106 | + |
| 107 | + var formatter = objectResult.SelectFormatter(formatterContext, formatters); |
| 108 | + if (formatter == null) |
| 109 | + { |
| 110 | + // If the available user-configured formatters can't write this type, then fall back to the |
| 111 | + // 'global' one. |
| 112 | + formatter = formatterContext |
| 113 | + .ActionContext |
| 114 | + .HttpContext |
| 115 | + .RequestServices |
| 116 | + .GetRequiredService<JsonOutputFormatter>(); |
108 | 117 |
|
109 |
| - return formatter; |
| 118 | + // Run SelectFormatter again to try to choose a content type that this formatter can do. |
| 119 | + objectResult.SelectFormatter(formatterContext, new[] { formatter }); |
| 120 | + } |
| 121 | + |
| 122 | + return formatter; |
| 123 | + } |
| 124 | + else |
| 125 | + { |
| 126 | + // Run SelectFormatter to try to choose a content type that this formatter can do. |
| 127 | + objectResult.SelectFormatter(formatterContext, new[] { Formatter }); |
| 128 | + return Formatter; |
| 129 | + } |
110 | 130 | }
|
111 | 131 | }
|
112 | 132 | }
|
0 commit comments