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

Commit 73fc840

Browse files
committed
Passing only the object instead of the entire object result to the output formatter + added some tests.
1 parent f8e057f commit 73fc840

File tree

7 files changed

+111
-23
lines changed

7 files changed

+111
-23
lines changed

src/Microsoft.AspNet.Mvc.Core/ActionResults/ObjectResult.cs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ public class ObjectResult : ActionResult
2525
public ObjectResult(object value)
2626
{
2727
Value = value;
28+
Formatters = new List<OutputFormatter>();
29+
ContentTypes = new List<MediaTypeHeaderValue>();
2830
}
2931

3032
public override async Task ExecuteResultAsync(ActionContext context)
@@ -34,7 +36,7 @@ public override async Task ExecuteResultAsync(ActionContext context)
3436
{
3537
DeclaredType = DeclaredType,
3638
ActionContext = context,
37-
ObjectResult = this,
39+
Object = Value,
3840
};
3941

4042
var selectedFormatter = SelectFormatter(formatterContext, formatters);
@@ -74,11 +76,10 @@ public virtual OutputFormatter SelectFormatter(OutputFormatterContext formatterC
7476
// No formatter found based on accept headers, fall back on request contentType.
7577
var incomingContentType =
7678
MediaTypeHeaderValue.Parse(formatterContext.ActionContext.HttpContext.Request.ContentType);
77-
if(incomingContentType == null)
78-
{
79-
return null;
80-
}
8179

80+
// In case the incomingContentType is null (as can be the case with get requests),
81+
// we need to pick the first formatter which
82+
// can support writing this type.
8283
var contentTypes = new [] { incomingContentType };
8384
selectedFormatter = SelectFormatterUsingAnyAcceptableContentType(
8485
formatterContext,

src/Microsoft.AspNet.Mvc.Core/Formatters/JsonOutputFormatter.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ public override Task WriteAsync(OutputFormatterContext context,
8484
using (var jsonWriter = CreateJsonWriter(writer))
8585
{
8686
var jsonSerializer = CreateJsonSerializer();
87-
jsonSerializer.Serialize(jsonWriter, context.ObjectResult.Value);
87+
jsonSerializer.Serialize(jsonWriter, context.Object);
8888

8989
// We're explicitly calling flush here to simplify the debugging experience because the
9090
// underlying TextWriter might be long-lived. If this method ends up being called repeatedly

src/Microsoft.AspNet.Mvc.Core/Formatters/OutputFormatter.cs

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -116,10 +116,21 @@ public virtual void SetResponseContentHeaders(OutputFormatterContext context)
116116
/// False otherwise.</returns>
117117
public virtual bool CanWriteResult(OutputFormatterContext context, MediaTypeHeaderValue contentType)
118118
{
119-
// Since supportedMedia Type is going to be more specific check if supportedMediaType is a subset
120-
// of the content type which is typically what we get on acceptHeader.
121-
var mediaType = SupportedMediaTypes
122-
.FirstOrDefault(supportedMediaType => supportedMediaType.IsSubsetOf(contentType));
119+
MediaTypeHeaderValue mediaType = null;
120+
if (contentType == null)
121+
{
122+
// If the desired content type is set to null, the current formatter is free to choose the
123+
// response media type.
124+
mediaType = SupportedMediaTypes.FirstOrDefault();
125+
}
126+
else
127+
{
128+
// Since supportedMedia Type is going to be more specific check if supportedMediaType is a subset
129+
// of the content type which is typically what we get on acceptHeader.
130+
mediaType = SupportedMediaTypes
131+
.FirstOrDefault(supportedMediaType => supportedMediaType.IsSubsetOf(contentType));
132+
}
133+
123134
if (mediaType != null)
124135
{
125136
context.SelectedContentType = mediaType;

src/Microsoft.AspNet.Mvc.Core/Formatters/OutputFormatterContext.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ namespace Microsoft.AspNet.Mvc
1010
{
1111
public class OutputFormatterContext
1212
{
13-
public ObjectResult ObjectResult { get; set; }
13+
public object Object { get; set; }
1414

1515
public Type DeclaredType { get; set; }
1616

test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/ObjectContentResultTests.cs

Lines changed: 87 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ public async Task ObjectResult_WithMultipleContentTypesAndAcceptHeaders_Performs
8080
result.ContentTypes = contentTypes.Select(contentType => MediaTypeHeaderValue.Parse(contentType)).ToList();
8181
result.Formatters = new List<OutputFormatter>
8282
{
83-
new TestOutputFormatter(),
83+
new CannotWriteFormatter(),
8484
new JsonOutputFormatter(JsonOutputFormatter.CreateDefaultSettings(), true),
8585
};
8686

@@ -146,7 +146,7 @@ public async Task ObjectResult_MultipleContentTypes_PicksFirstFormatterWhichSupp
146146
.ToList();
147147
result.Formatters = new List<OutputFormatter>
148148
{
149-
new TestOutputFormatter(),
149+
new CannotWriteFormatter(),
150150
new JsonOutputFormatter(JsonOutputFormatter.CreateDefaultSettings(), true),
151151
};
152152
// Act
@@ -180,7 +180,7 @@ public async Task ObjectResult_MultipleFormattersSupportingTheSameContentType_Se
180180
{
181181
mockFormatter.Object,
182182
new JsonOutputFormatter(JsonOutputFormatter.CreateDefaultSettings(), true),
183-
new TestOutputFormatter()
183+
new CannotWriteFormatter()
184184
};
185185
// Act
186186
await result.ExecuteResultAsync(actionContext);
@@ -208,7 +208,7 @@ public async Task ObjectResult_NoContentTypeSetWithAcceptHeaders_PicksFormatterO
208208
// Set more than one formatters. The test output formatter throws on write.
209209
result.Formatters = new List<OutputFormatter>
210210
{
211-
new TestOutputFormatter(),
211+
new CannotWriteFormatter(),
212212
new JsonOutputFormatter(JsonOutputFormatter.CreateDefaultSettings(), true),
213213
};
214214

@@ -239,7 +239,7 @@ public async Task ObjectResult_NoContentTypeSetWithNoAcceptHeaders_PicksFormatte
239239
// Set more than one formatters. The test output formatter throws on write.
240240
result.Formatters = new List<OutputFormatter>
241241
{
242-
new TestOutputFormatter(),
242+
new CannotWriteFormatter(),
243243
new JsonOutputFormatter(JsonOutputFormatter.CreateDefaultSettings(), true),
244244
};
245245
// Act
@@ -250,6 +250,65 @@ public async Task ObjectResult_NoContentTypeSetWithNoAcceptHeaders_PicksFormatte
250250
httpResponse.VerifySet(r => r.ContentType = expectedContentType);
251251
}
252252

253+
[Fact]
254+
public async Task
255+
ObjectResult_NoContentTypeSetWithNoAcceptHeadersAndNoRequestContentType_PicksFirstFormatterWhichCanWrite()
256+
{
257+
// Arrange
258+
var stream = new MemoryStream();
259+
var expectedContentType = "application/json;charset=utf-8";
260+
var httpResponse = new Mock<HttpResponse>();
261+
httpResponse.SetupProperty<string>(o => o.ContentType);
262+
httpResponse.SetupGet(r => r.Body).Returns(stream);
263+
264+
var actionContext = CreateMockActionContext(httpResponse.Object,
265+
requestAcceptHeader: null,
266+
requestContentType: null);
267+
var input = "testInput";
268+
var result = new ObjectResult(input);
269+
270+
// Set more than one formatters. The test output formatter throws on write.
271+
result.Formatters = new List<OutputFormatter>
272+
{
273+
new CannotWriteFormatter(),
274+
new JsonOutputFormatter(JsonOutputFormatter.CreateDefaultSettings(), true),
275+
};
276+
// Act
277+
await result.ExecuteResultAsync(actionContext);
278+
279+
// Assert
280+
// Asserts that content type is not text/custom.
281+
httpResponse.VerifySet(r => r.ContentType = expectedContentType);
282+
}
283+
284+
[Fact]
285+
public async Task ObjectResult_NoFormatterFound_Returns406()
286+
{
287+
// Arrange
288+
var stream = new MemoryStream();
289+
var httpResponse = new Mock<HttpResponse>();
290+
httpResponse.SetupProperty<string>(o => o.ContentType);
291+
httpResponse.SetupGet(r => r.Body).Returns(stream);
292+
293+
var actionContext = CreateMockActionContext(httpResponse.Object,
294+
requestAcceptHeader: null,
295+
requestContentType: null);
296+
var input = "testInput";
297+
var result = new ObjectResult(input);
298+
299+
// Set more than one formatters. The test output formatter throws on write.
300+
result.Formatters = new List<OutputFormatter>
301+
{
302+
new CannotWriteFormatter(),
303+
};
304+
// Act
305+
await result.ExecuteResultAsync(actionContext);
306+
307+
// Assert
308+
// Asserts that content type is not text/custom.
309+
httpResponse.VerifySet(r => r.StatusCode = 406);
310+
}
311+
253312
// TODO: Disabling since this scenario is no longer dealt with in object result.
254313
// Re-enable once we do.
255314
//[Fact]
@@ -300,7 +359,7 @@ public async Task ObjectResult_Execute_CallsJsonResult_SetsContent()
300359
var formatterContext = new OutputFormatterContext()
301360
{
302361
ActionContext = tempActionContext,
303-
ObjectResult = new ObjectResult(nonStringValue),
362+
Object = nonStringValue,
304363
DeclaredType = nonStringValue.GetType()
305364
};
306365
var formatter = new JsonOutputFormatter(JsonOutputFormatter.CreateDefaultSettings(), true);
@@ -352,9 +411,9 @@ private static Mock<HttpResponse> GetMockHttpResponse()
352411
return httpResponse;
353412
}
354413

355-
private static Mock<TestOutputFormatter> GetMockFormatter()
414+
private static Mock<CannotWriteFormatter> GetMockFormatter()
356415
{
357-
var mockFormatter = new Mock<TestOutputFormatter>();
416+
var mockFormatter = new Mock<CannotWriteFormatter>();
358417
mockFormatter.Setup(o => o.CanWriteResult(It.IsAny<OutputFormatterContext>(),
359418
It.IsAny<MediaTypeHeaderValue>()))
360419
.Returns(true);
@@ -378,13 +437,31 @@ private static IServiceProvider GetServiceProvider()
378437
return serviceCollection.BuildServiceProvider();
379438
}
380439

381-
public class TestOutputFormatter : OutputFormatter
440+
private class UnsupportedOutputFormatter : OutputFormatter
441+
{
442+
public override bool CanWriteResult(OutputFormatterContext context, MediaTypeHeaderValue contentType)
443+
{
444+
return false;
445+
}
446+
447+
public override Task WriteAsync(OutputFormatterContext context, CancellationToken cancellationToken)
448+
{
449+
throw new NotImplementedException();
450+
}
451+
}
452+
453+
public class CannotWriteFormatter : OutputFormatter
382454
{
383-
public TestOutputFormatter()
455+
public CannotWriteFormatter()
384456
{
385457
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("text/custom"));
386458
}
387459

460+
public override bool CanWriteResult(OutputFormatterContext context, MediaTypeHeaderValue contentType)
461+
{
462+
return false;
463+
}
464+
388465
public override Task WriteAsync(OutputFormatterContext context, CancellationToken cancellationToken)
389466
{
390467
throw new NotImplementedException();

test/Microsoft.AspNet.Mvc.Core.Test/Formatters/OutputFormatterTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ public void SelectResponseCharacterEncoding_SelectsEncoding(string acceptCharset
6262

6363
var formatterContext = new OutputFormatterContext()
6464
{
65-
ObjectResult = new ObjectResult("someValue"),
65+
Object = "someValue",
6666
ActionContext = actionContext,
6767
DeclaredType = typeof(string)
6868
};

test/Microsoft.AspNet.Mvc.HeaderValueAbstractions.Test/MediaTypeHeaderValueParsingTests.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,6 @@ public void MediaTypeWithQualityHeaderValue_ParseSuccessfully(string mediaType,
8787
Assert.Equal(mediaSubType, parsedValue.MediaSubType);
8888
Assert.Equal(charset, parsedValue.Charset);
8989
ValidateParametes(parameters, parsedValue.Parameters);
90-
ValidateParametes(acceptParameters, parsedValue.AcceptParameters);
9190
}
9291

9392
private static void ValidateParametes(IDictionary<string, string> expectedParameters,

0 commit comments

Comments
 (0)